Merge branch 'attack_system_discovery' into attack_sys_network_config

# Conflicts:
#	monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js
#	monkey/monkey_island/cc/ui/src/components/attack/techniques/T1082.js
This commit is contained in:
VakarisZ 2019-08-20 16:10:06 +03:00
commit 320bcc4c1d
59 changed files with 340 additions and 191 deletions

View File

@ -3,12 +3,6 @@ language: python
cache: pip cache: pip
python: python:
- 2.7 - 2.7
- 3.6
matrix:
include:
- python: 3.7
dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069)
sudo: required # required for Python 3.7 (travis-ci/travis-ci#9069)
install: install:
#- pip install -r requirements.txt #- pip install -r requirements.txt
- pip install flake8 # pytest # add another testing frameworks later - pip install flake8 # pytest # add another testing frameworks later

View File

@ -10,6 +10,20 @@ class ScanStatus(Enum):
USED = 2 USED = 2
class UsageEnum(Enum):
SMB = {ScanStatus.USED.value: "SMB exploiter ran the monkey by creating a service via MS-SCMR.",
ScanStatus.SCANNED.value: "SMB exploiter failed to run the monkey by creating a service via MS-SCMR."}
MIMIKATZ = {ScanStatus.USED.value: "Windows module loader was used to load Mimikatz DLL.",
ScanStatus.SCANNED.value: "Monkey tried to load Mimikatz DLL, but failed."}
MIMIKATZ_WINAPI = {ScanStatus.USED.value: "WinAPI was called to load mimikatz.",
ScanStatus.SCANNED.value: "Monkey tried to call WinAPI to load mimikatz."}
DROPPER = {ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot."}
SINGLETON_WINAPI = {ScanStatus.USED.value: "WinAPI was called to acquire system singleton for monkey's process.",
ScanStatus.SCANNED.value: "WinAPI call to acquire system singleton"
" for monkey process wasn't successful."}
DROPPER_WINAPI = {ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot."}
# Dict that describes what BITS job was used for # Dict that describes what BITS job was used for
BITS_UPLOAD_STRING = "BITS job was used to upload monkey to a remote system." BITS_UPLOAD_STRING = "BITS job was used to upload monkey to a remote system."

View File

@ -1,3 +1,4 @@
import hashlib
import os import os
import json import json
import sys import sys
@ -13,9 +14,11 @@ GUID = str(uuid.getnode())
EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin') EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin')
SENSITIVE_FIELDS = ["exploit_password_list", "exploit_user_list"]
HIDDEN_FIELD_REPLACEMENT_CONTENT = "hidden"
class Configuration(object): class Configuration(object):
def from_kv(self, formatted_data): def from_kv(self, formatted_data):
# now we won't work at <2.7 for sure # now we won't work at <2.7 for sure
network_import = importlib.import_module('infection_monkey.network') network_import = importlib.import_module('infection_monkey.network')
@ -53,6 +56,12 @@ class Configuration(object):
result = self.from_kv(formatted_data) result = self.from_kv(formatted_data)
return result return result
@staticmethod
def hide_sensitive_info(config_dict):
for field in SENSITIVE_FIELDS:
config_dict[field] = HIDDEN_FIELD_REPLACEMENT_CONTENT
return config_dict
def as_dict(self): def as_dict(self):
result = {} result = {}
for key in dir(Configuration): for key in dir(Configuration):
@ -272,5 +281,17 @@ class Configuration(object):
PBA_linux_filename = None PBA_linux_filename = None
PBA_windows_filename = None PBA_windows_filename = None
@staticmethod
def hash_sensitive_data(sensitive_data):
"""
Hash sensitive data (e.g. passwords). Used so the log won't contain sensitive data plain-text, as the log is
saved on client machines plain-text.
:param sensitive_data: the data to hash.
:return: the hashed data.
"""
password_hashed = hashlib.sha512(sensitive_data).hexdigest()
return password_hashed
WormConfiguration = Configuration() WormConfiguration = Configuration()

View File

@ -125,6 +125,7 @@ class ControlClient(object):
@staticmethod @staticmethod
def send_telemetry(telem_category, data): def send_telemetry(telem_category, data):
if not WormConfiguration.current_server: if not WormConfiguration.current_server:
LOG.error("Trying to send %s telemetry before current server is established, aborting." % telem_category)
return return
try: try:
telemetry = {'monkey_guid': GUID, 'telem_category': telem_category, 'data': data} telemetry = {'monkey_guid': GUID, 'telem_category': telem_category, 'data': data}
@ -168,7 +169,8 @@ class ControlClient(object):
try: try:
unknown_variables = WormConfiguration.from_kv(reply.json().get('config')) unknown_variables = WormConfiguration.from_kv(reply.json().get('config'))
LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),)) LOG.info("New configuration was loaded from server: %r" %
(WormConfiguration.hide_sensitive_info(WormConfiguration.as_dict()),))
except Exception as exc: except Exception as exc:
# we don't continue with default conf here because it might be dangerous # we don't continue with default conf here because it might be dangerous
LOG.error("Error parsing JSON reply from control server %s (%s): %s", LOG.error("Error parsing JSON reply from control server %s (%s): %s",

View File

@ -15,7 +15,7 @@ from infection_monkey.exploit.tools.helpers import build_monkey_commandline_expl
from infection_monkey.model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX from infection_monkey.model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
from infection_monkey.system_info import SystemInfoCollector, OperatingSystem from infection_monkey.system_info import SystemInfoCollector, OperatingSystem
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus, UsageEnum
if "win32" == sys.platform: if "win32" == sys.platform:
from win32process import DETACHED_PROCESS from win32process import DETACHED_PROCESS
@ -158,7 +158,6 @@ class MonkeyDrops(object):
else: else:
LOG.debug("Dropper source file '%s' is marked for deletion on next boot", LOG.debug("Dropper source file '%s' is marked for deletion on next boot",
self._config['source_path']) self._config['source_path'])
T1106Telem(ScanStatus.USED, "WinAPI was used to mark monkey files" T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send()
" for deletion on next boot.").send()
except AttributeError: except AttributeError:
LOG.error("Invalid configuration options. Failing") LOG.error("Invalid configuration options. Failing")

View File

@ -124,8 +124,9 @@ class MSSQLExploiter(HostExploiter):
# Core steps # Core steps
# Trying to connect # Trying to connect
conn = pymssql.connect(host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT) conn = pymssql.connect(host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT)
LOG.info('Successfully connected to host: {0}, ' LOG.info(
'using user: {1}, password: {2}'.format(host, user, password)) 'Successfully connected to host: {0}, using user: {1}, password (SHA-512): {2}'.format(
host, user, self._config.hash_sensitive_data(password)))
self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT)
self.report_login_attempt(True, user, password) self.report_login_attempt(True, user, password)
cursor = conn.cursor() cursor = conn.cursor()

View File

@ -9,6 +9,8 @@ from rdpy.core.error import RDPSecurityNegoFail
from rdpy.protocol.rdp import rdp from rdpy.protocol.rdp import rdp
from twisted.internet import reactor from twisted.internet import reactor
from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING
from common.utils.exploit_enum import ExploitType
from infection_monkey.exploit import HostExploiter from infection_monkey.exploit import HostExploiter
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey, build_monkey_commandline from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey, build_monkey_commandline
from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.tools.http_tools import HTTPTools
@ -16,8 +18,6 @@ from infection_monkey.model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
from infection_monkey.network.tools import check_tcp_port from infection_monkey.network.tools import check_tcp_port
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
from infection_monkey.utils import utf_to_ascii from infection_monkey.utils import utf_to_ascii
from common.utils.exploit_enum import ExploitType
from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING
__author__ = 'hoffer' __author__ = 'hoffer'
@ -298,8 +298,8 @@ class RdpExploiter(HostExploiter):
for user, password in user_password_pairs: for user, password in user_password_pairs:
try: try:
# run command using rdp. # run command using rdp.
LOG.info("Trying RDP logging into victim %r with user %s and password '%s'", LOG.info("Trying RDP logging into victim %r with user %s and password (SHA-512) '%s'",
self.host, user, password) self.host, user, self._config.hash_sensitive_data(password))
LOG.info("RDP connected to %r", self.host) LOG.info("RDP connected to %r", self.host)
@ -326,8 +326,8 @@ class RdpExploiter(HostExploiter):
except Exception as exc: except Exception as exc:
LOG.debug("Error logging into victim %r with user" LOG.debug("Error logging into victim %r with user"
" %s and password '%s': (%s)", self.host, " %s and password (SHA-512) '%s': (%s)", self.host,
user, password, exc) user, self._config.hash_sensitive_data(password), exc)
continue continue
http_thread.join(DOWNLOAD_TIMEOUT) http_thread.join(DOWNLOAD_TIMEOUT)

View File

@ -11,7 +11,7 @@ from infection_monkey.network import SMBFinger
from infection_monkey.network.tools import check_tcp_port from infection_monkey.network.tools import check_tcp_port
from common.utils.exploit_enum import ExploitType from common.utils.exploit_enum import ExploitType
from infection_monkey.telemetry.attack.t1035_telem import T1035Telem from infection_monkey.telemetry.attack.t1035_telem import T1035Telem
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus, UsageEnum
LOG = getLogger(__name__) LOG = getLogger(__name__)
@ -68,8 +68,8 @@ class SmbExploiter(HostExploiter):
self._config.smb_download_timeout) self._config.smb_download_timeout)
if remote_full_path is not None: if remote_full_path is not None:
LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)", LOG.debug("Successfully logged in %r using SMB (%s : (SHA-512) %s : %s : %s)",
self.host, user, password, lm_hash, ntlm_hash) self.host, user, self._config.hash_sensitive_data(password), lm_hash, ntlm_hash)
self.report_login_attempt(True, user, password, lm_hash, ntlm_hash) self.report_login_attempt(True, user, password, lm_hash, ntlm_hash)
self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1],
SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])) SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))
@ -81,8 +81,8 @@ class SmbExploiter(HostExploiter):
except Exception as exc: except Exception as exc:
LOG.debug("Exception when trying to copy file using SMB to %r with user:" LOG.debug("Exception when trying to copy file using SMB to %r with user:"
" %s, password: '%s', LM hash: %s, NTLM hash: %s: (%s)", self.host, " %s, password (SHA-512): '%s', LM hash: %s, NTLM hash: %s: (%s)", self.host,
user, password, lm_hash, ntlm_hash, exc) user, self._config.hash_sensitive_data(password), lm_hash, ntlm_hash, exc)
continue continue
if not exploited: if not exploited:
@ -133,11 +133,11 @@ class SmbExploiter(HostExploiter):
service = resp['lpServiceHandle'] service = resp['lpServiceHandle']
try: try:
scmr.hRStartServiceW(scmr_rpc, service) scmr.hRStartServiceW(scmr_rpc, service)
T1035Telem(ScanStatus.USED, "SMB exploiter ran the monkey by creating a service via MS-SCMR.").send() status = ScanStatus.USED
except: except:
T1035Telem(ScanStatus.SCANNED, status = ScanStatus.SCANNED
"SMB exploiter failed to run the monkey by creating a service via MS-SCMR.").send()
pass pass
T1035Telem(status, UsageEnum.SMB).send()
scmr.hRDeleteService(scmr_rpc, service) scmr.hRDeleteService(scmr_rpc, service)
scmr.hRCloseServiceHandle(scmr_rpc, service) scmr.hRCloseServiceHandle(scmr_rpc, service)

View File

@ -1,10 +1,11 @@
import StringIO
import logging import logging
import time import time
import paramiko import paramiko
import StringIO
import infection_monkey.monkeyfs as monkeyfs import infection_monkey.monkeyfs as monkeyfs
from common.utils.exploit_enum import ExploitType
from infection_monkey.exploit import HostExploiter from infection_monkey.exploit import HostExploiter
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
from infection_monkey.exploit.tools.helpers import get_interface_to_target from infection_monkey.exploit.tools.helpers import get_interface_to_target
@ -74,26 +75,26 @@ class SSHExploiter(HostExploiter):
exploited = False exploited = False
for user, curpass in user_password_pairs: for user, current_password in user_password_pairs:
try: try:
ssh.connect(self.host.ip_addr, ssh.connect(self.host.ip_addr,
username=user, username=user,
password=curpass, password=current_password,
port=port, port=port,
timeout=None) timeout=None)
LOG.debug("Successfully logged in %r using SSH (%s : %s)", LOG.debug("Successfully logged in %r using SSH. User: %s, pass (SHA-512): %s)",
self.host, user, curpass) self.host, user, self._config.hash_sensitive_data(current_password))
exploited = True exploited = True
self.add_vuln_port(port) self.add_vuln_port(port)
self.report_login_attempt(True, user, curpass) self.report_login_attempt(True, user, current_password)
break break
except Exception as exc: except Exception as exc:
LOG.debug("Error logging into victim %r with user" LOG.debug("Error logging into victim %r with user"
" %s and password '%s': (%s)", self.host, " %s and password (SHA-512) '%s': (%s)", self.host,
user, curpass, exc) user, self._config.hash_sensitive_data(current_password), exc)
self.report_login_attempt(False, user, curpass) self.report_login_attempt(False, user, current_password)
continue continue
return exploited return exploited
@ -112,7 +113,7 @@ class SSHExploiter(HostExploiter):
LOG.info("SSH port is closed on %r, skipping", self.host) LOG.info("SSH port is closed on %r, skipping", self.host)
return False return False
#Check for possible ssh exploits # Check for possible ssh exploits
exploited = self.exploit_with_ssh_keys(port, ssh) exploited = self.exploit_with_ssh_keys(port, ssh)
if not exploited: if not exploited:
exploited = self.exploit_with_login_creds(port, ssh) exploited = self.exploit_with_login_creds(port, ssh)
@ -165,18 +166,18 @@ class SSHExploiter(HostExploiter):
ftp.putfo(file_obj, self._config.dropper_target_path_linux, file_size=monkeyfs.getsize(src_path), ftp.putfo(file_obj, self._config.dropper_target_path_linux, file_size=monkeyfs.getsize(src_path),
callback=self.log_transfer) callback=self.log_transfer)
ftp.chmod(self._config.dropper_target_path_linux, 0o777) ftp.chmod(self._config.dropper_target_path_linux, 0o777)
status = ScanStatus.USED
T1222Telem(ScanStatus.USED, "chmod 0777 %s" % self._config.dropper_target_path_linux, self.host).send() T1222Telem(ScanStatus.USED, "chmod 0777 %s" % self._config.dropper_target_path_linux, self.host).send()
T1105Telem(ScanStatus.USED,
get_interface_to_target(self.host.ip_addr),
self.host.ip_addr,
src_path).send()
ftp.close() ftp.close()
except Exception as exc: except Exception as exc:
LOG.debug("Error uploading file into victim %r: (%s)", self.host, exc) LOG.debug("Error uploading file into victim %r: (%s)", self.host, exc)
T1105Telem(ScanStatus.SCANNED, status = ScanStatus.SCANNED
T1105Telem(status,
get_interface_to_target(self.host.ip_addr), get_interface_to_target(self.host.ip_addr),
self.host.ip_addr, self.host.ip_addr,
src_path).send() src_path).send()
if status == ScanStatus.SCANNED:
return False return False
try: try:

View File

@ -19,6 +19,7 @@ def get_interface_to_target(dst):
s.connect((dst, 1)) s.connect((dst, 1))
ip_to_dst = s.getsockname()[0] ip_to_dst = s.getsockname()[0]
except KeyError: except KeyError:
LOG.debug("Couldn't get an interface to the target, presuming that target is localhost.")
ip_to_dst = '127.0.0.1' ip_to_dst = '127.0.0.1'
finally: finally:
s.close() s.close()

View File

