Merge pull request #2197 from guardicore/2176-remove-credentials-intercepting-telemetry-messenger
2176 remove credentials intercepting telemetry messenger
This commit is contained in:
commit
c55098e186
|
@ -4,6 +4,7 @@ from abc import abstractmethod
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
|
from common.event_queue import IEventQueue
|
||||||
from common.utils.exceptions import FailedExploitationError
|
from common.utils.exceptions import FailedExploitationError
|
||||||
from infection_monkey.i_puppet import ExploiterResultData
|
from infection_monkey.i_puppet import ExploiterResultData
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
|
@ -31,6 +32,7 @@ class HostExploiter:
|
||||||
self.exploit_attempts = []
|
self.exploit_attempts = []
|
||||||
self.host = None
|
self.host = None
|
||||||
self.telemetry_messenger = None
|
self.telemetry_messenger = None
|
||||||
|
self.event_queue = None
|
||||||
self.options = {}
|
self.options = {}
|
||||||
self.exploit_result = {}
|
self.exploit_result = {}
|
||||||
|
|
||||||
|
@ -58,6 +60,7 @@ class HostExploiter:
|
||||||
host,
|
host,
|
||||||
current_depth: int,
|
current_depth: int,
|
||||||
telemetry_messenger: ITelemetryMessenger,
|
telemetry_messenger: ITelemetryMessenger,
|
||||||
|
event_queue: IEventQueue,
|
||||||
agent_repository: IAgentRepository,
|
agent_repository: IAgentRepository,
|
||||||
options: Dict,
|
options: Dict,
|
||||||
interrupt: threading.Event,
|
interrupt: threading.Event,
|
||||||
|
@ -65,6 +68,7 @@ class HostExploiter:
|
||||||
self.host = host
|
self.host = host
|
||||||
self.current_depth = current_depth
|
self.current_depth = current_depth
|
||||||
self.telemetry_messenger = telemetry_messenger
|
self.telemetry_messenger = telemetry_messenger
|
||||||
|
self.event_queue = event_queue
|
||||||
self.agent_repository = agent_repository
|
self.agent_repository = agent_repository
|
||||||
self.options = options
|
self.options = options
|
||||||
self.interrupt = interrupt
|
self.interrupt = interrupt
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import threading
|
import threading
|
||||||
from typing import Dict, Type
|
from typing import Dict, Type
|
||||||
|
|
||||||
|
from common.event_queue import IEventQueue
|
||||||
from infection_monkey.model import VictimHost
|
from infection_monkey.model import VictimHost
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
|
|
||||||
|
@ -21,10 +22,12 @@ class ExploiterWrapper:
|
||||||
self,
|
self,
|
||||||
exploit_class: Type[HostExploiter],
|
exploit_class: Type[HostExploiter],
|
||||||
telemetry_messenger: ITelemetryMessenger,
|
telemetry_messenger: ITelemetryMessenger,
|
||||||
|
event_queue: IEventQueue,
|
||||||
agent_repository: IAgentRepository,
|
agent_repository: IAgentRepository,
|
||||||
):
|
):
|
||||||
self._exploit_class = exploit_class
|
self._exploit_class = exploit_class
|
||||||
self._telemetry_messenger = telemetry_messenger
|
self._telemetry_messenger = telemetry_messenger
|
||||||
|
self._event_queue = event_queue
|
||||||
self._agent_repository = agent_repository
|
self._agent_repository = agent_repository
|
||||||
|
|
||||||
def exploit_host(
|
def exploit_host(
|
||||||
|
@ -35,18 +38,23 @@ class ExploiterWrapper:
|
||||||
host,
|
host,
|
||||||
current_depth,
|
current_depth,
|
||||||
self._telemetry_messenger,
|
self._telemetry_messenger,
|
||||||
|
self._event_queue,
|
||||||
self._agent_repository,
|
self._agent_repository,
|
||||||
options,
|
options,
|
||||||
interrupt,
|
interrupt,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, telemetry_messenger: ITelemetryMessenger, agent_repository: IAgentRepository
|
self,
|
||||||
|
telemetry_messenger: ITelemetryMessenger,
|
||||||
|
event_queue: IEventQueue,
|
||||||
|
agent_repository: IAgentRepository,
|
||||||
):
|
):
|
||||||
self._telemetry_messenger = telemetry_messenger
|
self._telemetry_messenger = telemetry_messenger
|
||||||
|
self._event_queue = event_queue
|
||||||
self._agent_repository = agent_repository
|
self._agent_repository = agent_repository
|
||||||
|
|
||||||
def wrap(self, exploit_class: Type[HostExploiter]):
|
def wrap(self, exploit_class: Type[HostExploiter]):
|
||||||
return ExploiterWrapper.Inner(
|
return ExploiterWrapper.Inner(
|
||||||
exploit_class, self._telemetry_messenger, self._agent_repository
|
exploit_class, self._telemetry_messenger, self._event_queue, self._agent_repository
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import tempfile
|
import tempfile
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Sequence, Tuple
|
||||||
|
|
||||||
import impacket
|
import impacket
|
||||||
from impacket.dcerpc.v5 import epm, nrpc, rpcrt, transport
|
from impacket.dcerpc.v5 import epm, nrpc, rpcrt, transport
|
||||||
|
@ -17,6 +17,7 @@ from impacket.dcerpc.v5.dtypes import NULL
|
||||||
|
|
||||||
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
|
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
|
||||||
from common.credentials import Credentials, LMHash, NTHash, Username
|
from common.credentials import Credentials, LMHash, NTHash, Username
|
||||||
|
from common.events import CredentialsStolenEvent
|
||||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||||
from infection_monkey.exploit.tools.wmi_tools import WmiTools
|
from infection_monkey.exploit.tools.wmi_tools import WmiTools
|
||||||
from infection_monkey.exploit.zerologon_utils.dump_secrets import DumpSecrets
|
from infection_monkey.exploit.zerologon_utils.dump_secrets import DumpSecrets
|
||||||
|
@ -30,6 +31,19 @@ from infection_monkey.utils.threading import interruptible_iter
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ZEROLOGON_EXPLOITER_TAG = "zerologon-exploiter"
|
||||||
|
T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003"
|
||||||
|
T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098"
|
||||||
|
|
||||||
|
|
||||||
|
ZEROLOGON_EVENT_TAGS = frozenset(
|
||||||
|
{
|
||||||
|
ZEROLOGON_EXPLOITER_TAG,
|
||||||
|
T1003_ATTACK_TECHNIQUE_TAG,
|
||||||
|
T1098_ATTACK_TECHNIQUE_TAG,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ZerologonExploiter(HostExploiter):
|
class ZerologonExploiter(HostExploiter):
|
||||||
_EXPLOITED_SERVICE = "Netlogon"
|
_EXPLOITED_SERVICE = "Netlogon"
|
||||||
|
@ -284,14 +298,20 @@ class ZerologonExploiter(HostExploiter):
|
||||||
def send_extracted_creds_as_credential_telemetry(
|
def send_extracted_creds_as_credential_telemetry(
|
||||||
self, user: str, lmhash: str, nthash: str
|
self, user: str, lmhash: str, nthash: str
|
||||||
) -> None:
|
) -> None:
|
||||||
self.telemetry_messenger.send_telemetry(
|
extracted_credentials = [
|
||||||
CredentialsTelem(
|
Credentials(Username(user), LMHash(lmhash)),
|
||||||
[
|
Credentials(Username(user), NTHash(nthash)),
|
||||||
Credentials(Username(user), LMHash(lmhash)),
|
]
|
||||||
Credentials(Username(user), NTHash(nthash)),
|
|
||||||
]
|
self.telemetry_messenger.send_telemetry(CredentialsTelem(extracted_credentials))
|
||||||
)
|
self._publish_credentials_stolen_event(extracted_credentials)
|
||||||
|
|
||||||
|
def _publish_credentials_stolen_event(self, extracted_credentials: Sequence[Credentials]):
|
||||||
|
credentials_stolen_event = CredentialsStolenEvent(
|
||||||
|
tags=ZEROLOGON_EVENT_TAGS,
|
||||||
|
stolen_credentials=extracted_credentials,
|
||||||
)
|
)
|
||||||
|
self.event_queue.publish(credentials_stolen_event)
|
||||||
|
|
||||||
def get_original_pwd_nthash(self, username: str, user_pwd_hashes: List[str]) -> str:
|
def get_original_pwd_nthash(self, username: str, user_pwd_hashes: List[str]) -> str:
|
||||||
if not self.save_HKLM_keys_locally(username, user_pwd_hashes):
|
if not self.save_HKLM_keys_locally(username, user_pwd_hashes):
|
||||||
|
|
|
@ -66,9 +66,6 @@ from infection_monkey.puppet.puppet import Puppet
|
||||||
from infection_monkey.system_singleton import SystemSingleton
|
from infection_monkey.system_singleton import SystemSingleton
|
||||||
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
||||||
from infection_monkey.telemetry.attack.t1107_telem import T1107Telem
|
from infection_monkey.telemetry.attack.t1107_telem import T1107Telem
|
||||||
from infection_monkey.telemetry.messengers.credentials_intercepting_telemetry_messenger import (
|
|
||||||
CredentialsInterceptingTelemetryMessenger,
|
|
||||||
)
|
|
||||||
from infection_monkey.telemetry.messengers.exploit_intercepting_telemetry_messenger import (
|
from infection_monkey.telemetry.messengers.exploit_intercepting_telemetry_messenger import (
|
||||||
ExploitInterceptingTelemetryMessenger,
|
ExploitInterceptingTelemetryMessenger,
|
||||||
)
|
)
|
||||||
|
@ -214,11 +211,8 @@ class InfectionMonkey:
|
||||||
|
|
||||||
victim_host_factory = self._build_victim_host_factory(local_network_interfaces)
|
victim_host_factory = self._build_victim_host_factory(local_network_interfaces)
|
||||||
|
|
||||||
telemetry_messenger = CredentialsInterceptingTelemetryMessenger(
|
telemetry_messenger = ExploitInterceptingTelemetryMessenger(
|
||||||
ExploitInterceptingTelemetryMessenger(
|
self._telemetry_messenger, self._monkey_inbound_tunnel
|
||||||
self._telemetry_messenger, self._monkey_inbound_tunnel
|
|
||||||
),
|
|
||||||
propagation_credentials_repository,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._master = AutomatedMaster(
|
self._master = AutomatedMaster(
|
||||||
|
@ -278,7 +272,7 @@ class InfectionMonkey:
|
||||||
agent_repository = CachingAgentRepository(
|
agent_repository = CachingAgentRepository(
|
||||||
f"https://{self._control_client.server_address}", self._control_client.proxies
|
f"https://{self._control_client.server_address}", self._control_client.proxies
|
||||||
)
|
)
|
||||||
exploit_wrapper = ExploiterWrapper(self._telemetry_messenger, agent_repository)
|
exploit_wrapper = ExploiterWrapper(self._telemetry_messenger, event_queue, agent_repository)
|
||||||
|
|
||||||
puppet.load_plugin(
|
puppet.load_plugin(
|
||||||
"HadoopExploiter", exploit_wrapper.wrap(HadoopExploiter), PluginType.EXPLOITER
|
"HadoopExploiter", exploit_wrapper.wrap(HadoopExploiter), PluginType.EXPLOITER
|
||||||
|
@ -296,13 +290,9 @@ class InfectionMonkey:
|
||||||
"MSSQLExploiter", exploit_wrapper.wrap(MSSQLExploiter), PluginType.EXPLOITER
|
"MSSQLExploiter", exploit_wrapper.wrap(MSSQLExploiter), PluginType.EXPLOITER
|
||||||
)
|
)
|
||||||
|
|
||||||
zerologon_telemetry_messenger = CredentialsInterceptingTelemetryMessenger(
|
|
||||||
self._telemetry_messenger, propagation_credentials_repository
|
|
||||||
)
|
|
||||||
zerologon_wrapper = ExploiterWrapper(zerologon_telemetry_messenger, agent_repository)
|
|
||||||
puppet.load_plugin(
|
puppet.load_plugin(
|
||||||
"ZerologonExploiter",
|
"ZerologonExploiter",
|
||||||
zerologon_wrapper.wrap(ZerologonExploiter),
|
exploit_wrapper.wrap(ZerologonExploiter),
|
||||||
PluginType.EXPLOITER,
|
PluginType.EXPLOITER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
from functools import singledispatch
|
|
||||||
|
|
||||||
from infection_monkey.credential_repository import IPropagationCredentialsRepository
|
|
||||||
from infection_monkey.telemetry.credentials_telem import CredentialsTelem
|
|
||||||
from infection_monkey.telemetry.i_telem import ITelem
|
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
|
||||||
|
|
||||||
|
|
||||||
class CredentialsInterceptingTelemetryMessenger(ITelemetryMessenger):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
telemetry_messenger: ITelemetryMessenger,
|
|
||||||
credentials_store: IPropagationCredentialsRepository,
|
|
||||||
):
|
|
||||||
self._telemetry_messenger = telemetry_messenger
|
|
||||||
self._credentials_store = credentials_store
|
|
||||||
|
|
||||||
def send_telemetry(self, telemetry: ITelem):
|
|
||||||
_send_telemetry(telemetry, self._telemetry_messenger, self._credentials_store)
|
|
||||||
|
|
||||||
|
|
||||||
# Note: We can use @singledispatchmethod instead of @singledispatch if we migrate to Python 3.8 or
|
|
||||||
# later.
|
|
||||||
@singledispatch
|
|
||||||
def _send_telemetry(
|
|
||||||
telemetry: ITelem,
|
|
||||||
telemetry_messenger: ITelemetryMessenger,
|
|
||||||
credentials_store: IPropagationCredentialsRepository,
|
|
||||||
):
|
|
||||||
telemetry_messenger.send_telemetry(telemetry)
|
|
||||||
|
|
||||||
|
|
||||||
@_send_telemetry.register
|
|
||||||
def _(
|
|
||||||
telemetry: CredentialsTelem,
|
|
||||||
telemetry_messenger: ITelemetryMessenger,
|
|
||||||
credentials_store: IPropagationCredentialsRepository,
|
|
||||||
):
|
|
||||||
credentials_store.add_credentials(telemetry.credentials)
|
|
||||||
telemetry_messenger.send_telemetry(telemetry)
|
|
|
@ -36,6 +36,7 @@ def powershell_arguments(http_and_https_both_enabled_host):
|
||||||
"options": options,
|
"options": options,
|
||||||
"current_depth": 2,
|
"current_depth": 2,
|
||||||
"telemetry_messenger": MagicMock(),
|
"telemetry_messenger": MagicMock(),
|
||||||
|
"event_queue": MagicMock(),
|
||||||
"agent_repository": mock_agent_repository,
|
"agent_repository": mock_agent_repository,
|
||||||
"interrupt": threading.Event(),
|
"interrupt": threading.Event(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
from unittest.mock import MagicMock
|
|
||||||
|
|
||||||
from common.credentials import Credentials, Password, SSHKeypair, Username
|
|
||||||
from infection_monkey.telemetry.credentials_telem import CredentialsTelem
|
|
||||||
from infection_monkey.telemetry.messengers.credentials_intercepting_telemetry_messenger import (
|
|
||||||
CredentialsInterceptingTelemetryMessenger,
|
|
||||||
)
|
|
||||||
|
|
||||||
TELEM_CREDENTIALS = [
|
|
||||||
Credentials(
|
|
||||||
Username("user1"),
|
|
||||||
SSHKeypair(public_key="some_public_key", private_key="some_private_key"),
|
|
||||||
),
|
|
||||||
Credentials(Username("root"), Password("password")),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class MockCredentialsTelem(CredentialsTelem):
|
|
||||||
def __init(self, credentials):
|
|
||||||
super().__init__(credentials)
|
|
||||||
|
|
||||||
def get_data(self):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def test_credentials_generic_telemetry(TestTelem):
|
|
||||||
mock_telemetry_messenger = MagicMock()
|
|
||||||
mock_credentials_repository = MagicMock()
|
|
||||||
|
|
||||||
telemetry_messenger = CredentialsInterceptingTelemetryMessenger(
|
|
||||||
mock_telemetry_messenger, mock_credentials_repository
|
|
||||||
)
|
|
||||||
|
|
||||||
telemetry_messenger.send_telemetry(TestTelem())
|
|
||||||
|
|
||||||
assert mock_telemetry_messenger.send_telemetry.called
|
|
||||||
assert not mock_credentials_repository.add_credentials.called
|
|
||||||
|
|
||||||
|
|
||||||
def test_successful_intercepting_credentials_telemetry():
|
|
||||||
mock_telemetry_messenger = MagicMock()
|
|
||||||
mock_credentials_repository = MagicMock()
|
|
||||||
mock_empty_credentials_telem = MockCredentialsTelem(TELEM_CREDENTIALS)
|
|
||||||
|
|
||||||
telemetry_messenger = CredentialsInterceptingTelemetryMessenger(
|
|
||||||
mock_telemetry_messenger, mock_credentials_repository
|
|
||||||
)
|
|
||||||
|
|
||||||
telemetry_messenger.send_telemetry(mock_empty_credentials_telem)
|
|
||||||
|
|
||||||
assert mock_telemetry_messenger.send_telemetry.called
|
|
||||||
assert mock_credentials_repository.add_credentials.called
|
|
Loading…
Reference in New Issue