From 5ecf626705b9bfd281136c8b6ed01ad6728537ce Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 19 Apr 2019 13:44:36 +0300 Subject: [PATCH] Another T1210 refactoring --- monkey/common/utils/attack_utils.py | 8 ++++ monkey/infection_monkey/exploit/__init__.py | 31 +++++++-------- .../infection_monkey/exploit/elasticgroovy.py | 1 - monkey/infection_monkey/exploit/hadoop.py | 1 - monkey/infection_monkey/exploit/mssqlexec.py | 1 + monkey/infection_monkey/exploit/rdpgrinder.py | 2 +- monkey/infection_monkey/exploit/sambacry.py | 2 +- monkey/infection_monkey/exploit/shellshock.py | 1 - monkey/infection_monkey/exploit/smbexec.py | 8 ++-- monkey/infection_monkey/exploit/sshexec.py | 4 +- monkey/infection_monkey/exploit/struts2.py | 1 - monkey/infection_monkey/exploit/web_rce.py | 4 +- monkey/infection_monkey/exploit/weblogic.py | 1 - monkey/infection_monkey/exploit/wmiexec.py | 2 +- monkey/infection_monkey/monkey.py | 4 +- monkey/infection_monkey/network/__init__.py | 31 +++++++-------- .../infection_monkey/network/elasticfinger.py | 4 +- monkey/infection_monkey/network/httpfinger.py | 4 +- .../network/mssql_fingerprint.py | 4 +- .../infection_monkey/network/mysqlfinger.py | 4 +- .../network/network_scanner.py | 3 -- .../infection_monkey/network/ping_scanner.py | 3 ++ monkey/infection_monkey/network/smbfinger.py | 4 +- monkey/infection_monkey/network/sshfinger.py | 5 ++- .../infection_monkey/network/tcp_scanner.py | 4 +- .../transport/attack_telems/base_telem.py | 3 +- monkey/infection_monkey/utils.py | 9 ----- .../attack/technique_reports/T1210.py | 19 +++++----- .../cc/ui/src/components/attack/T1210.js | 38 +++++++++++++------ 29 files changed, 104 insertions(+), 102 deletions(-) diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index b7f3346b3..28feaa537 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -11,3 +11,11 @@ class ScanStatus(Enum): # Dict that describes what BITS job was used for BITS_UPLOAD_STRING = {"usage": "BITS job was used to upload monkey to a remote system."} + + +def format_time(time): + return "%s-%s %s:%s:%s" % (time.date().month, + time.date().day, + time.time().hour, + time.time().minute, + time.time().second) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 41f82e50e..d5e9fcf3a 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -1,7 +1,6 @@ -from abc import ABCMeta, abstractmethod +from abc import ABCMeta, abstractmethod, abstractproperty import infection_monkey.config from common.utils.exploit_enum import ExploitType -from infection_monkey.utils import get_current_time_string __author__ = 'itamar' @@ -13,11 +12,16 @@ class HostExploiter(object): # Usual values are 'vulnerability' or 'brute_force' EXPLOIT_TYPE = ExploitType.VULNERABILITY - _EXPLOITED_SERVICE = '' + + @abstractproperty + def _EXPLOITED_SERVICE(self): + pass def __init__(self, host): self._config = infection_monkey.config.WormConfiguration - self._exploit_info = {} + self._exploit_info = {'display_name': self._EXPLOITED_SERVICE, + 'vulnerable_urls': [], + 'vulnerable_ports': []} self._exploit_attempts = [] self.host = host @@ -39,20 +43,11 @@ class HostExploiter(object): def exploit_host(self): raise NotImplementedError() - def add_vuln_service_info(self, port=None, url=None): - if port: - service_endpoint = port - elif url: - service_endpoint = url - else: - raise NotImplementedError("You must pass either port or url to add a vulnerable service info.") - if not self._EXPLOITED_SERVICE: - raise NotImplementedError("You must override _EXPLOITED_SERVICE to name a service this exploiter " - "is targeting") - self._exploit_info['exploited_service'] = {'name': self._EXPLOITED_SERVICE, - 'endpoint': service_endpoint, - 'time': get_current_time_string()} - return + def add_vuln_url(self, url): + self._exploit_info['vulnerable_urls'].append(url) + + def add_vuln_port(self, port): + self._exploit_info['vulnerable_ports'].append(port) from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index b40d01f0a..3a129ebc0 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -59,7 +59,6 @@ class ElasticGroovyExploiter(WebRCE): result = self.get_results(response) if not result: return False - self.add_vuln_service_info(url=url) return result[0] def upload_monkey(self, url, commands=None): diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index ef2fa506e..f02c4f3d3 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -49,7 +49,6 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() - self.add_vuln_service_info(url=self.vulnerable_urls[0]) return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 2e8bf6c90..d2d41a336 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -13,6 +13,7 @@ LOG = logging.getLogger(__name__) class MSSQLExploiter(HostExploiter): + _EXPLOITED_SERVICE = 'MSSQL' _TARGET_OS_TYPE = ['windows'] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE LOGIN_TIMEOUT = 15 diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index c7b3e1c71..2c94dcafa 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -317,7 +317,7 @@ class RdpExploiter(HostExploiter): if client_factory.success: if not self._config.rdp_use_vbs_download: VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) - self.add_vuln_service_info(port=RDP_PORT) + self.add_vuln_port(RDP_PORT) exploited = True self.report_login_attempt(True, user, password) break diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index 143c15c83..7c4d7790a 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -89,7 +89,7 @@ class SambaCryExploiter(HostExploiter): LOG.info( "Shares triggered successfully on host %s: %s" % ( self.host.ip_addr, str(successfully_triggered_shares))) - self.add_vuln_service_info(port='139 or 445') + self.add_vuln_port(str(writable_shares_creds_dict)) return True else: LOG.info("No shares triggered successfully on host %s" % self.host.ip_addr) diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 698a3fa25..2f6e3516f 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -144,7 +144,6 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue - self.add_vuln_service_info(url=url) return True return False diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 7edd4b528..1b4071312 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -69,8 +69,8 @@ class SmbExploiter(HostExploiter): LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)", self.host, user, password, lm_hash, ntlm_hash) self.report_login_attempt(True, user, password, lm_hash, ntlm_hash) - self.add_vuln_service_info(port=("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], - SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))) + self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], + SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])) exploited = True break else: @@ -140,6 +140,6 @@ class SmbExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", remote_full_path, self.host, cmdline) - self.add_vuln_service_info(port=("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], - SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))) + self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], + SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])) return True diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 1fd954fec..09982876d 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -82,11 +82,11 @@ class SSHExploiter(HostExploiter): LOG.debug("Successfully logged in %r using SSH (%s : %s)", self.host, user, curpass) exploited = True - self.add_vuln_service_info(port=port) + self.add_vuln_port(port) self.report_login_attempt(True, user, curpass) break - except paramiko.AuthenticationException as exc: + except Exception as exc: LOG.debug("Error logging into victim %r with user" " %s and password '%s': (%s)", self.host, user, curpass, exc) diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py index f1bce7d3b..042a38580 100644 --- a/monkey/infection_monkey/exploit/struts2.py +++ b/monkey/infection_monkey/exploit/struts2.py @@ -92,5 +92,4 @@ class Struts2Exploiter(WebRCE): except httplib.IncompleteRead as e: page = e.partial - self.add_vuln_service_info(url=url) return page diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 912d7c108..f360cfd16 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -209,13 +209,11 @@ class WebRCE(HostExploiter): """ for url in urls: if self.check_if_exploitable(url): - self.vulnerable_urls.append(url) + self.add_vuln_url(url) if stop_checking: break if not self.vulnerable_urls: LOG.info("No vulnerable urls found, skipping.") - # We add urls to param used in reporting - self._exploit_info['vulnerable_urls'] = self.vulnerable_urls def get_host_arch(self, url): """ diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 584883216..bbf0f2b60 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -69,7 +69,6 @@ class WebLogicExploiter(WebRCE): print('[!] Connection Error') print(e) - self.add_vuln_service_info(url=url) return True def add_vulnerable_urls(self, urls, stop_checking=False): diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 2db2321e0..29bc08981 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -105,7 +105,7 @@ class WmiExploiter(HostExploiter): LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", remote_full_path, self.host, result.ProcessId, result.ReturnValue, cmdline) - self.add_vuln_service_info(port='unknown') + self.add_vuln_port(port='unknown') success = True else: LOG.debug("Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 2ec3e5d1f..6be73d41b 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -17,8 +17,6 @@ from infection_monkey.system_info import SystemInfoCollector from infection_monkey.system_singleton import SystemSingleton from infection_monkey.windows_upgrader import WindowsUpgrader from infection_monkey.post_breach.post_breach_handler import PostBreach -from common.utils.attack_utils import ScanStatus -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem __author__ = 'itamar' @@ -155,7 +153,7 @@ class InfectionMonkey(object): finger.get_host_fingerprint(machine) ControlClient.send_telemetry('scan', {'machine': machine.as_dict(), - }) + 'service_count': len(machine.services)}) # skip machines that we've already exploited if machine in self._exploited_machines: diff --git a/monkey/infection_monkey/network/__init__.py b/monkey/infection_monkey/network/__init__.py index b298be0c7..b47d4ebca 100644 --- a/monkey/infection_monkey/network/__init__.py +++ b/monkey/infection_monkey/network/__init__.py @@ -1,5 +1,4 @@ -from abc import ABCMeta, abstractmethod -from infection_monkey.utils import get_current_time_string +from abc import ABCMeta, abstractmethod, abstractproperty __author__ = 'itamar' @@ -15,21 +14,23 @@ class HostScanner(object): class HostFinger(object): __metaclass__ = ABCMeta - _SCANNED_SERVICE = '' + @abstractproperty + def _SCANNED_SERVICE(self): + pass - def format_service_info(self, port=None, url=None): - if port: - service_endpoint = port - elif url: - service_endpoint = url + def init_service(self, services, service_key): + services[service_key] = {} + services[service_key]['display_name'] = self._SCANNED_SERVICE + + def add_found_port(self, services, port, key=None): + if key: + services[key]['port'] = port else: - raise NotImplementedError("You must pass either port or url to get formatted service info.") - if not self._SCANNED_SERVICE: - raise NotImplementedError("You must override _SCANNED_SERVICE property" - " to name what service is being scanned.") - return {'display_name': self._SCANNED_SERVICE, - 'endpoint': service_endpoint, - 'time': get_current_time_string()} + for service in services: + if services[service]['display_name'] == self._SCANNED_SERVICE: + service[service]['port'] = port + return + raise KeyError @abstractmethod def get_host_fingerprint(self, host): diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index c8b720722..710479cb8 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -36,11 +36,11 @@ class ElasticFinger(HostFinger): url = 'http://%s:%s/' % (host.ip_addr, ES_PORT) with closing(requests.get(url, timeout=ES_HTTP_TIMEOUT)) as req: data = json.loads(req.text) - host.services[ES_SERVICE] = {} + self.init_service(host.services, ES_SERVICE) host.services[ES_SERVICE]['cluster_name'] = data['cluster_name'] host.services[ES_SERVICE]['name'] = data['name'] host.services[ES_SERVICE]['version'] = data['version']['number'] - host.services[ES_SERVICE].update(self.format_service_info(url=url)) + self.add_found_port(host.services, ES_PORT) return True except Timeout: LOG.debug("Got timeout while trying to read header information") diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 55bdcb1f1..1bcc70fb5 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -37,11 +37,11 @@ class HTTPFinger(HostFinger): with closing(head(url, verify=False, timeout=1)) as req: server = req.headers.get('Server') ssl = True if 'https://' in url else False - host.services['tcp-' + port[1]] = {} + self.init_service(host.services, ('tcp-' + port[1])) host.services['tcp-' + port[1]]['name'] = 'http' + self.add_found_port(host.services, port[0], ('tcp-' + port[1])) host.services['tcp-' + port[1]]['data'] = (server,ssl) LOG.info("Port %d is open on host %s " % (port[0], host)) - host.services['tcp-' + port[1]].update(self.format_service_info(port=port[0])) break # https will be the same on the same port except Timeout: pass diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index dba614c85..f99f92f5c 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -63,7 +63,7 @@ class MSSQLFinger(HostFinger): sock.close() return False - host.services[self._SCANNED_SERVICE] = {} + self.init_service(host.services, self._SCANNED_SERVICE) # Loop through the server data instances_list = data[3:].decode().split(';;') @@ -76,7 +76,7 @@ class MSSQLFinger(HostFinger): # Each instance's info is nested under its own name, if there are multiple instances # each will appear under its own name host.services[self._SCANNED_SERVICE][instance_info[1]][instance_info[i - 1]] = instance_info[i] - host.services[self._SCANNED_SERVICE].update(self.format_service_info(port=MSSQLFinger.SQL_BROWSER_DEFAULT_PORT)) + self.add_found_port(host.services, MSSQLFinger.SQL_BROWSER_DEFAULT_PORT) # Close the socket sock.close() diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index b1b1a6164..94077f684 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -51,14 +51,14 @@ class MySQLFinger(HostFinger): version, curpos = struct_unpack_tracker_string(data, curpos) # special coded to solve string parsing version = version[0] - host.services[SQL_SERVICE] = {} + self.init_service(host.services, SQL_SERVICE) host.services[SQL_SERVICE]['version'] = version version = version.split('-')[0].split('.') host.services[SQL_SERVICE]['major_version'] = version[0] host.services[SQL_SERVICE]['minor_version'] = version[1] host.services[SQL_SERVICE]['build_version'] = version[2] thread_id, curpos = struct_unpack_tracker(data, curpos, "{(val.vulnerable_urls.length !== 0 ? val.vulnerable_urls[0] : val.vulnerable_ports[0])} + ) +}; + let formatScanned = function (data){ let result = []; for(let service in data.machine.services){ let scanned_service = {'machine': data.machine, - 'service': {'endpoint': data.machine.services[service].endpoint, - 'name': data.machine.services[service].display_name, - 'time': data.machine.services[service].time}}; + 'time': data.time, + 'service': {'port': [data.machine.services[service].port], + 'display_name': data.machine.services[service].display_name}}; result.push(scanned_service) } return result }; -const columns = [ +const scanColumns = [ { columns: [ {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: { 'whiteSpace': 'unset' }, width: 200}, - {Header: 'Time', id: 'time', accessor: x => x.service.time, style: { 'whiteSpace': 'unset' }, width: 170}, - {Header: 'Port/url', id: 'port', accessor: x =>x.service.endpoint, style: { 'whiteSpace': 'unset' }}, - {Header: 'Service', id: 'service', accessor: x => x.service.name, style: { 'whiteSpace': 'unset' }} + {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, + {Header: 'Port', id: 'port', accessor: x =>x.service.port, style: { 'whiteSpace': 'unset' }}, + {Header: 'Service', id: 'service', accessor: x => x.service.display_name, style: { 'whiteSpace': 'unset' }} + ] + } +]; + +const exploitColumns = [ + { + columns: [ + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), + style: { 'whiteSpace': 'unset' }, width: 200}, + {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, + {Header: 'Port/url', id: 'port', accessor: x =>renderEndpoint(x.service), style: { 'whiteSpace': 'unset' }}, + {Header: 'Service', id: 'service', accessor: x => x.service.display_name, style: { 'whiteSpace': 'unset' }} ] } ]; @@ -46,7 +64,7 @@ class T1210 extends React.Component {
Found services:
Exploited services:
{this.props.data.message}