@ -36,8 +36,10 @@ class WmiExploiter(HostExploiter):
creds = self._config.get_exploit_user_password_or_hash_product() creds = self._config.get_exploit_user_password_or_hash_product()
for user, password, lm_hash, ntlm_hash in creds: for user, password, lm_hash, ntlm_hash in creds:
LOG.debug("Attempting to connect %r using WMI with user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')", password_hashed = self._config.hash_sensitive_data(password)
self.host, user, password, lm_hash, ntlm_hash) LOG.debug("Attempting to connect %r using WMI with "
"user,password (SHA-512),lm hash,ntlm hash: ('%s','%s','%s','%s')",
self.host, user, password_hashed, lm_hash, ntlm_hash)
wmi_connection = WmiTools.WmiConnection() wmi_connection = WmiTools.WmiConnection()
@ -47,23 +49,23 @@ class WmiExploiter(HostExploiter):
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash) self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
LOG.debug("Failed connecting to %r using WMI with " LOG.debug("Failed connecting to %r using WMI with "
"user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')", "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
self.host, user, password, lm_hash, ntlm_hash) self.host, user, password_hashed, lm_hash, ntlm_hash)
continue continue
except DCERPCException: except DCERPCException:
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash) self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
LOG.debug("Failed connecting to %r using WMI with " LOG.debug("Failed connecting to %r using WMI with "
"user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')", "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
self.host, user, password, lm_hash, ntlm_hash) self.host, user, password_hashed, lm_hash, ntlm_hash)
continue continue
except socket.error: except socket.error:
LOG.debug("Network error in WMI connection to %r with " LOG.debug("Network error in WMI connection to %r with "
"user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')", "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
self.host, user, password, lm_hash, ntlm_hash) self.host, user, password_hashed, lm_hash, ntlm_hash)
return False return False
except Exception as exc: except Exception as exc:
LOG.debug("Unknown WMI connection error to %r with " LOG.debug("Unknown WMI connection error to %r with "
"user,password,lm hash,ntlm hash: ('%s','%s','%s','%s') (%s):\n%s", "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s') (%s):\n%s",
self.host, user, password, lm_hash, ntlm_hash, exc, traceback.format_exc()) self.host, user, password_hashed, lm_hash, ntlm_hash, exc, traceback.format_exc())
return False return False
self.report_login_attempt(True, user, password, lm_hash, ntlm_hash) self.report_login_attempt(True, user, password, lm_hash, ntlm_hash)
@ -94,7 +96,8 @@ class WmiExploiter(HostExploiter):
# execute the remote dropper in case the path isn't final # execute the remote dropper in case the path isn't final
elif remote_full_path.lower() != self._config.dropper_target_path_win_32.lower(): elif remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \ cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \
build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32) build_monkey_commandline(
self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32)
else: else:
cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \ cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \
build_monkey_commandline(self.host, get_monkey_depth() - 1) build_monkey_commandline(self.host, get_monkey_depth() - 1)
@ -121,3 +124,4 @@ class WmiExploiter(HostExploiter):
return success return success
return False return False

View File

@ -68,7 +68,7 @@ def main():
else: else:
print("Config file wasn't supplied and default path: %s wasn't found, using internal default" % (config_file,)) print("Config file wasn't supplied and default path: %s wasn't found, using internal default" % (config_file,))
print("Loaded Configuration: %r" % WormConfiguration.as_dict()) print("Loaded Configuration: %r" % WormConfiguration.hide_sensitive_info(WormConfiguration.as_dict()))
# Make sure we're not in a machine that has the kill file # Make sure we're not in a machine that has the kill file
kill_path = os.path.expandvars( kill_path = os.path.expandvars(

View File

@ -184,7 +184,7 @@ class InfectionMonkey(object):
(':'+self._default_server_port if self._default_server_port else '')) (':'+self._default_server_port if self._default_server_port else ''))
else: else:
machine.set_default_server(self._default_server) machine.set_default_server(self._default_server)
LOG.debug("Default server: %s set to machine: %r" % (self._default_server, machine)) LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server))
# Order exploits according to their type # Order exploits according to their type
if WormConfiguration.should_exploit: if WormConfiguration.should_exploit:
@ -255,6 +255,7 @@ class InfectionMonkey(object):
if WormConfiguration.self_delete_in_cleanup \ if WormConfiguration.self_delete_in_cleanup \
and -1 == sys.executable.find('python'): and -1 == sys.executable.find('python'):
try: try:
status = None
if "win32" == sys.platform: if "win32" == sys.platform:
from _subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE from _subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
startupinfo = subprocess.STARTUPINFO() startupinfo = subprocess.STARTUPINFO()
@ -265,10 +266,12 @@ class InfectionMonkey(object):
close_fds=True, startupinfo=startupinfo) close_fds=True, startupinfo=startupinfo)
else: else:
os.remove(sys.executable) os.remove(sys.executable)
T1107Telem(ScanStatus.USED, sys.executable).send() status = ScanStatus.USED
except Exception as exc: except Exception as exc:
LOG.error("Exception in self delete: %s", exc) LOG.error("Exception in self delete: %s", exc)
T1107Telem(ScanStatus.SCANNED, sys.executable).send() status = ScanStatus.SCANNED
if status:
T1107Telem(status, sys.executable).send()
def send_log(self): def send_log(self):
monkey_log_path = utils.get_monkey_log_path() monkey_log_path = utils.get_monkey_log_path()

View File

@ -82,17 +82,22 @@ class UsersPBA(PBA):
pba_file_contents = ControlClient.get_pba_file(filename) pba_file_contents = ControlClient.get_pba_file(filename)
status = None
if not pba_file_contents or not pba_file_contents.content: if not pba_file_contents or not pba_file_contents.content:
LOG.error("Island didn't respond with post breach file.") LOG.error("Island didn't respond with post breach file.")
T1105Telem(ScanStatus.SCANNED, status = ScanStatus.SCANNED
if not status:
status = ScanStatus.USED
T1105Telem(status,
WormConfiguration.current_server.split(':')[0], WormConfiguration.current_server.split(':')[0],
get_interface_to_target(WormConfiguration.current_server.split(':')[0]), get_interface_to_target(WormConfiguration.current_server.split(':')[0]),
filename).send() filename).send()
if status == ScanStatus.SCANNED:
return False return False
T1105Telem(ScanStatus.USED,
WormConfiguration.current_server.split(':')[0],
get_interface_to_target(WormConfiguration.current_server.split(':')[0]),
filename).send()
try: try:
with open(os.path.join(dst_dir, filename), 'wb') as written_PBA_file: with open(os.path.join(dst_dir, filename), 'wb') as written_PBA_file:
written_PBA_file.write(pba_file_contents.content) written_PBA_file.write(pba_file_contents.content)

View File

@ -5,7 +5,7 @@ import socket
import zipfile import zipfile
import infection_monkey.config import infection_monkey.config
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus, UsageEnum
from infection_monkey.telemetry.attack.t1129_telem import T1129Telem from infection_monkey.telemetry.attack.t1129_telem import T1129Telem
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
from infection_monkey.pyinstaller_utils import get_binary_file_path, get_binaries_dir_path from infection_monkey.pyinstaller_utils import get_binary_file_path, get_binaries_dir_path
@ -47,16 +47,16 @@ class MimikatzCollector(object):
collect_proto = ctypes.WINFUNCTYPE(ctypes.c_int) collect_proto = ctypes.WINFUNCTYPE(ctypes.c_int)
get_proto = ctypes.WINFUNCTYPE(MimikatzCollector.LogonData) get_proto = ctypes.WINFUNCTYPE(MimikatzCollector.LogonData)
get_text_output_proto = ctypes.WINFUNCTYPE(ctypes.c_wchar_p) get_text_output_proto = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
T1106Telem(ScanStatus.USED, "WinAPI was called to load mimikatz.").send()
self._collect = collect_proto(("collect", self._dll)) self._collect = collect_proto(("collect", self._dll))
self._get = get_proto(("get", self._dll)) self._get = get_proto(("get", self._dll))
self._get_text_output_proto = get_text_output_proto(("getTextOutput", self._dll)) self._get_text_output_proto = get_text_output_proto(("getTextOutput", self._dll))
self._isInit = True self._isInit = True
T1129Telem(ScanStatus.USED, "Windows module loader was used to load Mimikatz DLL.").send() status = ScanStatus.USED
except Exception: except Exception:
LOG.exception("Error initializing mimikatz collector") LOG.exception("Error initializing mimikatz collector")
T1129Telem(ScanStatus.SCANNED, "Monkey tried to load Mimikatz DLL, but failed.").send() status = ScanStatus.SCANNED
T1106Telem(ScanStatus.SCANNED, "Monkey tried to call WinAPI to load mimikatz.").send() T1106Telem(status, UsageEnum.MIMIKATZ_WINAPI).send()
T1129Telem(status, UsageEnum.MIMIKATZ).send()
def get_logon_info(self): def get_logon_info(self):
""" """

View File

@ -5,7 +5,7 @@ from abc import ABCMeta, abstractmethod
from infection_monkey.config import WormConfiguration from infection_monkey.config import WormConfiguration
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus, UsageEnum
__author__ = 'itamar' __author__ = 'itamar'
@ -45,22 +45,25 @@ class WindowsSystemSingleton(_SystemSingleton):
ctypes.c_bool(True), ctypes.c_bool(True),
ctypes.c_char_p(self._mutex_name)) ctypes.c_char_p(self._mutex_name))
last_error = ctypes.windll.kernel32.GetLastError() last_error = ctypes.windll.kernel32.GetLastError()
status = None
if not handle: if not handle:
LOG.error("Cannot acquire system singleton %r, unknown error %d", LOG.error("Cannot acquire system singleton %r, unknown error %d",
self._mutex_name, last_error) self._mutex_name, last_error)
T1106Telem(ScanStatus.SCANNED, "WinAPI call to acquire system singleton " status = ScanStatus.SCANNED
"for monkey process wasn't successful.").send()
return False
if winerror.ERROR_ALREADY_EXISTS == last_error: if winerror.ERROR_ALREADY_EXISTS == last_error:
status = ScanStatus.SCANNED
LOG.debug("Cannot acquire system singleton %r, mutex already exist", LOG.debug("Cannot acquire system singleton %r, mutex already exist",
self._mutex_name) self._mutex_name)
if not status:
status = ScanStatus.USED
T1106Telem(status, UsageEnum.SINGLETON_WINAPI).send()
if status == ScanStatus.SCANNED:
return False return False
self._mutex_handle = handle self._mutex_handle = handle
T1106Telem(ScanStatus.USED, "WinAPI was called to acquire system singleton for monkey's process.").send()
LOG.debug("Global singleton mutex %r acquired", LOG.debug("Global singleton mutex %r acquired",
self._mutex_name) self._mutex_name)

View File

@ -2,21 +2,21 @@ from infection_monkey.telemetry.attack.attack_telem import AttackTelem
class T1005Telem(AttackTelem): class T1005Telem(AttackTelem):
def __init__(self, status, _type, info=""): def __init__(self, status, gathered_data_type, info=""):
""" """
T1005 telemetry. T1005 telemetry.
:param status: ScanStatus of technique :param status: ScanStatus of technique
:param _type: Type of data collected :param gathered_data_type: Type of data collected from local system
:param info: Additional info about data :param info: Additional info about data
""" """
super(T1005Telem, self).__init__('T1005', status) super(T1005Telem, self).__init__('T1005', status)
self._type = _type self.gathered_data_type = gathered_data_type
self.info = info self.info = info
def get_data(self): def get_data(self):
data = super(T1005Telem, self).get_data() data = super(T1005Telem, self).get_data()
data.update({ data.update({
'type': self._type, 'gathered_data_type': self.gathered_data_type,
'info': self.info 'info': self.info
}) })
return data return data

View File

@ -6,6 +6,6 @@ class T1035Telem(UsageTelem):
""" """
T1035 telemetry. T1035 telemetry.
:param status: ScanStatus of technique :param status: ScanStatus of technique
:param usage: Usage string :param usage: Enum of UsageEnum type
""" """
super(T1035Telem, self).__init__('T1035', status, usage) super(T1035Telem, self).__init__('T1035', status, usage)

View File

@ -4,8 +4,8 @@ from infection_monkey.telemetry.attack.usage_telem import UsageTelem
class T1106Telem(UsageTelem): class T1106Telem(UsageTelem):
def __init__(self, status, usage): def __init__(self, status, usage):
""" """
T1129 telemetry. T1106 telemetry.
:param status: ScanStatus of technique :param status: ScanStatus of technique
:param usage: Usage string :param usage: Enum name of UsageEnum
""" """
super(T1106Telem, self).__init__("T1106", status, usage) super(T1106Telem, self).__init__("T1106", status, usage)

View File

@ -6,6 +6,6 @@ class T1129Telem(UsageTelem):
""" """
T1129 telemetry. T1129 telemetry.
:param status: ScanStatus of technique :param status: ScanStatus of technique
:param usage: Usage string :param usage: Enum of UsageEnum type
""" """
super(T1129Telem, self).__init__("T1129", status, usage) super(T1129Telem, self).__init__("T1129", status, usage)

View File

@ -7,6 +7,7 @@ class T1222Telem(VictimHostTelem):
T1222 telemetry. T1222 telemetry.
:param status: ScanStatus of technique :param status: ScanStatus of technique
:param command: command used to change permissions :param command: command used to change permissions
:param machine: VictimHost type object
""" """
super(T1222Telem, self).__init__('T1222', status, machine) super(T1222Telem, self).__init__('T1222', status, machine)
self.command = command self.command = command

View File

@ -5,12 +5,12 @@ class UsageTelem(AttackTelem):
def __init__(self, technique, status, usage): def __init__(self, technique, status, usage):
""" """
T1035 telemetry. :param technique: Id of technique
:param status: ScanStatus of technique :param status: ScanStatus of technique
:param usage: Usage string :param usage: Enum of UsageEnum type
""" """
super(UsageTelem, self).__init__(technique, status) super(UsageTelem, self).__init__(technique, status)
self.usage = usage self.usage = usage.name
def get_data(self): def get_data(self):
data = super(UsageTelem, self).get_data() data = super(UsageTelem, self).get_data()

View File

@ -3,3 +3,4 @@ import os
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
MONKEY_ISLAND_ABS_PATH = os.path.join(os.getcwd(), 'monkey_island') MONKEY_ISLAND_ABS_PATH = os.path.join(os.getcwd(), 'monkey_island')
DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS = 60 * 5

View File

@ -16,5 +16,5 @@ from config import Config
from creds import Creds from creds import Creds
from monkey_ttl import MonkeyTtl from monkey_ttl import MonkeyTtl
from pba_results import PbaResults from pba_results import PbaResults
from c2_info import C2Info from command_control_channel import CommandControlChannel
from monkey import Monkey from monkey import Monkey

View File

@ -1,6 +0,0 @@
from mongoengine import EmbeddedDocument, StringField
class C2Info(EmbeddedDocument):
src = StringField()
dst = StringField()

View File

@ -0,0 +1,11 @@
from mongoengine import EmbeddedDocument, StringField
class CommandControlChannel(EmbeddedDocument):
"""
This value describes command and control channel monkey used in communication
src - Monkey Island's IP
dst - Monkey's IP (in case of a proxy chain this is the IP of the last monkey)
"""
src = StringField()
dst = StringField()

View File

