commit
294ec0d546
|
@ -17,7 +17,7 @@
|
||||||
"type": "snippet",
|
"type": "snippet",
|
||||||
"path": "monkey/infection_monkey/config.py",
|
"path": "monkey/infection_monkey/config.py",
|
||||||
"comments": [],
|
"comments": [],
|
||||||
"firstLineNumber": 126,
|
"firstLineNumber": 124,
|
||||||
"lines": [
|
"lines": [
|
||||||
" exploiter_classes = []",
|
" exploiter_classes = []",
|
||||||
" system_info_collector_classes = []",
|
" system_info_collector_classes = []",
|
||||||
|
@ -33,19 +33,18 @@
|
||||||
"type": "snippet",
|
"type": "snippet",
|
||||||
"path": "monkey/infection_monkey/monkey.py",
|
"path": "monkey/infection_monkey/monkey.py",
|
||||||
"comments": [],
|
"comments": [],
|
||||||
"firstLineNumber": 159,
|
"firstLineNumber": 220,
|
||||||
"lines": [
|
"lines": [
|
||||||
|
" if not self._keep_running or not WormConfiguration.alive:",
|
||||||
|
" break",
|
||||||
" ",
|
" ",
|
||||||
" if not self._keep_running or not WormConfiguration.alive:",
|
"* machines = self._network.get_victim_machines(",
|
||||||
" break",
|
"* max_find=WormConfiguration.victims_max_find,",
|
||||||
"*",
|
"* stop_callback=ControlClient.check_for_stop,",
|
||||||
"* machines = self._network.get_victim_machines(",
|
"* )",
|
||||||
"* max_find=WormConfiguration.victims_max_find,",
|
" is_empty = True",
|
||||||
"* stop_callback=ControlClient.check_for_stop,",
|
" for machine in machines:",
|
||||||
"* )",
|
" if ControlClient.check_for_stop():"
|
||||||
" is_empty = True",
|
|
||||||
" for machine in machines:",
|
|
||||||
" if ControlClient.check_for_stop():"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -77,11 +76,11 @@
|
||||||
"symbols": {},
|
"symbols": {},
|
||||||
"file_version": "2.0.1",
|
"file_version": "2.0.1",
|
||||||
"meta": {
|
"meta": {
|
||||||
"app_version": "0.4.1-1",
|
"app_version": "0.4.9-1",
|
||||||
"file_blobs": {
|
"file_blobs": {
|
||||||
"monkey/infection_monkey/config.py": "ffdea551eb1ae2b65d4700db896c746771e7954c",
|
"monkey/infection_monkey/config.py": "0bede1c57949987f5c8025bd9b8f7aa29d02a6af",
|
||||||
"monkey/infection_monkey/monkey.py": "c81a6251746e3af4e93eaa7d50af44d33debe05c",
|
"monkey/infection_monkey/monkey.py": "89d2fa8452dee70f6d2985a9bb452f0159ea8219",
|
||||||
"monkey/monkey_island/cc/services/config_schema/internal.py": "d03527b89c21dfb832a15e4f7d55f4027d83b453"
|
"monkey/monkey_island/cc/services/config_schema/internal.py": "1ce1c864b1df332b65e16b4ce9ed533affd73f9c"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Attempted to delete a directory when monkey config reset was called. #1054
|
- Attempted to delete a directory when monkey config reset was called. #1054
|
||||||
- An errant space in the windows commands to run monkey manually. #1153
|
- An errant space in the windows commands to run monkey manually. #1153
|
||||||
- gevent tracebacks in console output. #859
|
- gevent tracebacks in console output. #859
|
||||||
|
- Crash and failure to run PBAs if max depth reached. #1374
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
- Address minor issues discovered by Dlint. #1075
|
- Address minor issues discovered by Dlint. #1075
|
||||||
|
|
|
@ -22,7 +22,6 @@ from infection_monkey.config import GUID, WormConfiguration
|
||||||
from infection_monkey.network.info import check_internet_access, local_ips
|
from infection_monkey.network.info import check_internet_access, local_ips
|
||||||
from infection_monkey.transport.http import HTTPConnectProxy
|
from infection_monkey.transport.http import HTTPConnectProxy
|
||||||
from infection_monkey.transport.tcp import TcpProxy
|
from infection_monkey.transport.tcp import TcpProxy
|
||||||
from infection_monkey.utils.exceptions.planned_shutdown_exception import PlannedShutdownException
|
|
||||||
|
|
||||||
requests.packages.urllib3.disable_warnings()
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
|
@ -401,10 +400,8 @@ class ControlClient(object):
|
||||||
and ControlClient.can_island_see_port(vulnerable_port)
|
and ControlClient.can_island_see_port(vulnerable_port)
|
||||||
and WormConfiguration.started_on_island
|
and WormConfiguration.started_on_island
|
||||||
):
|
):
|
||||||
raise PlannedShutdownException(
|
return False
|
||||||
"Monkey shouldn't run on current machine "
|
|
||||||
"(it will be exploited later with more depth)."
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -40,7 +40,7 @@ from infection_monkey.utils.monkey_dir import (
|
||||||
from infection_monkey.utils.monkey_log_path import get_monkey_log_path
|
from infection_monkey.utils.monkey_log_path import get_monkey_log_path
|
||||||
from infection_monkey.windows_upgrader import WindowsUpgrader
|
from infection_monkey.windows_upgrader import WindowsUpgrader
|
||||||
|
|
||||||
MAX_DEPTH_REACHED_MESSAGE = "Reached max depth, shutting down"
|
MAX_DEPTH_REACHED_MESSAGE = "Reached max depth, skipping propagation phase."
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -62,6 +62,8 @@ class InfectionMonkey(object):
|
||||||
self._default_server_port = None
|
self._default_server_port = None
|
||||||
self._opts = None
|
self._opts = None
|
||||||
self._upgrading_to_64 = False
|
self._upgrading_to_64 = False
|
||||||
|
self._monkey_tunnel = None
|
||||||
|
self._post_breach_phase = None
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
LOG.info("Monkey is initializing...")
|
LOG.info("Monkey is initializing...")
|
||||||
|
@ -125,124 +127,36 @@ class InfectionMonkey(object):
|
||||||
if is_running_on_island():
|
if is_running_on_island():
|
||||||
WormConfiguration.started_on_island = True
|
WormConfiguration.started_on_island = True
|
||||||
ControlClient.report_start_on_island()
|
ControlClient.report_start_on_island()
|
||||||
ControlClient.should_monkey_run(self._opts.vulnerable_port)
|
|
||||||
|
if not ControlClient.should_monkey_run(self._opts.vulnerable_port):
|
||||||
|
raise PlannedShutdownException(
|
||||||
|
"Monkey shouldn't run on current machine "
|
||||||
|
"(it will be exploited later with more depth)."
|
||||||
|
)
|
||||||
|
|
||||||
if firewall.is_enabled():
|
if firewall.is_enabled():
|
||||||
firewall.add_firewall_rule()
|
firewall.add_firewall_rule()
|
||||||
|
|
||||||
monkey_tunnel = ControlClient.create_control_tunnel()
|
self._monkey_tunnel = ControlClient.create_control_tunnel()
|
||||||
if monkey_tunnel:
|
if self._monkey_tunnel:
|
||||||
monkey_tunnel.start()
|
self._monkey_tunnel.start()
|
||||||
|
|
||||||
StateTelem(is_done=False, version=get_version()).send()
|
StateTelem(is_done=False, version=get_version()).send()
|
||||||
TunnelTelem().send()
|
TunnelTelem().send()
|
||||||
|
|
||||||
LOG.debug("Starting the post-breach phase asynchronously.")
|
LOG.debug("Starting the post-breach phase asynchronously.")
|
||||||
post_breach_phase = Thread(target=self.start_post_breach_phase)
|
self._post_breach_phase = Thread(target=self.start_post_breach_phase)
|
||||||
post_breach_phase.start()
|
self._post_breach_phase.start()
|
||||||
|
|
||||||
LOG.debug("Starting the propagation phase.")
|
if not InfectionMonkey.max_propagation_depth_reached():
|
||||||
self.shutdown_by_max_depth_reached()
|
LOG.info("Starting the propagation phase.")
|
||||||
|
LOG.debug("Running with depth: %d" % WormConfiguration.depth)
|
||||||
|
self.propagate()
|
||||||
|
else:
|
||||||
|
LOG.info("Maximum propagation depth has been reached; monkey will not propagate.")
|
||||||
|
TraceTelem(MAX_DEPTH_REACHED_MESSAGE).send()
|
||||||
|
|
||||||
for iteration_index in range(WormConfiguration.max_iterations):
|
InfectionMonkey.run_ransomware()
|
||||||
ControlClient.keepalive()
|
|
||||||
ControlClient.load_control_config()
|
|
||||||
|
|
||||||
self._network.initialize()
|
|
||||||
|
|
||||||
self._fingerprint = HostFinger.get_instances()
|
|
||||||
|
|
||||||
self._exploiters = HostExploiter.get_classes()
|
|
||||||
|
|
||||||
if not self._keep_running or not WormConfiguration.alive:
|
|
||||||
break
|
|
||||||
|
|
||||||
machines = self._network.get_victim_machines(
|
|
||||||
max_find=WormConfiguration.victims_max_find,
|
|
||||||
stop_callback=ControlClient.check_for_stop,
|
|
||||||
)
|
|
||||||
is_empty = True
|
|
||||||
for machine in machines:
|
|
||||||
if ControlClient.check_for_stop():
|
|
||||||
break
|
|
||||||
|
|
||||||
is_empty = False
|
|
||||||
for finger in self._fingerprint:
|
|
||||||
LOG.info(
|
|
||||||
"Trying to get OS fingerprint from %r with module %s",
|
|
||||||
machine,
|
|
||||||
finger.__class__.__name__,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
finger.get_host_fingerprint(machine)
|
|
||||||
except BaseException as exc:
|
|
||||||
LOG.error(
|
|
||||||
"Failed to run fingerprinter %s, exception %s"
|
|
||||||
% finger.__class__.__name__,
|
|
||||||
str(exc),
|
|
||||||
)
|
|
||||||
|
|
||||||
ScanTelem(machine).send()
|
|
||||||
|
|
||||||
# skip machines that we've already exploited
|
|
||||||
if machine in self._exploited_machines:
|
|
||||||
LOG.debug("Skipping %r - already exploited", machine)
|
|
||||||
continue
|
|
||||||
elif machine in self._fail_exploitation_machines:
|
|
||||||
if WormConfiguration.retry_failed_explotation:
|
|
||||||
LOG.debug("%r - exploitation failed before, trying again", machine)
|
|
||||||
else:
|
|
||||||
LOG.debug("Skipping %r - exploitation failed before", machine)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if monkey_tunnel:
|
|
||||||
monkey_tunnel.set_tunnel_for_host(machine)
|
|
||||||
if self._default_server:
|
|
||||||
if self._network.on_island(self._default_server):
|
|
||||||
machine.set_default_server(
|
|
||||||
get_interface_to_target(machine.ip_addr)
|
|
||||||
+ (
|
|
||||||
":" + self._default_server_port
|
|
||||||
if self._default_server_port
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
machine.set_default_server(self._default_server)
|
|
||||||
LOG.debug(
|
|
||||||
"Default server for machine: %r set to %s"
|
|
||||||
% (machine, machine.default_server)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Order exploits according to their type
|
|
||||||
self._exploiters = sorted(
|
|
||||||
self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value
|
|
||||||
)
|
|
||||||
host_exploited = False
|
|
||||||
for exploiter in [exploiter(machine) for exploiter in self._exploiters]:
|
|
||||||
if self.try_exploiting(machine, exploiter):
|
|
||||||
host_exploited = True
|
|
||||||
VictimHostTelem("T1210", ScanStatus.USED, machine=machine).send()
|
|
||||||
if exploiter.RUNS_AGENT_ON_SUCCESS:
|
|
||||||
break # if adding machine to exploited, won't try other exploits
|
|
||||||
# on it
|
|
||||||
if not host_exploited:
|
|
||||||
self._fail_exploitation_machines.add(machine)
|
|
||||||
VictimHostTelem("T1210", ScanStatus.SCANNED, machine=machine).send()
|
|
||||||
if not self._keep_running:
|
|
||||||
break
|
|
||||||
|
|
||||||
InfectionMonkey.run_ransomware()
|
|
||||||
|
|
||||||
if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1):
|
|
||||||
time_to_sleep = WormConfiguration.timeout_between_iterations
|
|
||||||
LOG.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep)
|
|
||||||
time.sleep(time_to_sleep)
|
|
||||||
|
|
||||||
if self._keep_running and WormConfiguration.alive:
|
|
||||||
LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations)
|
|
||||||
elif not WormConfiguration.alive:
|
|
||||||
LOG.info("Marked not alive from configuration")
|
|
||||||
|
|
||||||
# if host was exploited, before continue to closing the tunnel ensure the exploited
|
# if host was exploited, before continue to closing the tunnel ensure the exploited
|
||||||
# host had its chance to
|
# host had its chance to
|
||||||
|
@ -254,12 +168,6 @@ class InfectionMonkey(object):
|
||||||
)
|
)
|
||||||
time.sleep(time_to_sleep)
|
time.sleep(time_to_sleep)
|
||||||
|
|
||||||
if monkey_tunnel:
|
|
||||||
monkey_tunnel.stop()
|
|
||||||
monkey_tunnel.join()
|
|
||||||
|
|
||||||
post_breach_phase.join()
|
|
||||||
|
|
||||||
except PlannedShutdownException:
|
except PlannedShutdownException:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"A planned shutdown of the Monkey occurred. Logging the reason and finishing "
|
"A planned shutdown of the Monkey occurred. Logging the reason and finishing "
|
||||||
|
@ -267,16 +175,21 @@ class InfectionMonkey(object):
|
||||||
)
|
)
|
||||||
LOG.exception("Planned shutdown, reason:")
|
LOG.exception("Planned shutdown, reason:")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if self._monkey_tunnel:
|
||||||
|
self._monkey_tunnel.stop()
|
||||||
|
self._monkey_tunnel.join()
|
||||||
|
|
||||||
|
if self._post_breach_phase:
|
||||||
|
self._post_breach_phase.join()
|
||||||
|
|
||||||
def start_post_breach_phase(self):
|
def start_post_breach_phase(self):
|
||||||
self.collect_system_info_if_configured()
|
self.collect_system_info_if_configured()
|
||||||
PostBreach().execute_all_configured()
|
PostBreach().execute_all_configured()
|
||||||
|
|
||||||
def shutdown_by_max_depth_reached(self):
|
@staticmethod
|
||||||
if 0 == WormConfiguration.depth:
|
def max_propagation_depth_reached():
|
||||||
TraceTelem(MAX_DEPTH_REACHED_MESSAGE).send()
|
return 0 == WormConfiguration.depth
|
||||||
raise PlannedShutdownException(MAX_DEPTH_REACHED_MESSAGE)
|
|
||||||
else:
|
|
||||||
LOG.debug("Running with depth: %d" % WormConfiguration.depth)
|
|
||||||
|
|
||||||
def collect_system_info_if_configured(self):
|
def collect_system_info_if_configured(self):
|
||||||
LOG.debug("Calling for system info collection")
|
LOG.debug("Calling for system info collection")
|
||||||
|
@ -291,6 +204,101 @@ class InfectionMonkey(object):
|
||||||
if not WormConfiguration.alive:
|
if not WormConfiguration.alive:
|
||||||
raise PlannedShutdownException("Marked 'not alive' from configuration.")
|
raise PlannedShutdownException("Marked 'not alive' from configuration.")
|
||||||
|
|
||||||
|
def propagate(self):
|
||||||
|
for iteration_index in range(WormConfiguration.max_iterations):
|
||||||
|
ControlClient.keepalive()
|
||||||
|
ControlClient.load_control_config()
|
||||||
|
|
||||||
|
self._network.initialize()
|
||||||
|
|
||||||
|
self._fingerprint = HostFinger.get_instances()
|
||||||
|
|
||||||
|
self._exploiters = HostExploiter.get_classes()
|
||||||
|
|
||||||
|
if not self._keep_running or not WormConfiguration.alive:
|
||||||
|
break
|
||||||
|
|
||||||
|
machines = self._network.get_victim_machines(
|
||||||
|
max_find=WormConfiguration.victims_max_find,
|
||||||
|
stop_callback=ControlClient.check_for_stop,
|
||||||
|
)
|
||||||
|
is_empty = True
|
||||||
|
for machine in machines:
|
||||||
|
if ControlClient.check_for_stop():
|
||||||
|
break
|
||||||
|
|
||||||
|
is_empty = False
|
||||||
|
for finger in self._fingerprint:
|
||||||
|
LOG.info(
|
||||||
|
"Trying to get OS fingerprint from %r with module %s",
|
||||||
|
machine,
|
||||||
|
finger.__class__.__name__,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
finger.get_host_fingerprint(machine)
|
||||||
|
except BaseException as exc:
|
||||||
|
LOG.error(
|
||||||
|
"Failed to run fingerprinter %s, exception %s"
|
||||||
|
% finger.__class__.__name__,
|
||||||
|
str(exc),
|
||||||
|
)
|
||||||
|
|
||||||
|
ScanTelem(machine).send()
|
||||||
|
|
||||||
|
# skip machines that we've already exploited
|
||||||
|
if machine in self._exploited_machines:
|
||||||
|
LOG.debug("Skipping %r - already exploited", machine)
|
||||||
|
continue
|
||||||
|
elif machine in self._fail_exploitation_machines:
|
||||||
|
if WormConfiguration.retry_failed_explotation:
|
||||||
|
LOG.debug("%r - exploitation failed before, trying again", machine)
|
||||||
|
else:
|
||||||
|
LOG.debug("Skipping %r - exploitation failed before", machine)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self._monkey_tunnel:
|
||||||
|
self._monkey_tunnel.set_tunnel_for_host(machine)
|
||||||
|
if self._default_server:
|
||||||
|
if self._network.on_island(self._default_server):
|
||||||
|
machine.set_default_server(
|
||||||
|
get_interface_to_target(machine.ip_addr)
|
||||||
|
+ (":" + self._default_server_port if self._default_server_port else "")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
machine.set_default_server(self._default_server)
|
||||||
|
LOG.debug(
|
||||||
|
"Default server for machine: %r set to %s"
|
||||||
|
% (machine, machine.default_server)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Order exploits according to their type
|
||||||
|
self._exploiters = sorted(
|
||||||
|
self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value
|
||||||
|
)
|
||||||
|
host_exploited = False
|
||||||
|
for exploiter in [exploiter(machine) for exploiter in self._exploiters]:
|
||||||
|
if self.try_exploiting(machine, exploiter):
|
||||||
|
host_exploited = True
|
||||||
|
VictimHostTelem("T1210", ScanStatus.USED, machine=machine).send()
|
||||||
|
if exploiter.RUNS_AGENT_ON_SUCCESS:
|
||||||
|
break # if adding machine to exploited, won't try other exploits
|
||||||
|
# on it
|
||||||
|
if not host_exploited:
|
||||||
|
self._fail_exploitation_machines.add(machine)
|
||||||
|
VictimHostTelem("T1210", ScanStatus.SCANNED, machine=machine).send()
|
||||||
|
if not self._keep_running:
|
||||||
|
break
|
||||||
|
|
||||||
|
if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1):
|
||||||
|
time_to_sleep = WormConfiguration.timeout_between_iterations
|
||||||
|
LOG.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep)
|
||||||
|
time.sleep(time_to_sleep)
|
||||||
|
|
||||||
|
if self._keep_running and WormConfiguration.alive:
|
||||||
|
LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations)
|
||||||
|
elif not WormConfiguration.alive:
|
||||||
|
LOG.info("Marked not alive from configuration")
|
||||||
|
|
||||||
def upgrade_to_64_if_needed(self):
|
def upgrade_to_64_if_needed(self):
|
||||||
if WindowsUpgrader.should_upgrade():
|
if WindowsUpgrader.should_upgrade():
|
||||||
self._upgrading_to_64 = True
|
self._upgrading_to_64 = True
|
||||||
|
|
Loading…
Reference in New Issue