Another T1210 refactoring

This commit is contained in:
VakarisZ 2019-04-19 13:44:36 +03:00
parent 54c5d655dc
commit 5ecf626705
29 changed files with 104 additions and 102 deletions

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -92,5 +92,4 @@ class Struts2Exploiter(WebRCE):
except httplib.IncompleteRead as e:
page = e.partial
self.add_vuln_service_info(url=url)
return page

View File

@ -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):
"""

View File

@ -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):

View File

@ -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)",

View File

@ -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:

View File

@ -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):

View File

@ -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")

View File

@ -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

View File

@ -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()

View File

@ -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, "<I") # ignore thread id
host.services[SQL_SERVICE].update(self.format_service_info(port=MYSQL_PORT))
self.add_found_port(host.services, MYSQL_PORT)
# protocol parsing taken from
# https://nmap.org/nsedoc/scripts/mysql-info.html
if protocol == 10:

View File

@ -1,11 +1,9 @@
import logging
import time
from common.network.network_range import *
from infection_monkey.config import WormConfiguration
from infection_monkey.network.info import local_ips, get_interfaces_ranges
from infection_monkey.model import VictimHost
from infection_monkey.network import HostScanner
from infection_monkey.network import TcpScanner, PingScanner
__author__ = 'itamar'
@ -66,7 +64,6 @@ class NetworkScanner(object):
def get_victim_machines(self, max_find=5, stop_callback=None):
"""
Finds machines according to the ranges specified in the object
:param scan_type: A hostscanner class, will be instanced and used to scan for new machines
:param max_find: Max number of victims to find regardless of ranges
:param stop_callback: A callback to check at any point if we should stop scanning
:return: yields a sequence of VictimHost instances

View File

@ -20,6 +20,9 @@ LOG = logging.getLogger(__name__)
class PingScanner(HostScanner, HostFinger):
_SCANNED_SERVICE = ''
def __init__(self):
self._config = infection_monkey.config.WormConfiguration
self._devnull = open(os.devnull, "w")

View File

@ -114,7 +114,7 @@ class SMBFinger(HostFinger):
s.settimeout(0.7)
s.connect((host.ip_addr, SMB_PORT))
host.services[SMB_SERVICE] = {}
self.init_service(host.services, SMB_SERVICE)
h = SMBHeader(cmd="\x72", flag1="\x18", flag2="\x53\xc8")
n = SMBNego(data=SMBNegoFingerData())
@ -152,7 +152,7 @@ class SMBFinger(HostFinger):
host.os['version'] = os_version
else:
host.services[SMB_SERVICE]['os-version'] = os_version
host.services[SMB_SERVICE].update(self.format_service_info(port=SMB_PORT))
self.add_found_port(host.services, SMB_PORT)
return True
except Exception as exc:
LOG.debug("Error getting smb fingerprint: %s", exc)

View File

@ -40,18 +40,19 @@ class SSHFinger(HostFinger):
banner = data.get('banner', '')
if self._banner_regex.search(banner):
self._banner_match(name, host, banner)
host.services[SSH_SERVICE_DEFAULT]['display_name'] = self._SCANNED_SERVICE
return
is_open, banner = check_tcp_port(host.ip_addr, SSH_PORT, TIMEOUT, True)
if is_open:
host.services[SSH_SERVICE_DEFAULT] = {}
self.init_service(host.services, SSH_SERVICE_DEFAULT)
if banner:
host.services[SSH_SERVICE_DEFAULT]['banner'] = banner
if self._banner_regex.search(banner):
self._banner_match(SSH_SERVICE_DEFAULT, host, banner)
host.services[SSH_SERVICE_DEFAULT].update(self.format_service_info(port=SSH_PORT))
self.add_found_port(host.services, SSH_PORT)
return True
return False

View File

@ -38,8 +38,8 @@ class TcpScanner(HostScanner, HostFinger):
self._config.tcp_scan_get_banner)
for target_port, banner in izip_longest(ports, banners, fillvalue=None):
service = tcp_port_to_service(target_port)
host.services[service] = {}
host.services[service].update(self.format_service_info(port=target_port))
self.init_service(host.services, service)
self.add_found_port(host.services, target_port, key=service)
if banner:
host.services[service]['banner'] = banner
if only_one_port:

View File

@ -3,7 +3,6 @@ import requests
import json
from infection_monkey.control import ControlClient
import logging
from infection_monkey.utils import get_current_time_string
__author__ = "VakarisZ"
@ -21,7 +20,7 @@ class AttackTelem(object):
"""
self.technique = technique
self.result = status
self.data = {'status': status, 'id': GUID, 'time': get_current_time_string()}
self.data = {'status': status, 'id': GUID}
if data:
self.data.update(data)

View File

@ -58,12 +58,3 @@ def get_monkey_dir_path():
return WormConfiguration.monkey_dir_windows
else:
return WormConfiguration.monkey_dir_linux
def get_current_time_string():
time = datetime.datetime.now()
return "%s-%s-%s %s:%s:%s" % (time.date().year,
time.date().month,
time.date().day,
time.time().hour,
time.time().minute,
time.time().second)

View File

@ -27,20 +27,19 @@ def get_report_data():
def get_scanned_services():
results = mongo.db.telemetry.aggregate([{'$match': {'telem_type': 'scan'}},
{'$group': {
'_id': {'ip_addr': '$data.machine.ip_addr',
'services': '$data.machine.services'
},
'machine': {'$first': '$data.machine'}}}])
{'$sort': {'data.service_count': -1}},
{'$group': {
'_id': {'ip_addr': '$data.machine.ip_addr'},
'machine': {'$first': '$data.machine'},
'time': {'$first': '$timestamp'}}}])
return list(results)
def get_exploited_services():
results = mongo.db.telemetry.aggregate([{'$match': {'telem_type': 'exploit', 'data.result': True}},
{'$group': {
'_id': {'ip_addr': '$data.machine.ip_addr',
'info': '$data.info'
},
'service': {'$first': '$data.info.exploited_service'},
'machine': {'$first': '$data.machine'}}}])
'_id': {'ip_addr': '$data.machine.ip_addr'},
'service': {'$first': '$data.info'},
'machine': {'$first': '$data.machine'},
'time': {'$first': '$timestamp'}}}])
return list(results)

View File

@ -10,26 +10,44 @@ let renderMachine = function (val) {
)
};
let renderEndpoint = function (val) {
return (
<span>{(val.vulnerable_urls.length !== 0 ? val.vulnerable_urls[0] : val.vulnerable_ports[0])}</span>
)
};
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 {
<br/>
<div>Found services: </div>
<ReactTable
columns={columns}
columns={scanColumns}
data={data}
showPagination={false}
defaultPageSize={data.length}
@ -60,7 +78,7 @@ class T1210 extends React.Component {
<br/>
<div>Exploited services: </div>
<ReactTable
columns={columns}
columns={exploitColumns}
data={data}
showPagination={false}
defaultPageSize={data.length}
@ -70,8 +88,6 @@ class T1210 extends React.Component {
render() {
let scanned_services = this.props.data.scanned_services.map(formatScanned).flat();
console.log(scanned_services);
console.log(this.props.data);
return (
<div>
<div>{this.props.data.message}</div>