forked from p34709852/monkey
commit
82217b4094
|
@ -10,6 +10,11 @@ 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
|
||||||
|
@ -47,6 +52,5 @@ class InfectionMonkeyBaseModel(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class MutableInfectionMonkeyBaseModel(InfectionMonkeyBaseModel):
|
class MutableInfectionMonkeyBaseModel(InfectionMonkeyBaseModel):
|
||||||
class Config(InfectionMonkeyModelConfig):
|
class Config(MutableInfectionMonkeyModelConfig):
|
||||||
allow_mutation = True
|
pass
|
||||||
validate_assignment = True
|
|
||||||
|
|
|
@ -28,6 +28,17 @@ 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.
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from common.types import PingScanData
|
from common.types import NetworkPort, 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[Port, PortScanData]
|
port_scan_data: Dict[NetworkPort, PortScanData]
|
||||||
fingerprint_data: Dict[FingerprinterName, FingerprintData]
|
fingerprint_data: Dict[FingerprinterName, FingerprintData]
|
||||||
|
|
|
@ -10,7 +10,7 @@ from common.agent_configuration import (
|
||||||
PropagationConfiguration,
|
PropagationConfiguration,
|
||||||
ScanTargetConfiguration,
|
ScanTargetConfiguration,
|
||||||
)
|
)
|
||||||
from common.types import PingScanData, PortStatus
|
from common.types import NetworkPort, 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, Port
|
from .ip_scan_results import FingerprinterName
|
||||||
|
|
||||||
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[Port, PortScanData]
|
victim_host: VictimHost, port_scan_data: Mapping[NetworkPort, 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()
|
||||||
|
|
|
@ -1,19 +1,45 @@
|
||||||
|
import json
|
||||||
from ipaddress import IPv4Interface
|
from ipaddress import IPv4Interface
|
||||||
from typing import Optional, Sequence
|
from typing import Any, Dict, Mapping, 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
|
from common.base_models import MutableInfectionMonkeyBaseModel, MutableInfectionMonkeyModelConfig
|
||||||
from common.transforms import make_immutable_sequence
|
from common.transforms import make_immutable_sequence
|
||||||
from common.types import HardwareID
|
from common.types import HardwareID, NetworkService, SocketAddress
|
||||||
|
|
||||||
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"""
|
||||||
|
|
||||||
|
@ -35,6 +61,9 @@ 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
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from typing import FrozenSet, Mapping
|
from typing import FrozenSet, Mapping, Tuple
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -24,3 +25,6 @@ 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: Mapping[MachineID, Tuple[SocketAddress, ...]] = {}
|
||||||
|
"""All successfull outbound TCP connections"""
|
||||||
|
|
|
@ -6,8 +6,12 @@ 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,
|
||||||
|
@ -17,6 +21,10 @@ 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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,9 +34,13 @@ 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": "windows",
|
"operating_system": OperatingSystem.WINDOWS.value,
|
||||||
"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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,6 +72,11 @@ 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):
|
||||||
|
@ -77,6 +94,7 @@ 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):
|
||||||
|
@ -230,3 +248,19 @@ 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 == {}
|
||||||
|
|
|
@ -2,6 +2,7 @@ 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
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,13 +12,21 @@ 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=1,
|
machine_id=machine_id,
|
||||||
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():
|
||||||
|
@ -27,9 +36,12 @@ 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)
|
||||||
|
@ -44,6 +56,8 @@ 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={})
|
||||||
|
|
|
@ -9,14 +9,13 @@ 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.types import NetworkPort
|
|
||||||
from infection_monkey.exploit.HostExploiter.HostExploiter import (
|
from infection_monkey.exploit.HostExploiter.HostExploiter import (
|
||||||
_publish_exploitation_event,
|
_publish_exploitation_event,
|
||||||
_publish_propagation_event,
|
_publish_propagation_event,
|
||||||
)
|
)
|
||||||
|
from common.types import NetworkPort, NetworkService
|
||||||
from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory
|
from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory
|
||||||
from monkey_island.cc.event_queue import IslandEventTopic, PyPubSubIslandEventQueue
|
from monkey_island.cc.models import Machine, Node, Report
|
||||||
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
|
||||||
|
@ -340,3 +339,10 @@ 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
|
||||||
|
|
Loading…
Reference in New Issue