forked from p15670423/monkey
Merge pull request #1759 from guardicore/keep-tunnel-open
Keep tunnel open
This commit is contained in:
commit
731965f438
|
@ -292,7 +292,12 @@ class ControlClient(object):
|
||||||
proxy_class = HTTPConnectProxy
|
proxy_class = HTTPConnectProxy
|
||||||
target_addr, target_port = None, None
|
target_addr, target_port = None, None
|
||||||
|
|
||||||
return tunnel.MonkeyTunnel(proxy_class, target_addr=target_addr, target_port=target_port)
|
return tunnel.MonkeyTunnel(
|
||||||
|
proxy_class,
|
||||||
|
keep_tunnel_open_time=WormConfiguration.keep_tunnel_open_time,
|
||||||
|
target_addr=target_addr,
|
||||||
|
target_port=target_port,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_pba_file(filename):
|
def get_pba_file(filename):
|
||||||
|
|
|
@ -3,7 +3,6 @@ import logging
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import infection_monkey.tunnel as tunnel
|
import infection_monkey.tunnel as tunnel
|
||||||
|
@ -36,6 +35,9 @@ from infection_monkey.puppet.puppet import Puppet
|
||||||
from infection_monkey.system_singleton import SystemSingleton
|
from infection_monkey.system_singleton import SystemSingleton
|
||||||
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
||||||
from infection_monkey.telemetry.attack.t1107_telem import T1107Telem
|
from infection_monkey.telemetry.attack.t1107_telem import T1107Telem
|
||||||
|
from infection_monkey.telemetry.messengers.exploit_intercepting_telemetry_messenger import (
|
||||||
|
ExploitInterceptingTelemetryMessenger,
|
||||||
|
)
|
||||||
from infection_monkey.telemetry.messengers.legacy_telemetry_messenger_adapter import (
|
from infection_monkey.telemetry.messengers.legacy_telemetry_messenger_adapter import (
|
||||||
LegacyTelemetryMessengerAdapter,
|
LegacyTelemetryMessengerAdapter,
|
||||||
)
|
)
|
||||||
|
@ -164,9 +166,13 @@ class InfectionMonkey:
|
||||||
|
|
||||||
victim_host_factory = self._build_victim_host_factory(local_network_interfaces)
|
victim_host_factory = self._build_victim_host_factory(local_network_interfaces)
|
||||||
|
|
||||||
|
telemetry_messenger = ExploitInterceptingTelemetryMessenger(
|
||||||
|
self.telemetry_messenger, self._monkey_inbound_tunnel
|
||||||
|
)
|
||||||
|
|
||||||
self._master = AutomatedMaster(
|
self._master = AutomatedMaster(
|
||||||
puppet,
|
puppet,
|
||||||
self.telemetry_messenger,
|
telemetry_messenger,
|
||||||
victim_host_factory,
|
victim_host_factory,
|
||||||
ControlChannel(self._default_server, GUID),
|
ControlChannel(self._default_server, GUID),
|
||||||
local_network_interfaces,
|
local_network_interfaces,
|
||||||
|
@ -237,7 +243,6 @@ class InfectionMonkey:
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
logger.info("Monkey cleanup started")
|
logger.info("Monkey cleanup started")
|
||||||
self._wait_for_exploited_machine_connection()
|
|
||||||
try:
|
try:
|
||||||
if self._master:
|
if self._master:
|
||||||
self._master.cleanup()
|
self._master.cleanup()
|
||||||
|
@ -270,19 +275,6 @@ class InfectionMonkey:
|
||||||
|
|
||||||
logger.info("Monkey is shutting down")
|
logger.info("Monkey is shutting down")
|
||||||
|
|
||||||
def _wait_for_exploited_machine_connection(self):
|
|
||||||
# TODO check for actual exploitation
|
|
||||||
machines_exploited = False
|
|
||||||
# if host was exploited, before continue to closing the tunnel ensure the exploited
|
|
||||||
# host had its chance to
|
|
||||||
# connect to the tunnel
|
|
||||||
if machines_exploited:
|
|
||||||
time_to_sleep = WormConfiguration.keep_tunnel_open_time
|
|
||||||
logger.info(
|
|
||||||
"Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep
|
|
||||||
)
|
|
||||||
time.sleep(time_to_sleep)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _close_tunnel():
|
def _close_tunnel():
|
||||||
tunnel_address = (
|
tunnel_address = (
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
from functools import singledispatch
|
||||||
|
|
||||||
|
from infection_monkey.telemetry.i_telem import ITelem
|
||||||
|
from infection_monkey.telemetry.exploit_telem import ExploitTelem
|
||||||
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
|
from infection_monkey.tunnel import MonkeyTunnel
|
||||||
|
|
||||||
|
|
||||||
|
class ExploitInterceptingTelemetryMessenger(ITelemetryMessenger):
|
||||||
|
def __init__(self, telemetry_messenger: ITelemetryMessenger, tunnel: MonkeyTunnel):
|
||||||
|
self._telemetry_messenger = telemetry_messenger
|
||||||
|
self._tunnel = tunnel
|
||||||
|
|
||||||
|
def send_telemetry(self, telemetry: ITelem):
|
||||||
|
_send_telemetry(telemetry, self._telemetry_messenger, self._tunnel)
|
||||||
|
|
||||||
|
|
||||||
|
# Note: We can use @singledispatchmethod instead of @singledispatch if we migrate to Python 3.8 or
|
||||||
|
# later.
|
||||||
|
@singledispatch
|
||||||
|
def _send_telemetry(
|
||||||
|
telemetry: ITelem, telemetry_messenger: ITelemetryMessenger, tunnel: MonkeyTunnel
|
||||||
|
):
|
||||||
|
telemetry_messenger.send_telemetry(telemetry)
|
||||||
|
|
||||||
|
|
||||||
|
@_send_telemetry.register
|
||||||
|
def _(telemetry: ExploitTelem, telemetry_messenger: ITelemetryMessenger, tunnel: MonkeyTunnel):
|
||||||
|
tunnel.set_wait_for_exploited_machines()
|
||||||
|
telemetry_messenger.send_telemetry(telemetry)
|
|
@ -2,7 +2,7 @@ import logging
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
from threading import Thread
|
from threading import Event, Thread
|
||||||
|
|
||||||
from infection_monkey.network.firewall import app as firewall
|
from infection_monkey.network.firewall import app as firewall
|
||||||
from infection_monkey.network.info import get_free_tcp_port, local_ips
|
from infection_monkey.network.info import get_free_tcp_port, local_ips
|
||||||
|
@ -109,18 +109,27 @@ def quit_tunnel(address, timeout=DEFAULT_TIMEOUT):
|
||||||
|
|
||||||
|
|
||||||
class MonkeyTunnel(Thread):
|
class MonkeyTunnel(Thread):
|
||||||
def __init__(self, proxy_class, target_addr=None, target_port=None, timeout=DEFAULT_TIMEOUT):
|
def __init__(
|
||||||
|
self,
|
||||||
|
proxy_class,
|
||||||
|
keep_tunnel_open_time,
|
||||||
|
target_addr=None,
|
||||||
|
target_port=None,
|
||||||
|
timeout=DEFAULT_TIMEOUT,
|
||||||
|
):
|
||||||
self._target_addr = target_addr
|
self._target_addr = target_addr
|
||||||
self._target_port = target_port
|
self._target_port = target_port
|
||||||
self._proxy_class = proxy_class
|
self._proxy_class = proxy_class
|
||||||
|
self._keep_tunnel_open_time = keep_tunnel_open_time
|
||||||
self._broad_sock = None
|
self._broad_sock = None
|
||||||
self._timeout = timeout
|
self._timeout = timeout
|
||||||
self._stopped = False
|
self._stopped = Event()
|
||||||
self._clients = []
|
self._clients = []
|
||||||
self.local_port = None
|
self.local_port = None
|
||||||
super(MonkeyTunnel, self).__init__()
|
super(MonkeyTunnel, self).__init__()
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
self.l_ips = None
|
self.l_ips = None
|
||||||
|
self._wait_for_exploited_machines = Event()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self._broad_sock = _set_multicast_socket(self._timeout)
|
self._broad_sock = _set_multicast_socket(self._timeout)
|
||||||
|
@ -146,7 +155,7 @@ class MonkeyTunnel(Thread):
|
||||||
)
|
)
|
||||||
proxy.start()
|
proxy.start()
|
||||||
|
|
||||||
while not self._stopped:
|
while not self._stopped.is_set():
|
||||||
try:
|
try:
|
||||||
search, address = self._broad_sock.recvfrom(BUFFER_READ)
|
search, address = self._broad_sock.recvfrom(BUFFER_READ)
|
||||||
if b"?" == search:
|
if b"?" == search:
|
||||||
|
@ -195,5 +204,17 @@ class MonkeyTunnel(Thread):
|
||||||
ip_match = get_interface_to_target(ip)
|
ip_match = get_interface_to_target(ip)
|
||||||
return "%s:%d" % (ip_match, self.local_port)
|
return "%s:%d" % (ip_match, self.local_port)
|
||||||
|
|
||||||
|
def set_wait_for_exploited_machines(self):
|
||||||
|
self._wait_for_exploited_machines.set()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._stopped = True
|
self._wait_for_exploited_machine_connection()
|
||||||
|
self._stopped.set()
|
||||||
|
|
||||||
|
def _wait_for_exploited_machine_connection(self):
|
||||||
|
if self._wait_for_exploited_machines.is_set():
|
||||||
|
logger.info(
|
||||||
|
f"Waiting {self._keep_tunnel_open_time} seconds for exploited machines to connect "
|
||||||
|
"to the tunnel."
|
||||||
|
)
|
||||||
|
time.sleep(self._keep_tunnel_open_time)
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from infection_monkey.telemetry.base_telem import BaseTelem
|
||||||
|
from infection_monkey.telemetry.exploit_telem import ExploitTelem
|
||||||
|
from infection_monkey.telemetry.i_telem import ITelem
|
||||||
|
from infection_monkey.telemetry.messengers.exploit_intercepting_telemetry_messenger import (
|
||||||
|
ExploitInterceptingTelemetryMessenger,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestTelem(BaseTelem):
|
||||||
|
telem_category = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class MockExpliotTelem(ExploitTelem):
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_generic_telemetry():
|
||||||
|
mock_telemetry_messenger = MagicMock()
|
||||||
|
mock_tunnel = MagicMock()
|
||||||
|
|
||||||
|
telemetry_messenger = ExploitInterceptingTelemetryMessenger(
|
||||||
|
mock_telemetry_messenger, mock_tunnel
|
||||||
|
)
|
||||||
|
|
||||||
|
telemetry_messenger.send_telemetry(TestTelem())
|
||||||
|
|
||||||
|
assert mock_telemetry_messenger.send_telemetry.called
|
||||||
|
assert not mock_tunnel.set_wait_for_exploited_machines.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_expliot_telemetry():
|
||||||
|
mock_telemetry_messenger = MagicMock()
|
||||||
|
mock_tunnel = MagicMock()
|
||||||
|
mock_expliot_telem = MockExpliotTelem()
|
||||||
|
|
||||||
|
telemetry_messenger = ExploitInterceptingTelemetryMessenger(
|
||||||
|
mock_telemetry_messenger, mock_tunnel
|
||||||
|
)
|
||||||
|
|
||||||
|
telemetry_messenger.send_telemetry(mock_expliot_telem)
|
||||||
|
|
||||||
|
assert mock_telemetry_messenger.send_telemetry.called
|
||||||
|
assert mock_tunnel.set_wait_for_exploited_machines.called
|
Loading…
Reference in New Issue