From 4497ea00037889a432907ee66d94d6b7d6d13430 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 5 Apr 2022 14:46:10 +0300 Subject: [PATCH 1/8] Agent, Island: Decrease keep_tunnel_open_time to 30 seconds --- monkey/infection_monkey/config.py | 2 +- monkey/monkey_island/cc/services/config_schema/internal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 7b8d793cf..1f8c46311 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -87,7 +87,7 @@ class Configuration(object): # Configuration servers to try to connect to, in this order. command_servers = [] - keep_tunnel_open_time = 60 + keep_tunnel_open_time = 30 ########################### # testing configuration diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py index e825a9098..86a0089ec 100644 --- a/monkey/monkey_island/cc/services/config_schema/internal.py +++ b/monkey/monkey_island/cc/services/config_schema/internal.py @@ -9,7 +9,7 @@ INTERNAL = { "keep_tunnel_open_time": { "title": "Keep tunnel open time", "type": "integer", - "default": 60, + "default": 30, "description": "Time to keep tunnel open before going down after last exploit " "(in seconds)", }, From e40703dcac1f29ca880eae154d434256bd994243 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 5 Apr 2022 14:52:35 +0300 Subject: [PATCH 2/8] Agent: Add timeouts and improve firewall rule handling code --- monkey/infection_monkey/network/firewall.py | 38 ++++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/monkey/infection_monkey/network/firewall.py b/monkey/infection_monkey/network/firewall.py index 0851a575f..5c493b82f 100644 --- a/monkey/infection_monkey/network/firewall.py +++ b/monkey/infection_monkey/network/firewall.py @@ -1,18 +1,23 @@ +import logging import platform import subprocess import sys +from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT + +logger = logging.getLogger(__name__) + def _run_netsh_cmd(command, args): - cmd = subprocess.Popen( + output = subprocess.check_output( "netsh %s %s" % ( command, " ".join(['%s="%s"' % (key, value) for key, value in list(args.items()) if value]), ), - stdout=subprocess.PIPE, + timeout=SHORT_REQUEST_TIMEOUT, ) - return cmd.stdout.read().strip().lower().endswith("ok.") + return output.strip().lower().endswith("ok.") class FirewallApp(object): @@ -44,17 +49,21 @@ class WinAdvFirewall(FirewallApp): def is_enabled(self): try: - cmd = subprocess.Popen("netsh advfirewall show currentprofile", stdout=subprocess.PIPE) - out = cmd.stdout.readlines() - - for line in out: - if line.startswith("State"): - state = line.split()[-1].strip() - - return state == "ON" + out = subprocess.check_output( + "netsh advfirewall show currentprofile", timeout=SHORT_REQUEST_TIMEOUT + ) + except subprocess.TimeoutExpired: + return None except Exception: return None + for line in out.decode().splitlines(): + if line.startswith("State"): + state = line.split()[-1].strip() + return state == "ON" + + return None + def add_firewall_rule( self, name="Firewall", direction="in", action="allow", program=sys.executable, **kwargs ): @@ -66,8 +75,11 @@ class WinAdvFirewall(FirewallApp): return True else: return False - except Exception: - return None + except subprocess.CalledProcessError as err: + logger.info(f"Failed adding a firewall rule: {err.stdout}") + except subprocess.TimeoutExpired: + logger.info("Timeout expired trying to add a firewall rule.") + return None def remove_firewall_rule(self, name="Firewall", **kwargs): netsh_args = {"name": name} From 64f2d598cf891e1335df52da366340d12c54d7d3 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 5 Apr 2022 15:30:38 +0300 Subject: [PATCH 3/8] Agent: Add agent deletion check This check will avoid duplicate deletion calls in case an exception happens --- monkey/infection_monkey/monkey.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 96f1873bc..922f315a1 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -343,6 +343,7 @@ class InfectionMonkey: def cleanup(self): logger.info("Monkey cleanup started") + deleted = None try: if self._master: self._master.cleanup() @@ -357,7 +358,7 @@ class InfectionMonkey: firewall.remove_firewall_rule() firewall.close() - InfectionMonkey._self_delete() + deleted = InfectionMonkey._self_delete() InfectionMonkey._send_log() @@ -371,7 +372,8 @@ class InfectionMonkey: self._singleton.unlock() except Exception as e: logger.error(f"An error occurred while cleaning up the monkey agent: {e}") - InfectionMonkey._self_delete() + if deleted is None: + InfectionMonkey._self_delete() logger.info("Monkey is shutting down") @@ -400,9 +402,10 @@ class InfectionMonkey: ControlClient.send_log(log) @staticmethod - def _self_delete(): + def _self_delete() -> bool: status = ScanStatus.USED if remove_monkey_dir() else ScanStatus.SCANNED T1107Telem(status, get_monkey_dir_path()).send() + deleted = False if -1 == sys.executable.find("python"): try: @@ -421,11 +424,14 @@ class InfectionMonkey: close_fds=True, startupinfo=startupinfo, ) + deleted = True else: os.remove(sys.executable) status = ScanStatus.USED + deleted = True except Exception as exc: logger.error("Exception in self delete: %s", exc) status = ScanStatus.SCANNED if status: T1107Telem(status, sys.executable).send() + return deleted From fe0b408c062112ce68296bd0ab873fdd63de397f Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Tue, 5 Apr 2022 13:15:06 +0000 Subject: [PATCH 4/8] Agent: Fix a bug with output checking in firewall.py --- monkey/infection_monkey/network/firewall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/network/firewall.py b/monkey/infection_monkey/network/firewall.py index 5c493b82f..7a9f83827 100644 --- a/monkey/infection_monkey/network/firewall.py +++ b/monkey/infection_monkey/network/firewall.py @@ -17,7 +17,7 @@ def _run_netsh_cmd(command, args): ), timeout=SHORT_REQUEST_TIMEOUT, ) - return output.strip().lower().endswith("ok.") + return output.strip().lower().endswith(b"ok.") class FirewallApp(object): From 0b1b829cd76a5ba82caee6261cdd2fe925d1dc08 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Tue, 5 Apr 2022 13:15:36 +0000 Subject: [PATCH 5/8] Agent: Change firewall rule name to be more indicative --- monkey/infection_monkey/network/firewall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/network/firewall.py b/monkey/infection_monkey/network/firewall.py index 7a9f83827..e3edcff66 100644 --- a/monkey/infection_monkey/network/firewall.py +++ b/monkey/infection_monkey/network/firewall.py @@ -65,7 +65,7 @@ class WinAdvFirewall(FirewallApp): return None def add_firewall_rule( - self, name="Firewall", direction="in", action="allow", program=sys.executable, **kwargs + self, name="MonkeyRule", direction="in", action="allow", program=sys.executable, **kwargs ): netsh_args = {"name": name, "dir": direction, "action": action, "program": program} netsh_args.update(kwargs) From 7fc49196d7e14358494a493c8c6198e0cdce6d53 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Tue, 5 Apr 2022 14:03:26 +0000 Subject: [PATCH 6/8] Agent: Extract proxy timeout to const, change it to 2.5 --- monkey/infection_monkey/transport/base.py | 1 + monkey/infection_monkey/transport/http.py | 8 ++++++-- monkey/infection_monkey/transport/tcp.py | 12 ++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/transport/base.py b/monkey/infection_monkey/transport/base.py index 77be3f3af..f61f7b115 100644 --- a/monkey/infection_monkey/transport/base.py +++ b/monkey/infection_monkey/transport/base.py @@ -2,6 +2,7 @@ import time from threading import Thread g_last_served = None +PROXY_TIMEOUT = 2.5 class TransportProxyBase(Thread): diff --git a/monkey/infection_monkey/transport/http.py b/monkey/infection_monkey/transport/http.py index 49272481f..63aaa0b36 100644 --- a/monkey/infection_monkey/transport/http.py +++ b/monkey/infection_monkey/transport/http.py @@ -7,7 +7,11 @@ from logging import getLogger from urllib.parse import urlsplit from infection_monkey.network.tools import get_interface_to_target -from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time +from infection_monkey.transport.base import ( + PROXY_TIMEOUT, + TransportProxyBase, + update_last_serve_time, +) logger = getLogger(__name__) @@ -227,6 +231,6 @@ class LockedHTTPServer(threading.Thread): class HTTPConnectProxy(TransportProxyBase): def run(self): httpd = http.server.HTTPServer((self.local_host, self.local_port), HTTPConnectProxyHandler) - httpd.timeout = 10 + httpd.timeout = PROXY_TIMEOUT while not self._stopped: httpd.handle_request() diff --git a/monkey/infection_monkey/transport/tcp.py b/monkey/infection_monkey/transport/tcp.py index 500dc2a22..83c631c3b 100644 --- a/monkey/infection_monkey/transport/tcp.py +++ b/monkey/infection_monkey/transport/tcp.py @@ -3,16 +3,20 @@ import socket from logging import getLogger from threading import Thread -from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time +from infection_monkey.transport.base import ( + PROXY_TIMEOUT, + TransportProxyBase, + update_last_serve_time, +) READ_BUFFER_SIZE = 8192 -DEFAULT_TIMEOUT = 10 +SOCKET_READ_TIMEOUT = 10 logger = getLogger(__name__) class SocketsPipe(Thread): - def __init__(self, source, dest, timeout=DEFAULT_TIMEOUT): + def __init__(self, source, dest, timeout=SOCKET_READ_TIMEOUT): Thread.__init__(self) self.source = source self.dest = dest @@ -51,7 +55,7 @@ class TcpProxy(TransportProxyBase): pipes = [] l_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) l_socket.bind((self.local_host, self.local_port)) - l_socket.settimeout(DEFAULT_TIMEOUT) + l_socket.settimeout(PROXY_TIMEOUT) l_socket.listen(5) while not self._stopped: From 6ef365d3e58a7d6b78fd8cdaec6d1bd049556698 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Wed, 6 Apr 2022 07:09:10 +0000 Subject: [PATCH 7/8] Agent: Improve ssh exception handling in sshexec.py --- monkey/infection_monkey/exploit/sshexec.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index dab29ae03..2b13b719e 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -86,13 +86,15 @@ class SSHExploiter(HostExploiter): self.exploit_result.exploitation_success = True self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh - except Exception: + except paramiko.AuthenticationException as err: ssh.close() - logger.debug( - "Error logging into victim %r with %s" " private key", self.host, ssh_string + logger.info( + f"Failed logging into victim {self.host} with {ssh_string} private key: {err}", ) self.report_login_attempt(False, user, ssh_key=ssh_string) continue + except Exception as err: + logger.error(f"Unknown error while attempting to login with ssh key: {err}") raise FailedExploitationError def exploit_with_login_creds(self, port) -> paramiko.SSHClient: @@ -130,16 +132,18 @@ class SSHExploiter(HostExploiter): self.report_login_attempt(True, user, current_password) return ssh - except Exception as exc: + except paramiko.AuthenticationException as err: logger.debug( - "Error logging into victim %r with user" " %s: (%s)", + "Failed logging into victim %r with user" " %s: (%s)", self.host, user, - exc, + err, ) self.report_login_attempt(False, user, current_password) ssh.close() continue + except Exception as err: + logger.error(f"Unknown error occurred while trying to login to ssh: {err}") raise FailedExploitationError def _exploit_host(self) -> ExploiterResultData: From 93fd31b0536eb32f4aa020af9bee2ec3a0bdd15f Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Wed, 6 Apr 2022 11:18:48 +0000 Subject: [PATCH 8/8] Agent: Remove TODO for determining time between telem and exit Time taken is upto 10s for tunnel close and potentially up to 15 to send T1107Telem, so maximum up to 25 seconds --- monkey/infection_monkey/monkey.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 922f315a1..e3d71a2f1 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -366,8 +366,6 @@ class InfectionMonkey: is_done=True, version=get_version() ).send() # Signal the server (before closing the tunnel) - # TODO: Determine how long between when we - # send telemetry and the monkey actually exits InfectionMonkey._close_tunnel() self._singleton.unlock() except Exception as e: