Merge branch '2269-publish-events-from-powershell-exploiter' into develop

PR #2402
This commit is contained in:
Mike Salvatore 2022-10-06 12:45:13 -04:00
commit b2c5b22128
4 changed files with 68 additions and 12 deletions

View File

@ -2,6 +2,7 @@ from .attack import (
T1003_ATTACK_TECHNIQUE_TAG, T1003_ATTACK_TECHNIQUE_TAG,
T1005_ATTACK_TECHNIQUE_TAG, T1005_ATTACK_TECHNIQUE_TAG,
T1021_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG,
T1059_ATTACK_TECHNIQUE_TAG,
T1098_ATTACK_TECHNIQUE_TAG, T1098_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG,
T1110_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG,

View File

@ -1,6 +1,7 @@
T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003"
T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005" T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005"
T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021"
T1059_ATTACK_TECHNIQUE_TAG = "attack-t1059"
T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098" T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098"
T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105"
T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110"

View File

@ -1,8 +1,14 @@
import logging import logging
from pathlib import Path, PurePath from pathlib import Path, PurePath
from time import time
from typing import List, Optional from typing import List, Optional
from common import OperatingSystem from common import OperatingSystem
from common.tags import (
T1059_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG,
T1110_ATTACK_TECHNIQUE_TAG,
)
from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions, get_auth_options from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions, get_auth_options
from infection_monkey.exploit.powershell_utils.credentials import ( from infection_monkey.exploit.powershell_utils.credentials import (
@ -21,6 +27,7 @@ from infection_monkey.utils.environment import is_windows_os
from infection_monkey.utils.threading import interruptible_iter from infection_monkey.utils.threading import interruptible_iter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
POWERSHELL_EXPLOITER_TAG = "powershell-exploiter"
class RemoteAgentCopyError(Exception): class RemoteAgentCopyError(Exception):
@ -34,6 +41,17 @@ class RemoteAgentExecutionError(Exception):
class PowerShellExploiter(HostExploiter): class PowerShellExploiter(HostExploiter):
_EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)"
_EXPLOITER_TAGS = (
POWERSHELL_EXPLOITER_TAG,
T1059_ATTACK_TECHNIQUE_TAG,
T1110_ATTACK_TECHNIQUE_TAG,
)
_PROPAGATION_TAGS = (
POWERSHELL_EXPLOITER_TAG,
T1059_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG,
)
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._client = None self._client = None
@ -68,12 +86,21 @@ class PowerShellExploiter(HostExploiter):
) )
return self.exploit_result return self.exploit_result
execute_agent_timestamp = time()
try: try:
self._execute_monkey_agent_on_victim() self._execute_monkey_agent_on_victim()
except Exception as err:
self.exploit_result.error_message = f"Failed to propagate to the remote host: {err}"
self._publish_propagation_event(
time=execute_agent_timestamp,
success=False,
error_message=self.exploit_result.error_message,
)
logger.error(self.exploit_result.error_message)
return self.exploit_result
self.exploit_result.propagation_success = True self.exploit_result.propagation_success = True
except Exception as ex: self._publish_propagation_event(time=execute_agent_timestamp, success=True)
logger.error(f"Failed to propagate to the remote host: {ex}")
self.exploit_result.error_message = str(ex)
return self.exploit_result return self.exploit_result
@ -94,21 +121,27 @@ class PowerShellExploiter(HostExploiter):
try: try:
client = PowerShellClient(self.host.ip_addr, creds, opts) client = PowerShellClient(self.host.ip_addr, creds, opts)
connect_timestamp = time()
client.connect() client.connect()
logger.info( logger.info(
f"Successfully logged into {self.host.ip_addr} using Powershell. User: " f"Successfully logged into {self.host.ip_addr} using Powershell. User: "
f"{creds.username}, Secret Type: {creds.secret_type.name}" f"{creds.username}, Secret Type: {creds.secret_type.name}"
) )
self._publish_exploitation_event(time=connect_timestamp, success=True)
self.exploit_result.exploitation_success = True self.exploit_result.exploitation_success = True
self._report_login_attempt(True, creds) self._report_login_attempt(True, creds)
return client return client
except Exception as ex: except Exception as ex:
logger.debug( error_message = (
f"Error logging into {self.host.ip_addr} using Powershell. User: " f"Error logging into {self.host.ip_addr} using Powershell. User: "
f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}" f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}"
) )
logger.debug(error_message)
self._publish_exploitation_event(
time=connect_timestamp, success=False, error_message=error_message
)
self._report_login_attempt(False, creds) self._report_login_attempt(False, creds)
return None return None

View File

@ -16,6 +16,7 @@ LM_HASH_LIST = ["bogo_lm_1"]
NT_HASH_LIST = ["bogo_nt_1", "bogo_nt_2"] NT_HASH_LIST = ["bogo_nt_1", "bogo_nt_2"]
bogus_servers = ["1.1.1.1:5000", "2.2.2.2:5007"] bogus_servers = ["1.1.1.1:5000", "2.2.2.2:5007"]
VICTIM_IP = "10.10.10.1"
mock_agent_binary_repository = MagicMock() mock_agent_binary_repository = MagicMock()
@ -23,7 +24,25 @@ mock_agent_binary_repository.get_agent_binary.return_value = BytesIO(b"BINARY_EX
@pytest.fixture @pytest.fixture
def powershell_arguments(http_and_https_both_enabled_host): def host_with_ip_address(http_and_https_both_enabled_host):
http_and_https_both_enabled_host.ip_addr = VICTIM_IP
return http_and_https_both_enabled_host
@pytest.fixture
def http_host_with_ip_address(http_only_host):
http_only_host.ip_addr = VICTIM_IP
return http_only_host
@pytest.fixture
def https_host_with_ip_address(https_only_host):
https_only_host.ip_addr = VICTIM_IP
return https_only_host
@pytest.fixture
def powershell_arguments(host_with_ip_address):
options = { options = {
"credentials": { "credentials": {
"exploit_user_list": USER_LIST, "exploit_user_list": USER_LIST,
@ -33,7 +52,7 @@ def powershell_arguments(http_and_https_both_enabled_host):
}, },
} }
arguments = { arguments = {
"host": http_and_https_both_enabled_host, "host": host_with_ip_address,
"servers": bogus_servers, "servers": bogus_servers,
"options": options, "options": options,
"current_depth": 2, "current_depth": 2,
@ -63,8 +82,10 @@ def test_powershell_disabled(powershell_exploiter, powershell_arguments, powersh
assert "disabled" in exploit_result.error_message assert "disabled" in exploit_result.error_message
def test_powershell_http(monkeypatch, powershell_exploiter, powershell_arguments, http_only_host): def test_powershell_http(
powershell_arguments["host"] = http_only_host monkeypatch, powershell_exploiter, powershell_arguments, http_host_with_ip_address
):
powershell_arguments["host"] = http_host_with_ip_address
mock_powershell_client = MagicMock() mock_powershell_client = MagicMock()
monkeypatch.setattr( monkeypatch.setattr(
@ -77,7 +98,7 @@ def test_powershell_http(monkeypatch, powershell_exploiter, powershell_arguments
assert not call_args[0][2].ssl assert not call_args[0][2].ssl
def test_powershell_https(monkeypatch, powershell_exploiter, powershell_arguments, https_only_host): def test_powershell_https(monkeypatch, powershell_exploiter, powershell_arguments):
mock_powershell_client = MagicMock() mock_powershell_client = MagicMock()
mock_powershell_client.connect = MagicMock(side_effect=Exception("Failed login")) mock_powershell_client.connect = MagicMock(side_effect=Exception("Failed login"))
mock_powershell_client_constructor = MagicMock(return_value=mock_powershell_client) mock_powershell_client_constructor = MagicMock(return_value=mock_powershell_client)
@ -191,11 +212,11 @@ def test_build_monkey_execution_command():
def test_skip_http_only_logins( def test_skip_http_only_logins(
monkeypatch, powershell_exploiter, powershell_arguments, https_only_host monkeypatch, powershell_exploiter, powershell_arguments, https_host_with_ip_address
): ):
# Only HTTPS is enabled on the destination, so we should never try to connect with "" empty # Only HTTPS is enabled on the destination, so we should never try to connect with "" empty
# password, since connection with empty password requires SSL == False. # password, since connection with empty password requires SSL == False.
powershell_arguments["host"] = https_only_host powershell_arguments["host"] = https_host_with_ip_address
mock_powershell_client = MagicMock() mock_powershell_client = MagicMock()
mock_powershell_client.connect = MagicMock(side_effect=Exception("Failed login")) mock_powershell_client.connect = MagicMock(side_effect=Exception("Failed login"))