Merge pull request #2355 from guardicore/2268-pingscanevent

2268 pingscanevent
This commit is contained in:
ilija-lazoroski 2022-09-27 10:58:20 +02:00 committed by GitHub
commit 90890106f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 116 additions and 90 deletions

View File

@ -1,2 +1,3 @@
from .abstract_agent_event import AbstractAgentEvent
from .credentials_stolen_events import CredentialsStolenEvent
from .ping_scan_event import PingScanEvent

View File

@ -0,0 +1,14 @@
from common.types import PingScanData
from . import AbstractAgentEvent
class PingScanEvent(AbstractAgentEvent):
"""
An event that occurs when the agent performs a ping scan on its network
Attributes:
:param scan_data: The data collected from the ping scan
"""
scan_data: PingScanData

View File

@ -1,11 +1,14 @@
from __future__ import annotations
from dataclasses import dataclass
from ipaddress import IPv4Address
from typing import Optional
from uuid import UUID
from pydantic import PositiveInt, conint
from typing_extensions import TypeAlias
from common import OperatingSystem
from common.base_models import InfectionMonkeyBaseModel
from common.network.network_utils import address_to_ip_port
@ -14,6 +17,12 @@ HardwareID: TypeAlias = PositiveInt
MachineID: TypeAlias = PositiveInt
@dataclass
class PingScanData:
response_received: bool
os: Optional[OperatingSystem]
class SocketAddress(InfectionMonkeyBaseModel):
ip: IPv4Address
port: conint(ge=1, le=65535) # type: ignore[valid-type]

View File

@ -2,7 +2,6 @@ from .plugin_type import PluginType
from .i_puppet import (
IPuppet,
ExploiterResultData,
PingScanData,
PortScanData,
FingerprintData,
PortStatus,

View File

@ -1,7 +1,9 @@
from abc import abstractmethod
from typing import Dict
from . import FingerprintData, PingScanData, PortScanData
from common.types import PingScanData
from . import FingerprintData, PortScanData
class IFingerprinter:

View File

@ -6,6 +6,7 @@ from enum import Enum
from typing import Dict, Iterable, Mapping, Optional, Sequence
from common.credentials import Credentials
from common.types import PingScanData
from infection_monkey.model import VictimHost
from . import PluginType
@ -31,7 +32,6 @@ class ExploiterResultData:
error_message: str = ""
PingScanData = namedtuple("PingScanData", ["response_received", "os"])
PortScanData = namedtuple("PortScanData", ["port", "status", "banner", "service"])
FingerprintData = namedtuple("FingerprintData", ["os_type", "os_version", "services"])
PostBreachData = namedtuple("PostBreachData", ["display_name", "command", "result"])

View File

@ -1,7 +1,8 @@
from dataclasses import dataclass
from typing import Dict
from infection_monkey.i_puppet import FingerprintData, PingScanData, PortScanData
from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, PortScanData
Port = int
FingerprinterName = str

View File

@ -9,13 +9,8 @@ from common.agent_configuration.agent_sub_configurations import (
NetworkScanConfiguration,
PluginConfiguration,
)
from infection_monkey.i_puppet import (
FingerprintData,
IPuppet,
PingScanData,
PortScanData,
PortStatus,
)
from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, IPuppet, PortScanData, PortStatus
from infection_monkey.network import NetworkAddress
from infection_monkey.utils.threading import interruptible_iter, run_worker_threads

View File

@ -10,13 +10,8 @@ from common.agent_configuration import (
PropagationConfiguration,
ScanTargetConfiguration,
)
from infection_monkey.i_puppet import (
ExploiterResultData,
FingerprintData,
PingScanData,
PortScanData,
PortStatus,
)
from common.types import PingScanData
from infection_monkey.i_puppet import ExploiterResultData, FingerprintData, PortScanData, PortStatus
from infection_monkey.model import VictimHost, VictimHostFactory
from infection_monkey.network import NetworkAddress
from infection_monkey.network_scanning.scan_target_generator import compile_scan_target_list

View File

@ -5,13 +5,8 @@ from typing import Any, Dict
import requests
from common.common_consts.network_consts import ES_SERVICE
from infection_monkey.i_puppet import (
FingerprintData,
IFingerprinter,
PingScanData,
PortScanData,
PortStatus,
)
from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, IFingerprinter, PortScanData, PortStatus
DISPLAY_NAME = "ElasticSearch"
ES_PORT = 9200

View File

@ -5,13 +5,8 @@ from typing import Any, Dict, Iterable, Optional, Set, Tuple
from requests import head
from requests.exceptions import ConnectionError, Timeout
from infection_monkey.i_puppet import (
FingerprintData,
IFingerprinter,
PingScanData,
PortScanData,
PortStatus,
)
from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, IFingerprinter, PortScanData, PortStatus
logger = logging.getLogger(__name__)

View File

@ -3,7 +3,8 @@ import logging
import socket
from typing import Any, Dict, Optional
from infection_monkey.i_puppet import FingerprintData, IFingerprinter, PingScanData, PortScanData
from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, IFingerprinter, PortScanData
MSSQL_SERVICE = "MSSQL"
DISPLAY_NAME = MSSQL_SERVICE

View File

@ -6,7 +6,7 @@ import subprocess
import sys
from common import OperatingSystem
from infection_monkey.i_puppet import PingScanData
from common.types import PingScanData
from infection_monkey.utils.environment import is_windows_os
TTL_REGEX = re.compile(r"TTL=([0-9]+)\b", re.IGNORECASE)
@ -78,11 +78,8 @@ def _process_ping_command_output(ping_command_output: str) -> PingScanData:
# match at all if the group isn't found or the contents of the group are not only digits.
ttl = int(ttl_match.group(1))
operating_system = None
if ttl <= LINUX_TTL:
operating_system = OperatingSystem.LINUX
else: # as far we we know, could also be OSX/BSD, but lets handle that when it comes up.
operating_system = OperatingSystem.WINDOWS
# could also be OSX/BSD, but lets handle that when it comes up.
operating_system = OperatingSystem.LINUX if ttl <= LINUX_TTL else OperatingSystem.WINDOWS
return PingScanData(True, operating_system)

View File

@ -6,13 +6,8 @@ from typing import Dict
from odict import odict
from common import OperatingSystem
from infection_monkey.i_puppet import (
FingerprintData,
IFingerprinter,
PingScanData,
PortScanData,
PortStatus,
)
from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, IFingerprinter, PortScanData, PortStatus
DISPLAY_NAME = "SMB"
SMB_PORT = 445

View File

@ -2,7 +2,8 @@ import re
from typing import Dict, Optional, Tuple
from common import OperatingSystem
from infection_monkey.i_puppet import FingerprintData, IFingerprinter, PingScanData, PortScanData
from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, IFingerprinter, PortScanData
SSH_REGEX = r"SSH-\d\.\d-OpenSSH"
LINUX_DIST_SSH = ["ubuntu", "debian"]

View File

@ -4,12 +4,12 @@ from typing import Dict, Iterable, Sequence
from common.common_consts.timeouts import CONNECTION_TIMEOUT
from common.credentials import Credentials
from common.types import PingScanData
from infection_monkey import network_scanning
from infection_monkey.i_puppet import (
ExploiterResultData,
FingerprintData,
IPuppet,
PingScanData,
PluginType,
PortScanData,
PostBreachData,

View File

@ -1,14 +1,14 @@
import logging
import threading
from typing import Dict, Iterable, List, Sequence
from typing import Dict, Iterable, Sequence
from common import OperatingSystem
from common.credentials import Credentials, LMHash, Password, SSHKeypair, Username
from common.types import PingScanData
from infection_monkey.i_puppet import (
ExploiterResultData,
FingerprintData,
IPuppet,
PingScanData,
PluginType,
PortScanData,
PortStatus,
@ -25,26 +25,34 @@ logger = logging.getLogger()
class MockPuppet(IPuppet):
def load_plugin(self, plugin: object, plugin_type: PluginType) -> None:
def load_plugin(self, plugin_name: str, plugin: object, plugin_type: PluginType) -> None:
logger.debug(f"load_plugin({plugin}, {plugin_type})")
def run_credential_collector(self, name: str, options: Dict) -> Sequence[Credentials]:
logger.debug(f"run_credential_collector({name})")
if name == "SSHCollector":
ssh_credentials = Credentials(
[Username("m0nk3y")],
[
SSHKeypair("Public_Key_0", "Private_Key_0"),
SSHKeypair("Public_Key_1", "Private_Key_1"),
],
)
return [ssh_credentials]
ssh_credentials = [
Credentials(
identity=Username(username="m0nk3y"),
secret=SSHKeypair(private_key="Public_Key_0", public_key="Private_Key_0"),
),
Credentials(
identity=Username(username="m0nk3y"),
secret=SSHKeypair(private_key="Public_Key_1", public_key="Private_Key_1"),
),
]
return ssh_credentials
elif name == "MimikatzCollector":
windows_credentials = Credentials(
[Username("test_user")], [Password("1234"), LMHash("DEADBEEF")]
)
return [windows_credentials]
windows_credentials = [
Credentials(
identity=Username(username="test_user"), secret=Password(password="1234")
),
Credentials(
identity=Username(username="test_user"), secret=LMHash(lm_hash="DEADBEEF")
),
]
return windows_credentials
return []
@ -59,13 +67,13 @@ class MockPuppet(IPuppet):
def ping(self, host: str, timeout: float = 1) -> PingScanData:
logger.debug(f"run_ping({host}, {timeout})")
if host == DOT_1:
return PingScanData(True, "windows")
return PingScanData(True, OperatingSystem.WINDOWS)
if host == DOT_2:
return PingScanData(False, None)
if host == DOT_3:
return PingScanData(True, "linux")
return PingScanData(True, OperatingSystem.LINUX)
if host == DOT_4:
return PingScanData(False, None)
@ -73,7 +81,7 @@ class MockPuppet(IPuppet):
return PingScanData(False, None)
def scan_tcp_ports(
self, host: str, ports: List[int], timeout: float = 3
self, host: str, ports: Sequence[int], timeout: float = 3
) -> Dict[int, PortScanData]:
logger.debug(f"run_scan_tcp_port({host}, {ports}, {timeout})")
dot_1_results = {
@ -139,6 +147,7 @@ class MockPuppet(IPuppet):
name: str,
host: VictimHost,
current_depth: int,
servers: Sequence[str],
options: Dict,
interrupt: threading.Event,
) -> ExploiterResultData:
@ -186,19 +195,19 @@ class MockPuppet(IPuppet):
successful_exploiters = {
DOT_1: {
"ZerologonExploiter": ExploiterResultData(
False, False, False, OperatingSystem.WINDOWS, {}, [], "Zerologon failed"
False, False, False, OperatingSystem.WINDOWS.value, {}, [], "Zerologon failed"
),
"SSHExploiter": ExploiterResultData(
False,
False,
False,
OperatingSystem.LINUX,
OperatingSystem.LINUX.value,
info_ssh,
attempts,
"Failed exploiting",
),
"WmiExploiter": ExploiterResultData(
True, True, False, OperatingSystem.WINDOWS, info_wmi, attempts, None
True, True, False, OperatingSystem.WINDOWS.value, info_wmi, attempts
),
},
DOT_3: {
@ -206,7 +215,7 @@ class MockPuppet(IPuppet):
False,
False,
False,
OperatingSystem.WINDOWS,
OperatingSystem.WINDOWS.value,
info_wmi,
attempts,
"PowerShell Exploiter Failed",
@ -215,13 +224,13 @@ class MockPuppet(IPuppet):
False,
False,
False,
OperatingSystem.LINUX,
OperatingSystem.LINUX.value,
info_ssh,
attempts,
"Failed exploiting",
),
"ZerologonExploiter": ExploiterResultData(
True, False, False, OperatingSystem.WINDOWS, {}, [], None
True, False, False, OperatingSystem.WINDOWS.value, {}, []
),
},
}
@ -233,7 +242,7 @@ class MockPuppet(IPuppet):
False,
False,
False,
OperatingSystem.LINUX,
OperatingSystem.LINUX.value,
{},
[],
f"{name} failed for host {host}",

View File

@ -5,12 +5,14 @@ from unittest.mock import MagicMock
import pytest
from tests.unit_tests.infection_monkey.master.mock_puppet import MockPuppet
from common import OperatingSystem
from common.agent_configuration.agent_sub_configurations import (
ICMPScanConfiguration,
NetworkScanConfiguration,
PluginConfiguration,
TCPScanConfiguration,
)
from common.types import PingScanData
from infection_monkey.i_puppet import FingerprintData, PortScanData, PortStatus
from infection_monkey.master import IPScanner
from infection_monkey.network import NetworkAddress
@ -78,10 +80,15 @@ def assert_scan_results(address, scan_results):
assert_scan_results_host_down(address, ping_scan_data, port_scan_data, fingerprint_data)
def assert_scan_results_no_1(domain, ping_scan_data, port_scan_data, fingerprint_data):
def assert_scan_results_no_1(
domain,
ping_scan_data: PingScanData,
port_scan_data: PortScanData,
fingerprint_data: FingerprintData,
):
assert domain == "d1"
assert ping_scan_data.response_received is True
assert ping_scan_data.os == WINDOWS_OS
assert ping_scan_data.os == OperatingSystem.WINDOWS
assert len(port_scan_data.keys()) == 6
@ -100,7 +107,7 @@ def assert_scan_results_no_1(domain, ping_scan_data, port_scan_data, fingerprint
assert_fingerprint_results_no_1(fingerprint_data)
def assert_fingerprint_results_no_1(fingerprint_data):
def assert_fingerprint_results_no_1(fingerprint_data: FingerprintData):
assert len(fingerprint_data.keys()) == 3
assert fingerprint_data["SSHFinger"].services == {}
assert fingerprint_data["HTTPFinger"].services == {}
@ -112,11 +119,16 @@ def assert_fingerprint_results_no_1(fingerprint_data):
assert fingerprint_data["SMBFinger"].services["tcp-445"]["name"] == "smb_service_name"
def assert_scan_results_no_3(domain, ping_scan_data, port_scan_data, fingerprint_data):
def assert_scan_results_no_3(
domain,
ping_scan_data: PingScanData,
port_scan_data: PortScanData,
fingerprint_data: FingerprintData,
):
assert domain == "d3"
assert ping_scan_data.response_received is True
assert ping_scan_data.os == LINUX_OS
assert ping_scan_data.os == OperatingSystem.LINUX
assert len(port_scan_data.keys()) == 6
psd_443 = port_scan_data[443]
@ -134,7 +146,7 @@ def assert_scan_results_no_3(domain, ping_scan_data, port_scan_data, fingerprint
assert_fingerprint_results_no_3(fingerprint_data)
def assert_fingerprint_results_no_3(fingerprint_data):
def assert_fingerprint_results_no_3(fingerprint_data: FingerprintData):
assert len(fingerprint_data.keys()) == 3
assert fingerprint_data["SMBFinger"].services == {}
@ -152,7 +164,12 @@ def assert_fingerprint_results_no_3(fingerprint_data):
assert fingerprint_data["HTTPFinger"].services["tcp-443"]["data"] == ("SERVER_HEADERS_2", True)
def assert_scan_results_host_down(address, ping_scan_data, port_scan_data, fingerprint_data):
def assert_scan_results_host_down(
address,
ping_scan_data: PingScanData,
port_scan_data: PortScanData,
fingerprint_data: FingerprintData,
):
assert address.ip not in {"10.0.0.1", "10.0.0.3"}
assert address.domain is None

View File

@ -4,18 +4,14 @@ from unittest.mock import MagicMock
import pytest
from common import OperatingSystem
from common.agent_configuration.agent_sub_configurations import (
NetworkScanConfiguration,
PropagationConfiguration,
ScanTargetConfiguration,
)
from infection_monkey.i_puppet import (
ExploiterResultData,
FingerprintData,
PingScanData,
PortScanData,
PortStatus,
)
from common.types import PingScanData
from infection_monkey.i_puppet import ExploiterResultData, FingerprintData, PortScanData, PortStatus
from infection_monkey.master import IPScanResults, Propagator
from infection_monkey.model import VictimHost, VictimHostFactory
from infection_monkey.network import NetworkAddress
@ -38,7 +34,7 @@ def mock_victim_host_factory():
empty_fingerprint_data = FingerprintData(None, None, {})
dot_1_scan_results = IPScanResults(
PingScanData(True, "windows"),
PingScanData(True, OperatingSystem.WINDOWS),
{
22: PortScanData(22, PortStatus.CLOSED, None, None),
445: PortScanData(445, PortStatus.OPEN, "SMB BANNER", "tcp-445"),
@ -52,7 +48,7 @@ dot_1_scan_results = IPScanResults(
)
dot_3_scan_results = IPScanResults(
PingScanData(True, "linux"),
PingScanData(True, OperatingSystem.LINUX),
{
22: PortScanData(22, PortStatus.OPEN, "SSH BANNER", "tcp-22"),
443: PortScanData(443, PortStatus.OPEN, "HTTPS BANNER", "tcp-443"),

View File

@ -1,7 +1,8 @@
import threading
from unittest.mock import MagicMock
from infection_monkey.i_puppet import PingScanData, PluginType
from common.types import PingScanData
from infection_monkey.i_puppet import PluginType
from infection_monkey.puppet.puppet import EMPTY_FINGERPRINT, Puppet

View File

@ -7,6 +7,7 @@ from common.agent_configuration.agent_sub_configurations import (
CustomPBAConfiguration,
ScanTargetConfiguration,
)
from common.agent_events import PingScanEvent
from common.credentials import Credentials, LMHash, NTHash
from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory
from monkey_island.cc.event_queue import IslandEventTopic, PyPubSubIslandEventQueue
@ -307,6 +308,8 @@ IAgentLogRepository
IAgentLogRepository.upsert_agent_log
IAgentLogRepository.get_agent_log
# TODO: Remove once #2268 is closed
PingScanEvent
# pydantic base models
underscore_attrs_are_private