@ -1,11 +1,12 @@
""" """
Define a Document Schema for the Monkey document. Define a Document Schema for the Monkey document.
""" """
import mongoengine
from mongoengine import Document, StringField, ListField, BooleanField, EmbeddedDocumentField, ReferenceField, \ from mongoengine import Document, StringField, ListField, BooleanField, EmbeddedDocumentField, ReferenceField, \
DateTimeField DateTimeField, DynamicField, DoesNotExist
from monkey_island.cc.models.monkey_ttl import MonkeyTtl from monkey_island.cc.models.monkey_ttl import MonkeyTtl, create_monkey_ttl_document
from monkey_island.cc.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS
from monkey_island.cc.models.command_control_channel import CommandControlChannel
class Monkey(Document): class Monkey(Document):
@ -26,26 +27,38 @@ class Monkey(Document):
ip_addresses = ListField(StringField()) ip_addresses = ListField(StringField())
keepalive = DateTimeField() keepalive = DateTimeField()
modifytime = DateTimeField() modifytime = DateTimeField()
# TODO change this to an embedded document as well - RN it's an unnamed tuple which is confusing. # TODO make "parent" an embedded document, so this can be removed and the schema explained (and validated) verbosly.
parent = ListField(ListField(StringField())) # This is a temporary fix, since mongoengine doesn't allow for lists of strings to be null
# (even with required=False of null=True).
# See relevant issue: https://github.com/MongoEngine/mongoengine/issues/1904
parent = ListField(ListField(DynamicField()))
config_error = BooleanField() config_error = BooleanField()
critical_services = ListField(StringField()) critical_services = ListField(StringField())
pba_results = ListField() pba_results = ListField()
ttl_ref = ReferenceField(MonkeyTtl) ttl_ref = ReferenceField(MonkeyTtl)
tunnel = ReferenceField("self") tunnel = ReferenceField("self")
c2_info = EmbeddedDocumentField('C2Info') command_control_channel = EmbeddedDocumentField(CommandControlChannel)
# LOGIC # LOGIC
@staticmethod @staticmethod
def get_single_monkey_by_id(db_id): def get_single_monkey_by_id(db_id):
try: try:
return Monkey.objects(id=db_id)[0] return Monkey.objects.get(id=db_id)
except IndexError: except DoesNotExist as ex:
raise MonkeyNotFoundError("id: {0}".format(str(db_id))) raise MonkeyNotFoundError("info: {0} | id: {1}".format(ex.message, str(db_id)))
@staticmethod
def get_single_monkey_by_guid(monkey_guid):
try:
return Monkey.objects.get(guid=monkey_guid)
except DoesNotExist as ex:
raise MonkeyNotFoundError("info: {0} | guid: {1}".format(ex.message, str(monkey_guid)))
@staticmethod @staticmethod
def get_latest_modifytime(): def get_latest_modifytime():
if Monkey.objects.count() > 0:
return Monkey.objects.order_by('-modifytime').first().modifytime return Monkey.objects.order_by('-modifytime').first().modifytime
return None
def is_dead(self): def is_dead(self):
monkey_is_dead = False monkey_is_dead = False
@ -56,7 +69,7 @@ class Monkey(Document):
if MonkeyTtl.objects(id=self.ttl_ref.id).count() == 0: if MonkeyTtl.objects(id=self.ttl_ref.id).count() == 0:
# No TTLs - monkey has timed out. The monkey is MIA. # No TTLs - monkey has timed out. The monkey is MIA.
monkey_is_dead = True monkey_is_dead = True
except (mongoengine.DoesNotExist, AttributeError): except (DoesNotExist, AttributeError):
# Trying to dereference unknown document - the monkey is MIA. # Trying to dereference unknown document - the monkey is MIA.
monkey_is_dead = True monkey_is_dead = True
return monkey_is_dead return monkey_is_dead
@ -69,6 +82,10 @@ class Monkey(Document):
os = "windows" os = "windows"
return os return os
def renew_ttl(self, duration=DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS):
self.ttl_ref = create_monkey_ttl_document(duration)
self.save()
@staticmethod @staticmethod
def get_tunneled_monkeys(): def get_tunneled_monkeys():
return Monkey.objects(tunnel__exists=True) return Monkey.objects(tunnel__exists=True)

View File

@ -38,3 +38,16 @@ class MonkeyTtl(Document):
} }
expire_at = DateTimeField() expire_at = DateTimeField()
def create_monkey_ttl_document(expiry_duration_in_seconds):
"""
Create a new Monkey TTL document and save it as a document.
:param expiry_duration_in_seconds: How long should the TTL last for. THIS IS A LOWER BOUND - depends on mongodb
performance.
:return: The TTL document. To get its ID use `.id`.
"""
# The TTL data uses the new `models` module which depends on mongoengine.
current_ttl = MonkeyTtl.create_ttl_expire_in(expiry_duration_in_seconds)
current_ttl.save()
return current_ttl

View File

@ -46,6 +46,19 @@ class TestMonkey(IslandTestCase):
self.assertTrue(mia_monkey.is_dead()) self.assertTrue(mia_monkey.is_dead())
self.assertFalse(alive_monkey.is_dead()) self.assertFalse(alive_monkey.is_dead())
def test_ttl_renewal(self):
self.fail_if_not_testing_env()
self.clean_monkey_db()
# Arrange
monkey = Monkey(guid=str(uuid.uuid4()))
monkey.save()
self.assertIsNone(monkey.ttl_ref)
# act + assert
monkey.renew_ttl()
self.assertIsNotNone(monkey.ttl_ref)
def test_get_single_monkey_by_id(self): def test_get_single_monkey_by_id(self):
self.fail_if_not_testing_env() self.fail_if_not_testing_env()
self.clean_monkey_db() self.clean_monkey_db()

View File

@ -5,26 +5,17 @@ import dateutil.parser
import flask_restful import flask_restful
from flask import request from flask import request
from monkey_island.cc.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.models.monkey_ttl import MonkeyTtl from monkey_island.cc.models.monkey_ttl import create_monkey_ttl_document
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS = 60 * 5
__author__ = 'Barak' __author__ = 'Barak'
# TODO: separate logic from interface # TODO: separate logic from interface
def create_monkey_ttl():
# The TTL data uses the new `models` module which depends on mongoengine.
current_ttl = MonkeyTtl.create_ttl_expire_in(MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS)
current_ttl.save()
ttlid = current_ttl.id
return ttlid
class Monkey(flask_restful.Resource): class Monkey(flask_restful.Resource):
# Used by monkey. can't secure. # Used by monkey. can't secure.
@ -58,8 +49,8 @@ class Monkey(flask_restful.Resource):
tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace("//", "") tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace("//", "")
NodeService.set_monkey_tunnel(monkey["_id"], tunnel_host_ip) NodeService.set_monkey_tunnel(monkey["_id"], tunnel_host_ip)
ttlid = create_monkey_ttl() ttl = create_monkey_ttl_document(DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS)
update['$set']['ttl_ref'] = ttlid update['$set']['ttl_ref'] = ttl.id
return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False) return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False)
@ -120,7 +111,8 @@ class Monkey(flask_restful.Resource):
tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace("//", "") tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace("//", "")
monkey_json.pop('tunnel') monkey_json.pop('tunnel')
monkey_json['ttl_ref'] = create_monkey_ttl() ttl = create_monkey_ttl_document(DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS)
monkey_json['ttl_ref'] = ttl.id
mongo.db.monkey.update({"guid": monkey_json["guid"]}, mongo.db.monkey.update({"guid": monkey_json["guid"]},
{"$set": monkey_json}, {"$set": monkey_json},

View File

@ -15,10 +15,10 @@ from monkey_island.cc.services.edge import EdgeService
from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
from monkey_island.cc.encryptor import encryptor from monkey_island.cc.encryptor import encryptor
from monkey_island.cc.services.wmi_handler import WMIHandler from monkey_island.cc.services.wmi_handler import WMIHandler
from monkey_island.cc.models.monkey import Monkey
__author__ = 'Barak' __author__ = 'Barak'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -48,7 +48,10 @@ class Telemetry(flask_restful.Resource):
def post(self): def post(self):
telemetry_json = json.loads(request.data) telemetry_json = json.loads(request.data)
telemetry_json['timestamp'] = datetime.now() telemetry_json['timestamp'] = datetime.now()
telemetry_json['c2_channel'] = {'src': request.remote_addr, 'dst': request.host} telemetry_json['command_control_channel'] = {'src': request.remote_addr, 'dst': request.host}
# Monkey communicated, so it's alive. Update the TTL.
Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']).renew_ttl()
monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
@ -60,7 +63,7 @@ class Telemetry(flask_restful.Resource):
else: else:
logger.info('Got unknown type of telemetry: %s' % telem_category) logger.info('Got unknown type of telemetry: %s' % telem_category)
except Exception as ex: except Exception as ex:
logger.error("Exception caught while processing telemetry", exc_info=True) logger.error("Exception caught while processing telemetry. Info: {}".format(ex.message), exc_info=True)
telem_id = mongo.db.telemetry.insert(telemetry_json) telem_id = mongo.db.telemetry.insert(telemetry_json)
return mongo.db.telemetry.find_one_or_404({"_id": telem_id}) return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
@ -111,7 +114,7 @@ class Telemetry(flask_restful.Resource):
@staticmethod @staticmethod
def process_state_telemetry(telemetry_json): def process_state_telemetry(telemetry_json):
monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
NodeService.add_communication_info(monkey, telemetry_json['c2_channel']) NodeService.add_communication_info(monkey, telemetry_json['command_control_channel'])
if telemetry_json['data']['done']: if telemetry_json['data']['done']:
NodeService.set_monkey_dead(monkey, True) NodeService.set_monkey_dead(monkey, True)
else: else:
@ -190,7 +193,7 @@ class Telemetry(flask_restful.Resource):
Telemetry.add_system_info_creds_to_config(creds) Telemetry.add_system_info_creds_to_config(creds)
Telemetry.replace_user_dot_with_comma(creds) Telemetry.replace_user_dot_with_comma(creds)
if 'mimikatz' in telemetry_json['data']: if 'mimikatz' in telemetry_json['data']:
users_secrets = mimikatz_utils.MimikatzSecrets.\ users_secrets = mimikatz_utils.MimikatzSecrets. \
extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', '')) extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', ''))
if 'wmi' in telemetry_json['data']: if 'wmi' in telemetry_json['data']:
wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)

View File

@ -1,3 +1,4 @@
import logging
from datetime import datetime from datetime import datetime
import dateutil import dateutil
@ -9,6 +10,8 @@ from monkey_island.cc.auth import jwt_required
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
logger = logging.getLogger(__name__)
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -23,11 +26,15 @@ class TelemetryFeed(flask_restful.Resource):
telemetries = telemetries.sort([('timestamp', flask_pymongo.ASCENDING)]) telemetries = telemetries.sort([('timestamp', flask_pymongo.ASCENDING)])
try:
return \ return \
{ {
'telemetries': [TelemetryFeed.get_displayed_telemetry(telem) for telem in telemetries], 'telemetries': [TelemetryFeed.get_displayed_telemetry(telem) for telem in telemetries],
'timestamp': datetime.now().isoformat() 'timestamp': datetime.now().isoformat()
} }
except KeyError as err:
logger.error("Failed parsing telemetries. Error: {0}.".format(err.message))
return {'telemetries': [], 'timestamp': datetime.now().isoformat()}
@staticmethod @staticmethod
def get_displayed_telemetry(telem): def get_displayed_telemetry(telem):

View File

@ -19,12 +19,12 @@ class T1005(AttackTechnique):
'as': 'monkey'}}, 'as': 'monkey'}},
{'$project': {'monkey': {'$arrayElemAt': ['$monkey', 0]}, {'$project': {'monkey': {'$arrayElemAt': ['$monkey', 0]},
'status': '$data.status', 'status': '$data.status',
'type': '$data.type', 'gathered_data_type': '$data.gathered_data_type',
'info': '$data.info'}}, 'info': '$data.info'}},
{'$addFields': {'_id': 0, {'$addFields': {'_id': 0,
'machine': {'hostname': '$monkey.hostname', 'ips': '$monkey.ip_addresses'}, 'machine': {'hostname': '$monkey.hostname', 'ips': '$monkey.ip_addresses'},
'monkey': 0}}, 'monkey': 0}},
{'$group': {'_id': {'machine': '$machine', 'type': '$type', 'info': '$info'}}}, {'$group': {'_id': {'machine': '$machine', 'gathered_data_type': '$gathered_data_type', 'info': '$info'}}},
{"$replaceRoot": {"newRoot": "$_id"}}] {"$replaceRoot": {"newRoot": "$_id"}}]
@staticmethod @staticmethod

View File

@ -1,10 +1,10 @@
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.services.attack.technique_reports import AttackTechnique from monkey_island.cc.services.attack.technique_reports import UsageTechnique
__author__ = "VakarisZ" __author__ = "VakarisZ"
class T1035(AttackTechnique): class T1035(UsageTechnique):
tech_id = "T1035" tech_id = "T1035"
unscanned_msg = "Monkey didn't try to interact with Windows services." unscanned_msg = "Monkey didn't try to interact with Windows services."
scanned_msg = "Monkey tried to interact with Windows services, but failed." scanned_msg = "Monkey tried to interact with Windows services, but failed."
@ -13,5 +13,5 @@ class T1035(AttackTechnique):
@staticmethod @staticmethod
def get_report_data(): def get_report_data():
data = T1035.get_tech_base_data() data = T1035.get_tech_base_data()
data.update({'services': list(mongo.db.telemetry.aggregate(T1035.get_usage_query()))}) data.update({'services': T1035.get_usage_data()})
return data return data

View File

@ -15,13 +15,13 @@ class T1041(AttackTechnique):
@staticmethod @staticmethod
def get_report_data(): def get_report_data():
monkeys = list(Monkey.objects()) monkeys = list(Monkey.objects())
info = [{'src': monkey['c2_info']['src'], info = [{'src': monkey['command_control_channel']['src'],
'dst': monkey['c2_info']['dst']} 'dst': monkey['command_control_channel']['dst']}
for monkey in monkeys if monkey['c2_info']] for monkey in monkeys if monkey['command_control_channel']]
if info: if info:
status = ScanStatus.USED.value status = ScanStatus.USED.value
else: else:
status = ScanStatus.UNSCANNED.value status = ScanStatus.UNSCANNED.value
data = T1041.get_base_data_by_status(status) data = T1041.get_base_data_by_status(status)
data.update({'c2_info': info}) data.update({'command_control_channel': info})
return data return data

View File

@ -1,10 +1,9 @@
from monkey_island.cc.database import mongo from monkey_island.cc.services.attack.technique_reports import UsageTechnique
from monkey_island.cc.services.attack.technique_reports import AttackTechnique
__author__ = "VakarisZ" __author__ = "VakarisZ"
class T1106(AttackTechnique): class T1106(UsageTechnique):
tech_id = "T1106" tech_id = "T1106"
unscanned_msg = "Monkey didn't try to directly use WinAPI." unscanned_msg = "Monkey didn't try to directly use WinAPI."
scanned_msg = "Monkey tried to use WinAPI, but failed." scanned_msg = "Monkey tried to use WinAPI, but failed."
@ -13,5 +12,5 @@ class T1106(AttackTechnique):
@staticmethod @staticmethod
def get_report_data(): def get_report_data():
data = T1106.get_tech_base_data() data = T1106.get_tech_base_data()
data.update({'api_uses': list(mongo.db.telemetry.aggregate(T1106.get_usage_query()))}) data.update({'api_uses': T1106.get_usage_data()})
return data return data

View File

@ -1,10 +1,10 @@
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.services.attack.technique_reports import AttackTechnique from monkey_island.cc.services.attack.technique_reports import UsageTechnique
__author__ = "VakarisZ" __author__ = "VakarisZ"
class T1129(AttackTechnique): class T1129(UsageTechnique):
tech_id = "T1129" tech_id = "T1129"
unscanned_msg = "Monkey didn't try to load any DLL's." unscanned_msg = "Monkey didn't try to load any DLL's."
scanned_msg = "Monkey tried to load DLL's, but failed." scanned_msg = "Monkey tried to load DLL's, but failed."
@ -13,5 +13,5 @@ class T1129(AttackTechnique):
@staticmethod @staticmethod
def get_report_data(): def get_report_data():
data = T1129.get_tech_base_data() data = T1129.get_tech_base_data()
data.update({'dlls': list(mongo.db.telemetry.aggregate(T1129.get_usage_query()))}) data.update({'dlls': T1129.get_usage_data()})
return data return data

View File

@ -1,10 +1,13 @@
import abc import abc
import logging
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus, UsageEnum
from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.services.attack.attack_config import AttackConfig
from common.utils.code_utils import abstractstatic from common.utils.code_utils import abstractstatic
logger = logging.getLogger(__name__)
class AttackTechnique(object): class AttackTechnique(object):
""" Abstract class for ATT&CK report components """ """ Abstract class for ATT&CK report components """
@ -113,6 +116,29 @@ class AttackTechnique(object):
data.update({'title': cls.technique_title()}) data.update({'title': cls.technique_title()})
return data return data
class UsageTechnique(AttackTechnique):
__metaclass__ = abc.ABCMeta
@staticmethod
def parse_usages(usage):
"""
Parses data from database and translates usage enums into strings
:param usage: Usage telemetry that contains fields: {'usage': 'SMB', 'status': 1}
:return: usage string
"""
try:
usage['usage'] = UsageEnum[usage['usage']].value[usage['status']]
except KeyError:
logger.error("Error translating usage enum. into string. "
"Check if usage enum field exists and covers all telem. statuses.")
return usage
@classmethod
def get_usage_data(cls):
data = list(mongo.db.telemetry.aggregate(cls.get_usage_query()))
return list(map(cls.parse_usages, data))
@classmethod @classmethod
def get_usage_query(cls): def get_usage_query(cls):
""" """
@ -131,4 +157,5 @@ class AttackTechnique(object):
{'$addFields': {'_id': 0, {'$addFields': {'_id': 0,
'machine': {'hostname': '$monkey.hostname', 'ips': '$monkey.ip_addresses'}, 'machine': {'hostname': '$monkey.hostname', 'ips': '$monkey.ip_addresses'},
'monkey': 0}}, 'monkey': 0}},
{'$group': {'_id': {'machine': '$machine', 'status': '$status', 'usage': '$usage'}}}] {'$group': {'_id': {'machine': '$machine', 'status': '$status', 'usage': '$usage'}}},
{"$replaceRoot": {"newRoot": "$_id"}}]

View File

@ -389,7 +389,7 @@ SCHEMA = {
"self_delete_in_cleanup": { "self_delete_in_cleanup": {
"title": "Self delete on cleanup", "title": "Self delete on cleanup",
"type": "boolean", "type": "boolean",
"default": False, "default": True,
"description": "Should the monkey delete its executable when going down" "description": "Should the monkey delete its executable when going down"
}, },
"use_file_logging": { "use_file_logging": {

View File

@ -250,7 +250,7 @@ class NodeService:
@staticmethod @staticmethod
def add_communication_info(monkey, info): def add_communication_info(monkey, info):
mongo.db.monkey.update({"guid": monkey["guid"]}, mongo.db.monkey.update({"guid": monkey["guid"]},
{"$set": {'c2_info': info}}, {"$set": {'command_control_channel': info}},
upsert=False) upsert=False)
@staticmethod @staticmethod

View File

@ -27,12 +27,12 @@ export function getUsageColumns() {
columns: [ columns: [
{Header: 'Machine', {Header: 'Machine',
id: 'machine', id: 'machine',
accessor: x => renderMachineFromSystemData(x._id.machine), accessor: x => renderMachineFromSystemData(x.machine),
style: { 'whiteSpace': 'unset' }, style: { 'whiteSpace': 'unset' },
width: 300}, width: 300},
{Header: 'Usage', {Header: 'Usage',
id: 'usage', id: 'usage',
accessor: x => x._id.usage, accessor: x => x.usage,
style: { 'whiteSpace': 'unset' }}] style: { 'whiteSpace': 'unset' }}]
}])} }])}
@ -49,7 +49,7 @@ export function renderCollections(info){
return (<div>{output}</div>); return (<div>{output}</div>);
} }
export const scanStatus = { export const ScanStatus = {
UNSCANNED: 0, UNSCANNED: 0,
SCANNED: 1, SCANNED: 1,
USED: 2 USED: 2

View File

@ -2,7 +2,7 @@ import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import '../../report-components/StolenPasswords' import '../../report-components/StolenPasswords'
import StolenPasswordsComponent from "../../report-components/StolenPasswords"; import StolenPasswordsComponent from "../../report-components/StolenPasswords";
import {scanStatus} from "./Helpers" import {ScanStatus} from "./Helpers"
class T1003 extends React.Component { class T1003 extends React.Component {
@ -16,7 +16,7 @@ class T1003 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<StolenPasswordsComponent data={this.props.reportData.glance.stolen_creds.concat(this.props.reportData.glance.ssh_keys)}/> <StolenPasswordsComponent data={this.props.reportData.glance.stolen_creds.concat(this.props.reportData.glance.ssh_keys)}/>
: ""} : ""}
</div> </div>

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import {renderMachineFromSystemData, scanStatus} from "./Helpers"; import {renderMachineFromSystemData, ScanStatus} from "./Helpers";
class T1005 extends React.Component { class T1005 extends React.Component {
@ -11,10 +11,10 @@ class T1005 extends React.Component {
static getDataColumns() { static getDataColumns() {
return ([{ return ([{
Header: "Data gathered from local systems", Header: "Sensitive data",
columns: [ columns: [
{Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.machine), style: { 'whiteSpace': 'unset' }}, {Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.machine), style: { 'whiteSpace': 'unset' }},
{Header: 'Type', id: 'type', accessor: x => x.type, style: { 'whiteSpace': 'unset' }}, {Header: 'Type', id: 'type', accessor: x => x.gathered_data_type, style: { 'whiteSpace': 'unset' }},
{Header: 'Info', id: 'info', accessor: x => x.info, style: { 'whiteSpace': 'unset' }}, {Header: 'Info', id: 'info', accessor: x => x.info, style: { 'whiteSpace': 'unset' }},
]}])}; ]}])};
@ -23,7 +23,7 @@ class T1005 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1005.getDataColumns()} columns={T1005.getDataColumns()}
data={this.props.data.collected_data} data={this.props.data.collected_data}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachineFromSystemData, renderMachine, scanStatus } from "./Helpers" import { renderMachineFromSystemData, renderMachine, ScanStatus } from "./Helpers"
class T1018 extends React.Component { class T1018 extends React.Component {
@ -22,8 +22,8 @@ class T1018 extends React.Component {
return ([{ return ([{
columns: [ columns: [
{Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.monkey), style: { 'whiteSpace': 'unset' }}, {Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.monkey), style: { 'whiteSpace': 'unset' }},
{Header: 'Started', id: 'started', accessor: x => x.started, style: { 'whiteSpace': 'unset' }}, {Header: 'First scan', id: 'started', accessor: x => x.started, style: { 'whiteSpace': 'unset' }},
{Header: 'Finished', id: 'finished', accessor: x => x.finished, style: { 'whiteSpace': 'unset' }}, {Header: 'Last scan', id: 'finished', accessor: x => x.finished, style: { 'whiteSpace': 'unset' }},
{Header: 'Systems found', id: 'systems', accessor: x => T1018.renderMachines(x.machines), style: { 'whiteSpace': 'unset' }}, {Header: 'Systems found', id: 'systems', accessor: x => T1018.renderMachines(x.machines), style: { 'whiteSpace': 'unset' }},
] ]
}])}; }])};
@ -33,7 +33,7 @@ class T1018 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1018.getScanInfoColumns()} columns={T1018.getScanInfoColumns()}
data={this.props.data.scan_info} data={this.props.data.scan_info}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import {scanStatus} from "./Helpers"; import {ScanStatus} from "./Helpers";
class T1041 extends React.Component { class T1041 extends React.Component {
@ -22,12 +22,12 @@ class T1041 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1041.getC2Columns()} columns={T1041.getC2Columns()}
data={this.props.data.c2_info} data={this.props.data.command_control_channel}
showPagination={false} showPagination={false}
defaultPageSize={this.props.data.c2_info.length} defaultPageSize={this.props.data.command_control_channel.length}
/> : ""} /> : ""}
</div> </div>
); );

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachine, scanStatus } from "./Helpers" import { renderMachine, ScanStatus } from "./Helpers"
class T1059 extends React.Component { class T1059 extends React.Component {
@ -25,7 +25,7 @@ class T1059 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1059.getCommandColumns()} columns={T1059.getCommandColumns()}
data={this.props.data.cmds} data={this.props.data.cmds}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachine, scanStatus } from "./Helpers" import { renderMachine, ScanStatus } from "./Helpers"
class T1075 extends React.Component { class T1075 extends React.Component {
@ -34,7 +34,7 @@ class T1075 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status !== scanStatus.UNSCANNED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1075.getHashColumns()} columns={T1075.getHashColumns()}
data={this.props.data.successful_logins} data={this.props.data.successful_logins}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachineFromSystemData, renderCollections, scanStatus } from "./Helpers" import { renderMachineFromSystemData, renderCollections, ScanStatus } from "./Helpers"
class T1082 extends React.Component { class T1082 extends React.Component {
@ -23,7 +23,7 @@ class T1082 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1082.getSystemInfoColumns()} columns={T1082.getSystemInfoColumns()}
data={this.props.data.system_info} data={this.props.data.system_info}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachine, scanStatus } from "./Helpers" import { renderMachine, ScanStatus } from "./Helpers"
class T1086 extends React.Component { class T1086 extends React.Component {
@ -25,7 +25,7 @@ class T1086 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1086.getPowershellColumns()} columns={T1086.getPowershellColumns()}
data={this.props.data.cmds} data={this.props.data.cmds}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachineFromSystemData, scanStatus } from "./Helpers" import { renderMachineFromSystemData, ScanStatus } from "./Helpers"
class T1090 extends React.Component { class T1090 extends React.Component {
@ -25,7 +25,7 @@ class T1090 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1090.getProxyColumns()} columns={T1090.getProxyColumns()}
data={this.props.data.proxies} data={this.props.data.proxies}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { scanStatus } from "./Helpers" import { ScanStatus } from "./Helpers"
class T1105 extends React.Component { class T1105 extends React.Component {
@ -12,7 +12,7 @@ class T1105 extends React.Component {
static getFilesColumns() { static getFilesColumns() {
return ([{ return ([{
Header: 'Files copied.', Header: 'Files copied',
columns: [ columns: [
{Header: 'Src. Machine', id: 'srcMachine', accessor: x => x.src, style: { 'whiteSpace': 'unset'}, width: 170 }, {Header: 'Src. Machine', id: 'srcMachine', accessor: x => x.src, style: { 'whiteSpace': 'unset'}, width: 170 },
{Header: 'Dst. Machine', id: 'dstMachine', accessor: x => x.dst, style: { 'whiteSpace': 'unset'}, width: 170}, {Header: 'Dst. Machine', id: 'dstMachine', accessor: x => x.dst, style: { 'whiteSpace': 'unset'}, width: 170},
@ -25,7 +25,7 @@ class T1105 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status !== scanStatus.UNSCANNED ? {this.props.data.status !== ScanStatus.UNSCANNED ?
<ReactTable <ReactTable
columns={T1105.getFilesColumns()} columns={T1105.getFilesColumns()}
data={this.props.data.files} data={this.props.data.files}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachineFromSystemData, scanStatus } from "./Helpers" import { renderMachineFromSystemData, ScanStatus } from "./Helpers"
class T1107 extends React.Component { class T1107 extends React.Component {
@ -11,7 +11,7 @@ class T1107 extends React.Component {
} }
static renderDelete(status){ static renderDelete(status){
if(status === scanStatus.USED){ if(status === ScanStatus.USED){
return <span>Yes</span> return <span>Yes</span>
} else { } else {
return <span>No</span> return <span>No</span>

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachine, scanStatus } from "./Helpers" import { renderMachine, ScanStatus } from "./Helpers"
class T1110 extends React.Component { class T1110 extends React.Component {
@ -32,7 +32,7 @@ class T1110 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status !== scanStatus.UNSCANNED ? {this.props.data.status !== ScanStatus.UNSCANNED ?
<ReactTable <ReactTable
columns={T1110.getServiceColumns()} columns={T1110.getServiceColumns()}
data={this.props.data.services} data={this.props.data.services}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachineFromSystemData, scanStatus } from "./Helpers" import { renderMachineFromSystemData, ScanStatus } from "./Helpers"
class T1145 extends React.Component { class T1145 extends React.Component {
@ -38,7 +38,7 @@ class T1145 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1145.getKeysInfoColumns()} columns={T1145.getKeysInfoColumns()}
data={this.props.data.ssh_info} data={this.props.data.ssh_info}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachineFromSystemData, scanStatus } from "./Helpers" import { renderMachineFromSystemData, ScanStatus } from "./Helpers"
class T1188 extends React.Component { class T1188 extends React.Component {
@ -34,7 +34,7 @@ class T1188 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1188.getHopColumns()} columns={T1188.getHopColumns()}
data={this.props.data.hops} data={this.props.data.hops}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import '../../../styles/Collapse.scss' import '../../../styles/Collapse.scss'
import ReactTable from "react-table"; import ReactTable from "react-table";
import { renderMachine, scanStatus } from "./Helpers" import { renderMachine, ScanStatus } from "./Helpers"
class T1222 extends React.Component { class T1222 extends React.Component {
@ -24,7 +24,7 @@ class T1222 extends React.Component {
<div> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
<br/> <br/>
{this.props.data.status === scanStatus.USED ? {this.props.data.status === ScanStatus.USED ?
<ReactTable <ReactTable
columns={T1222.getCommandColumns()} columns={T1222.getCommandColumns()}
data={this.props.data.commands} data={this.props.data.commands}

View File

@ -241,7 +241,7 @@ class RunMonkeyPageComponent extends AuthComponent {
<div style={{'marginTop': '1em', 'marginBottom': '1em'}}> <div style={{'marginTop': '1em', 'marginBottom': '1em'}}>
<p className="alert alert-info"> <p className="alert alert-info">
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/> <i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
Not sure what this is? Not seeing your AWS EC2 instances? <a href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances">Read the documentation</a>! Not sure what this is? Not seeing your AWS EC2 instances? <a href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances" target="_blank">Read the documentation</a>!
</p> </p>
</div> </div>
{ {

View File

@ -4,7 +4,7 @@ import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {edgeGroupToColor, options} from 'components/map/MapOptions'; import {edgeGroupToColor, options} from 'components/map/MapOptions';
import '../../styles/Collapse.scss'; import '../../styles/Collapse.scss';
import AuthComponent from '../AuthComponent'; import AuthComponent from '../AuthComponent';
import {scanStatus} from "../attack/techniques/Helpers"; import {ScanStatus} from "../attack/techniques/Helpers";
import Collapse from '@kunukn/react-collapse'; import Collapse from '@kunukn/react-collapse';
import T1210 from '../attack/techniques/T1210'; import T1210 from '../attack/techniques/T1210';
import T1197 from '../attack/techniques/T1197'; import T1197 from '../attack/techniques/T1197';
@ -102,9 +102,9 @@ class AttackReportPageComponent extends AuthComponent {
getComponentClass(tech_id){ getComponentClass(tech_id){
switch (this.state.report[tech_id].status) { switch (this.state.report[tech_id].status) {
case scanStatus.SCANNED: case ScanStatus.SCANNED:
return 'collapse-info'; return 'collapse-info';
case scanStatus.USED: case ScanStatus.USED:
return 'collapse-danger'; return 'collapse-danger';
default: default:
return 'collapse-default'; return 'collapse-default';

View File

@ -0,0 +1,23 @@
"""
Utility script for running a string through SHA3_512 hash.
Used for Monkey Island password hash, see
https://github.com/guardicore/monkey/wiki/Enabling-Monkey-Island-Password-Protection
for more details.
"""
import argparse
from Crypto.Hash import SHA3_512
def main():
parser = argparse.ArgumentParser()
parser.add_argument("string_to_sha", help="The string to do sha for")
args = parser.parse_args()
h = SHA3_512.new()
h.update(args.string_to_sha)
print(h.hexdigest())
if __name__ == '__main__':
main()