Merge remote-tracking branch 'upstream/develop' into attack_execution_api
# Conflicts: # monkey/infection_monkey/system_info/mimikatz_collector.py
This commit is contained in:
commit
cefe9a7d9c
|
@ -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
|
||||||
|
|
|
@ -13,9 +13,10 @@ Don't forget to add python to PATH or do so while installing it via this script.
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
You must have root permissions, but there is no need to run the script as root.<br>
|
You must have root permissions, but don't run the script as root.<br>
|
||||||
Launch deploy_linux.sh from scripts directory.<br>
|
Launch deploy_linux.sh from scripts directory.<br>
|
||||||
First argument is an empty directory (script can create one) and second is branch you want to clone.
|
First argument should be an empty directory (script can create one, default is ./infection_monkey) and second is the branch you want to clone (develop by default).
|
||||||
|
Choose a directory where you have all the relevant permissions, for e.g. /home/your_username
|
||||||
Example usages:<br>
|
Example usages:<br>
|
||||||
./deploy_linux.sh (deploys under ./infection_monkey)<br>
|
./deploy_linux.sh (deploys under ./infection_monkey)<br>
|
||||||
./deploy_linux.sh "/home/test/monkey" (deploys under /home/test/monkey)<br>
|
./deploy_linux.sh "/home/test/monkey" (deploys under /home/test/monkey)<br>
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -169,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",
|
||||||
|
|
|
@ -123,8 +123,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()
|
||||||
|
|
|
@ -9,16 +9,16 @@ 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 import HTTPTools, get_monkey_depth
|
from infection_monkey.exploit.tools import HTTPTools, get_monkey_depth
|
||||||
|
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||||
from infection_monkey.exploit.tools import get_target_monkey
|
from infection_monkey.exploit.tools import get_target_monkey
|
||||||
from infection_monkey.model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
|
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.exploit.tools import build_monkey_commandline
|
|
||||||
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'
|
||||||
|
|
||||||
|
@ -299,8 +299,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)
|
||||||
|
|
||||||
|
@ -327,8 +327,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)
|
||||||
|
|
|
@ -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:
|
||||||
|
@ -137,7 +137,7 @@ class SmbExploiter(HostExploiter):
|
||||||
except:
|
except:
|
||||||
status = ScanStatus.SCANNED
|
status = ScanStatus.SCANNED
|
||||||
pass
|
pass
|
||||||
T1035Telem(status, UsageEnum.SMB.name).send()
|
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)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
|
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 import build_monkey_commandline
|
||||||
from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth
|
from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth
|
||||||
from infection_monkey.model import MONKEY_ARG
|
from infection_monkey.model import MONKEY_ARG
|
||||||
from infection_monkey.network.tools import check_tcp_port
|
from infection_monkey.network.tools import check_tcp_port
|
||||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
|
||||||
from common.utils.exploit_enum import ExploitType
|
|
||||||
|
|
||||||
__author__ = 'hoffer'
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
@ -71,26 +71,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
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,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()
|
||||||
|
|
||||||
|
@ -44,23 +46,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)
|
||||||
|
@ -91,7 +93,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)
|
||||||
|
@ -118,3 +121,4 @@ class WmiExploiter(HostExploiter):
|
||||||
return success
|
return success
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -56,7 +56,7 @@ class MimikatzCollector(object):
|
||||||
LOG.exception("Error initializing mimikatz collector")
|
LOG.exception("Error initializing mimikatz collector")
|
||||||
status = ScanStatus.SCANNED
|
status = ScanStatus.SCANNED
|
||||||
T1106Telem(status, UsageEnum.MIMIKATZ_WINAPI.name).send()
|
T1106Telem(status, UsageEnum.MIMIKATZ_WINAPI.name).send()
|
||||||
T1129Telem(status, UsageEnum.MIMIKATZ.name).send()
|
T1129Telem(status, UsageEnum.MIMIKATZ).send()
|
||||||
|
|
||||||
|
|
||||||
def get_logon_info(self):
|
def get_logon_info(self):
|
||||||
|
|
|
@ -6,6 +6,6 @@ class T1035Telem(UsageTelem):
|
||||||
"""
|
"""
|
||||||
T1035 telemetry.
|
T1035 telemetry.
|
||||||
:param status: ScanStatus of technique
|
:param status: ScanStatus of technique
|
||||||
:param usage: Enum name of UsageEnum
|
:param usage: Enum of UsageEnum type
|
||||||
"""
|
"""
|
||||||
super(T1035Telem, self).__init__('T1035', status, usage)
|
super(T1035Telem, self).__init__('T1035', status, usage)
|
||||||
|
|
|
@ -6,6 +6,6 @@ class T1129Telem(UsageTelem):
|
||||||
"""
|
"""
|
||||||
T1129 telemetry.
|
T1129 telemetry.
|
||||||
:param status: ScanStatus of technique
|
:param status: ScanStatus of technique
|
||||||
:param usage: Enum name of UsageEnum
|
:param usage: Enum of UsageEnum type
|
||||||
"""
|
"""
|
||||||
super(T1129Telem, self).__init__("T1129", status, usage)
|
super(T1129Telem, self).__init__("T1129", status, usage)
|
||||||
|
|
|
@ -7,10 +7,10 @@ class UsageTelem(AttackTelem):
|
||||||
"""
|
"""
|
||||||
:param technique: Id of technique
|
:param technique: Id of technique
|
||||||
:param status: ScanStatus of technique
|
:param status: ScanStatus of technique
|
||||||
:param usage: Enum name of UsageEnum
|
: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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
class Monkey(Document):
|
class Monkey(Document):
|
||||||
|
@ -26,24 +26,37 @@ 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")
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -54,7 +67,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
|
||||||
|
@ -67,6 +80,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()
|
||||||
|
|
||||||
|
|
||||||
class MonkeyNotFoundError(Exception):
|
class MonkeyNotFoundError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,6 +49,9 @@ class Telemetry(flask_restful.Resource):
|
||||||
telemetry_json = json.loads(request.data)
|
telemetry_json = json.loads(request.data)
|
||||||
telemetry_json['timestamp'] = datetime.now()
|
telemetry_json['timestamp'] = datetime.now()
|
||||||
|
|
||||||
|
# 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'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -59,7 +62,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})
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -124,8 +124,8 @@ class UsageTechnique(AttackTechnique):
|
||||||
def parse_usages(usage):
|
def parse_usages(usage):
|
||||||
"""
|
"""
|
||||||
Parses data from database and translates usage enums into strings
|
Parses data from database and translates usage enums into strings
|
||||||
:param usage:
|
:param usage: Usage telemetry that contains fields: {'usage': 'SMB', 'status': 1}
|
||||||
:return:
|
:return: usage string
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
usage['usage'] = UsageEnum[usage['usage']].value[usage['status']]
|
usage['usage'] = UsageEnum[usage['usage']].value[usage['status']]
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -373,8 +373,13 @@ class ReportService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_exploits():
|
def get_exploits():
|
||||||
|
query = [{'$match': {'telem_category': 'exploit', 'data.result': True}},
|
||||||
|
{'$group': {'_id': {'ip_address': '$data.machine.ip_addr'},
|
||||||
|
'data': {'$first': '$$ROOT'},
|
||||||
|
}},
|
||||||
|
{"$replaceRoot": {"newRoot": "$data"}}]
|
||||||
exploits = []
|
exploits = []
|
||||||
for exploit in mongo.db.telemetry.find({'telem_category': 'exploit', 'data.result': True}):
|
for exploit in mongo.db.telemetry.aggregate(query):
|
||||||
new_exploit = ReportService.process_exploit(exploit)
|
new_exploit = ReportService.process_exploit(exploit)
|
||||||
if new_exploit not in exploits:
|
if new_exploit not in exploits:
|
||||||
exploits.append(new_exploit)
|
exploits.append(new_exploit)
|
||||||
|
|
|
@ -36,7 +36,7 @@ export function getUsageColumns() {
|
||||||
style: { 'whiteSpace': 'unset' }}]
|
style: { 'whiteSpace': 'unset' }}]
|
||||||
}])}
|
}])}
|
||||||
|
|
||||||
export const scanStatus = {
|
export const ScanStatus = {
|
||||||
UNSCANNED: 0,
|
UNSCANNED: 0,
|
||||||
SCANNED: 1,
|
SCANNED: 1,
|
||||||
USED: 2
|
USED: 2
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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 T1082 extends React.Component {
|
class T1082 extends React.Component {
|
||||||
|
@ -33,7 +33,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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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>
|
||||||
{
|
{
|
||||||
|
|
|
@ -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';
|
||||||
|
@ -86,9 +86,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';
|
||||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue