From 06ae6a8b905b6ecba7deac70a0fb62517b849a13 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Thu, 25 Aug 2022 17:55:29 +0000 Subject: [PATCH 1/9] Project: Add ipaddress types for mypy --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4e93e4be..9c64501ee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,7 +39,7 @@ repos: rev: v0.971 hooks: - id: mypy - additional_dependencies: [types-paramiko, types-python-dateutil, types-requests] + additional_dependencies: [types-ipaddress, types-paramiko, types-python-dateutil, types-requests] exclude: "vulture_allowlist.py" args: [--ignore-missing-imports] - repo: https://github.com/koalaman/shellcheck-precommit From 75ba889f57734da931e79a8aa0e6560894bdb5ba Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Thu, 25 Aug 2022 18:12:48 +0000 Subject: [PATCH 2/9] Agent: Fix typing issues --- .../network_scanning/scan_target_generator.py | 6 +++--- monkey/infection_monkey/network_scanning/tcp_scanner.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/network_scanning/scan_target_generator.py b/monkey/infection_monkey/network_scanning/scan_target_generator.py index 7fbdbe61a..2b2221a5a 100644 --- a/monkey/infection_monkey/network_scanning/scan_target_generator.py +++ b/monkey/infection_monkey/network_scanning/scan_target_generator.py @@ -1,7 +1,7 @@ import itertools import logging import socket -from typing import List +from typing import Any, Dict, List from common.network.network_range import InvalidNetworkRangeError, NetworkRange from infection_monkey.network import NetworkAddress, NetworkInterface @@ -39,7 +39,7 @@ def compile_scan_target_list( def _remove_redundant_targets(targets: List[NetworkAddress]) -> List[NetworkAddress]: - reverse_dns = {} + reverse_dns: Dict[Any, Any] = {} for target in targets: domain_name = target.domain ip = target.ip @@ -124,7 +124,7 @@ def _get_segmentation_check_targets( for (subnet1, subnet2) in subnet_pairs: if _is_segmentation_check_required(local_ips, subnet1, subnet2): - ips = _get_ips_from_ranges_to_scan(subnet2) + ips = _get_ips_from_ranges_to_scan([subnet2]) ips_to_scan.extend(ips) return ips_to_scan diff --git a/monkey/infection_monkey/network_scanning/tcp_scanner.py b/monkey/infection_monkey/network_scanning/tcp_scanner.py index f10e74115..cfb90026b 100644 --- a/monkey/infection_monkey/network_scanning/tcp_scanner.py +++ b/monkey/infection_monkey/network_scanning/tcp_scanner.py @@ -3,7 +3,7 @@ import select import socket import time from pprint import pformat -from typing import Iterable, Mapping, Tuple +from typing import Collection, Iterable, Mapping, Tuple from common.utils import Timer from infection_monkey.i_puppet import PortScanData, PortStatus @@ -16,7 +16,7 @@ EMPTY_PORT_SCAN = {-1: PortScanData(-1, PortStatus.CLOSED, None, None)} def scan_tcp_ports( - host: str, ports_to_scan: Iterable[int], timeout: float + host: str, ports_to_scan: Collection[int], timeout: float ) -> Mapping[int, PortScanData]: try: return _scan_tcp_ports(host, ports_to_scan, timeout) @@ -25,7 +25,7 @@ def scan_tcp_ports( return EMPTY_PORT_SCAN -def _scan_tcp_ports(host: str, ports_to_scan: Iterable[int], timeout: float): +def _scan_tcp_ports(host: str, ports_to_scan: Collection[int], timeout: float): open_ports = _check_tcp_ports(host, ports_to_scan, timeout) return _build_port_scan_data(ports_to_scan, open_ports) @@ -52,7 +52,7 @@ def _get_closed_port_data(port: int) -> PortScanData: def _check_tcp_ports( - ip: str, ports_to_scan: Iterable[int], timeout: float = DEFAULT_TIMEOUT + ip: str, ports_to_scan: Collection[int], timeout: float = DEFAULT_TIMEOUT ) -> Mapping[int, str]: """ Checks whether any of the given ports are open on a target IP. From d8beba17fdcd5180b0dbd699cf5a856b6e3a8149 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Thu, 25 Aug 2022 18:13:34 +0000 Subject: [PATCH 3/9] Agent: Update NetworkInterface to be IPv4Interface --- monkey/infection_monkey/monkey.py | 4 +- monkey/infection_monkey/network/info.py | 12 +--- .../network_scanning/scan_target_generator.py | 8 ++- .../master/test_propagator.py | 2 +- .../test_scan_target_generator.py | 65 +++++++++---------- 5 files changed, 43 insertions(+), 48 deletions(-) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index a9470dd24..2a28d78f9 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -242,7 +242,7 @@ class InfectionMonkey: def _get_local_network_interfaces(): local_network_interfaces = get_local_network_interfaces() for i in local_network_interfaces: - logger.debug(f"Found local interface {i.address}{i.netmask}") + logger.debug(f"Found local interface {i.ip.compressed}/{i.network.prefixlen}") return local_network_interfaces @@ -375,7 +375,7 @@ class InfectionMonkey: def _running_on_island(self, local_network_interfaces: List[NetworkInterface]) -> bool: server_ip, _ = address_to_ip_port(self._control_client.server_address) - return server_ip in {interface.address for interface in local_network_interfaces} + return server_ip in {interface.ip.compressed for interface in local_network_interfaces} def _is_another_monkey_running(self): return not self._singleton.try_lock() diff --git a/monkey/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py index 14f06acfe..99545d232 100644 --- a/monkey/infection_monkey/network/info.py +++ b/monkey/infection_monkey/network/info.py @@ -2,7 +2,7 @@ import itertools import socket import struct from collections import namedtuple -from ipaddress import IPv4Network +from ipaddress import IPv4Interface from random import randint # noqa: DUO102 from typing import List @@ -20,18 +20,12 @@ RTF_REJECT = 0x0200 # TODO: We can probably replace both of these namedtuples with classes in Python's ipaddress # library: https://docs.python.org/3/library/ipaddress.html -NetworkInterface = namedtuple("NetworkInterface", ("address", "netmask")) +NetworkInterface = IPv4Interface NetworkAddress = namedtuple("NetworkAddress", ("ip", "domain")) def get_local_network_interfaces() -> List[NetworkInterface]: - network_interfaces = [] - for i in get_host_subnets(): - netmask_bits = IPv4Network(f"{i['addr']}/{i['netmask']}", strict=False).prefixlen - cidr_netmask = f"/{netmask_bits}" - network_interfaces.append(NetworkInterface(i["addr"], cidr_netmask)) - - return network_interfaces + return [IPv4Interface(f"{i['addr']}/{i['netmask']}") for i in get_host_subnets()] def get_host_subnets(): diff --git a/monkey/infection_monkey/network_scanning/scan_target_generator.py b/monkey/infection_monkey/network_scanning/scan_target_generator.py index 2b2221a5a..298b20505 100644 --- a/monkey/infection_monkey/network_scanning/scan_target_generator.py +++ b/monkey/infection_monkey/network_scanning/scan_target_generator.py @@ -75,7 +75,9 @@ def _get_ips_from_ranges_to_scan(ranges_to_scan: List[str]) -> List[NetworkAddre def _get_ips_to_scan_from_local_interface( interfaces: List[NetworkInterface], ) -> List[NetworkAddress]: - ranges = [f"{interface.address}{interface.netmask}" for interface in interfaces] + ranges = [ + f"{interface.ip.compressed}/{interface.network.prefixlen}" for interface in interfaces + ] ranges = NetworkRange.filter_invalid_ranges( ranges, "Local network interface returns an invalid IP:" @@ -86,7 +88,7 @@ def _get_ips_to_scan_from_local_interface( def _remove_interface_ips( scan_targets: List[NetworkAddress], interfaces: List[NetworkInterface] ) -> List[NetworkAddress]: - interface_ips = [interface.address for interface in interfaces] + interface_ips = [interface.ip.compressed for interface in interfaces] return _remove_ips_from_scan_targets(scan_targets, interface_ips) @@ -112,7 +114,7 @@ def _get_segmentation_check_targets( inaccessible_subnets: List[str], local_interfaces: List[NetworkInterface] ) -> List[NetworkAddress]: ips_to_scan = [] - local_ips = [interface.address for interface in local_interfaces] + local_ips = [interface.ip.compressed for interface in local_interfaces] local_ips = NetworkRange.filter_invalid_ranges(local_ips, "Invalid local IP found: ") inaccessible_subnets = NetworkRange.filter_invalid_ranges( diff --git a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py index 39912c24f..0ebb6db76 100644 --- a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py +++ b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py @@ -294,7 +294,7 @@ def test_exploiter_result_processing( def test_scan_target_generation( telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory, default_agent_configuration ): - local_network_interfaces = [NetworkInterface("10.0.0.9", "/29")] + local_network_interfaces = [NetworkInterface("10.0.0.9/29")] p = Propagator( telemetry_messenger_spy, mock_ip_scanner, diff --git a/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py b/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py index 631d65fa8..2feff418b 100644 --- a/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py +++ b/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py @@ -112,10 +112,10 @@ def test_only_ip_blocklisted(ranges_to_scan): def test_local_network_interface_ips_removed_from_targets(): local_network_interfaces = [ - NetworkInterface("10.0.0.5", "/24"), - NetworkInterface("10.0.0.32", "/24"), - NetworkInterface("10.0.0.119", "/24"), - NetworkInterface("192.168.1.33", "/24"), + NetworkInterface("10.0.0.5/24"), + NetworkInterface("10.0.0.32/24"), + NetworkInterface("10.0.0.119/24"), + NetworkInterface("192.168.1.33/24"), ] scan_targets = compile_scan_target_list( @@ -128,12 +128,12 @@ def test_local_network_interface_ips_removed_from_targets(): assert len(scan_targets) == 252 for interface in local_network_interfaces: - assert interface.address not in scan_targets + assert interface.ip.compressed not in scan_targets def test_no_redundant_targets(): local_network_interfaces = [ - NetworkInterface("10.0.0.5", "/24"), + NetworkInterface("10.0.0.5/24"), ] scan_targets = compile_scan_target_list( @@ -152,10 +152,10 @@ def test_no_redundant_targets(): @pytest.mark.parametrize("ranges_to_scan", [["10.0.0.5"], []]) def test_only_scan_ip_is_local(ranges_to_scan): local_network_interfaces = [ - NetworkInterface("10.0.0.5", "/24"), - NetworkInterface("10.0.0.32", "/24"), - NetworkInterface("10.0.0.119", "/24"), - NetworkInterface("192.168.1.33", "/24"), + NetworkInterface("10.0.0.5/24"), + NetworkInterface("10.0.0.32/24"), + NetworkInterface("10.0.0.119/24"), + NetworkInterface("192.168.1.33/24"), ] scan_targets = compile_scan_target_list( @@ -171,10 +171,10 @@ def test_only_scan_ip_is_local(ranges_to_scan): def test_local_network_interface_ips_and_blocked_ips_removed_from_targets(): local_network_interfaces = [ - NetworkInterface("10.0.0.5", "/24"), - NetworkInterface("10.0.0.32", "/24"), - NetworkInterface("10.0.0.119", "/24"), - NetworkInterface("192.168.1.33", "/24"), + NetworkInterface("10.0.0.5/24"), + NetworkInterface("10.0.0.32/24"), + NetworkInterface("10.0.0.119/24"), + NetworkInterface("192.168.1.33/24"), ] blocked_ips = ["10.0.0.63", "192.168.1.77", "0.0.0.0"] @@ -191,14 +191,14 @@ def test_local_network_interface_ips_and_blocked_ips_removed_from_targets(): ) for interface in local_network_interfaces: - assert interface.address not in scan_targets + assert interface.ip.compressed not in scan_targets for ip in blocked_ips: assert ip not in scan_targets def test_local_subnet_added(): - local_network_interfaces = [NetworkInterface("10.0.0.5", "/24")] + local_network_interfaces = [NetworkInterface("10.0.0.5/24")] scan_targets = compile_scan_target_list( local_network_interfaces=local_network_interfaces, @@ -216,8 +216,8 @@ def test_local_subnet_added(): def test_multiple_local_subnets_added(): local_network_interfaces = [ - NetworkInterface("10.0.0.5", "/24"), - NetworkInterface("172.33.66.99", "/24"), + NetworkInterface("10.0.0.5/24"), + NetworkInterface("172.33.66.99/24"), ] scan_targets = compile_scan_target_list( @@ -239,8 +239,8 @@ def test_multiple_local_subnets_added(): def test_blocklisted_ips_missing_from_local_subnets(): local_network_interfaces = [ - NetworkInterface("10.0.0.5", "/24"), - NetworkInterface("172.33.66.99", "/24"), + NetworkInterface("10.0.0.5/24"), + NetworkInterface("172.33.66.99/24"), ] blocklisted_ips = ["10.0.0.12", "10.0.0.13", "172.33.66.25"] @@ -259,7 +259,7 @@ def test_blocklisted_ips_missing_from_local_subnets(): def test_local_subnets_and_ranges_added(): - local_network_interfaces = [NetworkInterface("10.0.0.5", "/24")] + local_network_interfaces = [NetworkInterface("10.0.0.5/24")] scan_targets = compile_scan_target_list( local_network_interfaces=local_network_interfaces, @@ -281,7 +281,7 @@ def test_local_subnets_and_ranges_added(): def test_local_network_interfaces_specified_but_disabled(): - local_network_interfaces = [NetworkInterface("10.0.0.5", "/24")] + local_network_interfaces = [NetworkInterface("10.0.0.5/24")] scan_targets = compile_scan_target_list( local_network_interfaces=local_network_interfaces, @@ -299,8 +299,8 @@ def test_local_network_interfaces_specified_but_disabled(): def test_local_network_interfaces_subnet_masks(): local_network_interfaces = [ - NetworkInterface("172.60.145.109", "/30"), - NetworkInterface("172.60.145.144", "/30"), + NetworkInterface("172.60.145.109/30"), + NetworkInterface("172.60.145.144/30"), ] scan_targets = compile_scan_target_list( @@ -318,7 +318,7 @@ def test_local_network_interfaces_subnet_masks(): def test_segmentation_targets(): - local_network_interfaces = [NetworkInterface("172.60.145.109", "/24")] + local_network_interfaces = [NetworkInterface("172.60.145.109/24")] inaccessible_subnets = ["172.60.145.108/30", "172.60.145.144/30"] @@ -338,7 +338,7 @@ def test_segmentation_targets(): def test_segmentation_clash_with_blocked(): local_network_interfaces = [ - NetworkInterface("172.60.145.109", "/30"), + NetworkInterface("172.60.145.109/30"), ] inaccessible_subnets = ["172.60.145.108/30", "172.60.145.149/30"] @@ -358,7 +358,7 @@ def test_segmentation_clash_with_blocked(): def test_segmentation_clash_with_targets(): local_network_interfaces = [ - NetworkInterface("172.60.145.109", "/30"), + NetworkInterface("172.60.145.109/30"), ] inaccessible_subnets = ["172.60.145.108/30", "172.60.145.149/30"] @@ -381,7 +381,7 @@ def test_segmentation_clash_with_targets(): def test_segmentation_one_network(): local_network_interfaces = [ - NetworkInterface("172.60.145.109", "/30"), + NetworkInterface("172.60.145.109/30"), ] inaccessible_subnets = ["172.60.145.1/24"] @@ -401,8 +401,8 @@ def test_segmentation_one_network(): def test_segmentation_inaccessible_networks(): local_network_interfaces = [ - NetworkInterface("172.60.1.1", "/24"), - NetworkInterface("172.60.2.1", "/24"), + NetworkInterface("172.60.1.1/24"), + NetworkInterface("172.60.2.1/24"), ] inaccessible_subnets = ["172.60.144.1/24", "172.60.146.1/24"] @@ -420,8 +420,7 @@ def test_segmentation_inaccessible_networks(): def test_invalid_inputs(): local_network_interfaces = [ - NetworkInterface("172.60.999.109", "/30"), - NetworkInterface("172.60.145.109", "/30"), + NetworkInterface("172.60.145.109/30"), ] inaccessible_subnets = [ @@ -447,7 +446,7 @@ def test_invalid_inputs(): def test_invalid_blocklisted_ip(): - local_network_interfaces = [NetworkInterface("172.60.145.109", "/30")] + local_network_interfaces = [NetworkInterface("172.60.145.109/30")] inaccessible_subnets = ["172.60.147.8/30", "172.60.147.148/30"] From f31ba824c6af118a44aafd6a2548ce25803fd115 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Thu, 25 Aug 2022 18:51:52 +0000 Subject: [PATCH 4/9] UT: Fix tests --- .../network_scanning/scan_target_generator.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/monkey/infection_monkey/network_scanning/scan_target_generator.py b/monkey/infection_monkey/network_scanning/scan_target_generator.py index 298b20505..a799bcce9 100644 --- a/monkey/infection_monkey/network_scanning/scan_target_generator.py +++ b/monkey/infection_monkey/network_scanning/scan_target_generator.py @@ -19,7 +19,7 @@ def compile_scan_target_list( blocklisted_ips: List[str], enable_local_network_scan: bool, ) -> List[NetworkAddress]: - scan_targets = _get_ips_from_ranges_to_scan(ranges_to_scan) + scan_targets = _get_ips_from_subnets_to_scan(ranges_to_scan) if enable_local_network_scan: scan_targets.extend(_get_ips_to_scan_from_local_interface(local_network_interfaces)) @@ -58,14 +58,17 @@ def _range_to_addresses(range_obj: NetworkRange) -> List[NetworkAddress]: return addresses -def _get_ips_from_ranges_to_scan(ranges_to_scan: List[str]) -> List[NetworkAddress]: - scan_targets = [] - +def _get_ips_from_subnets_to_scan(subnets_to_scan: List[str]) -> List[NetworkAddress]: ranges_to_scan = NetworkRange.filter_invalid_ranges( - ranges_to_scan, "Bad network range input for targets to scan:" + subnets_to_scan, "Bad network range input for targets to scan:" ) network_ranges = [NetworkRange.get_range_obj(_range) for _range in ranges_to_scan] + return _get_ips_from_ranges_to_scan(network_ranges) + + +def _get_ips_from_ranges_to_scan(network_ranges: List[NetworkRange]) -> List[NetworkAddress]: + scan_targets = [] for _range in network_ranges: scan_targets.extend(_range_to_addresses(_range)) @@ -82,7 +85,7 @@ def _get_ips_to_scan_from_local_interface( ranges = NetworkRange.filter_invalid_ranges( ranges, "Local network interface returns an invalid IP:" ) - return _get_ips_from_ranges_to_scan(ranges) + return _get_ips_from_subnets_to_scan(ranges) def _remove_interface_ips( @@ -121,8 +124,8 @@ def _get_segmentation_check_targets( inaccessible_subnets, "Invalid segmentation scan target: " ) - inaccessible_subnets = _convert_to_range_object(inaccessible_subnets) - subnet_pairs = itertools.product(inaccessible_subnets, inaccessible_subnets) + inaccessible_ranges = _convert_to_range_object(inaccessible_subnets) + subnet_pairs = itertools.product(inaccessible_ranges, inaccessible_ranges) for (subnet1, subnet2) in subnet_pairs: if _is_segmentation_check_required(local_ips, subnet1, subnet2): From 01c508e248c624a1ab1d9459b01b72d7c0c28ab7 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Mon, 29 Aug 2022 15:27:39 +0000 Subject: [PATCH 5/9] Agent: Replace NetworkInterface with IPv4Interface --- .../master/automated_master.py | 4 +- monkey/infection_monkey/master/propagator.py | 5 +- monkey/infection_monkey/monkey.py | 6 +- monkey/infection_monkey/network/__init__.py | 2 +- monkey/infection_monkey/network/info.py | 3 +- .../network_scanning/scan_target_generator.py | 11 ++-- .../master/test_propagator.py | 5 +- .../test_scan_target_generator.py | 63 ++++++++++--------- 8 files changed, 51 insertions(+), 48 deletions(-) diff --git a/monkey/infection_monkey/master/automated_master.py b/monkey/infection_monkey/master/automated_master.py index 55afad11a..e491e4537 100644 --- a/monkey/infection_monkey/master/automated_master.py +++ b/monkey/infection_monkey/master/automated_master.py @@ -1,6 +1,7 @@ import logging import threading import time +from ipaddress import IPv4Interface from typing import Any, Callable, Iterable, List, Optional from common.agent_configuration import CustomPBAConfiguration, PluginConfiguration @@ -10,7 +11,6 @@ from infection_monkey.i_control_channel import IControlChannel, IslandCommunicat from infection_monkey.i_master import IMaster from infection_monkey.i_puppet import IPuppet from infection_monkey.model import VictimHostFactory -from infection_monkey.network import NetworkInterface from infection_monkey.telemetry.credentials_telem import CredentialsTelem from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger from infection_monkey.telemetry.post_breach_telem import PostBreachTelem @@ -39,7 +39,7 @@ class AutomatedMaster(IMaster): telemetry_messenger: ITelemetryMessenger, victim_host_factory: VictimHostFactory, control_channel: IControlChannel, - local_network_interfaces: List[NetworkInterface], + local_network_interfaces: List[IPv4Interface], credentials_store: IPropagationCredentialsRepository, ): self._current_depth = current_depth diff --git a/monkey/infection_monkey/master/propagator.py b/monkey/infection_monkey/master/propagator.py index 187ff645a..62f489657 100644 --- a/monkey/infection_monkey/master/propagator.py +++ b/monkey/infection_monkey/master/propagator.py @@ -1,5 +1,6 @@ import logging from dataclasses import replace +from ipaddress import IPv4Interface from queue import Queue from threading import Event from typing import List, Sequence @@ -18,7 +19,7 @@ from infection_monkey.i_puppet import ( PortStatus, ) from infection_monkey.model import VictimHost, VictimHostFactory -from infection_monkey.network import NetworkAddress, NetworkInterface +from infection_monkey.network import NetworkAddress from infection_monkey.network_scanning.scan_target_generator import compile_scan_target_list from infection_monkey.telemetry.exploit_telem import ExploitTelem from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger @@ -37,7 +38,7 @@ class Propagator: ip_scanner: IPScanner, exploiter: Exploiter, victim_host_factory: VictimHostFactory, - local_network_interfaces: List[NetworkInterface], + local_network_interfaces: List[IPv4Interface], ): self._telemetry_messenger = telemetry_messenger self._ip_scanner = ip_scanner diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 2a28d78f9..ff36ebb88 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -3,6 +3,7 @@ import logging import os import subprocess import sys +from ipaddress import IPv4Interface from pathlib import Path, WindowsPath from typing import List @@ -39,7 +40,6 @@ from infection_monkey.i_puppet import IPuppet, PluginType from infection_monkey.master import AutomatedMaster from infection_monkey.master.control_channel import ControlChannel from infection_monkey.model import VictimHostFactory -from infection_monkey.network import NetworkInterface from infection_monkey.network.firewall import app as firewall from infection_monkey.network.info import get_local_network_interfaces from infection_monkey.network_scanning.elasticsearch_fingerprinter import ElasticSearchFingerprinter @@ -364,7 +364,7 @@ class InfectionMonkey: return puppet def _build_victim_host_factory( - self, local_network_interfaces: List[NetworkInterface] + self, local_network_interfaces: List[IPv4Interface] ) -> VictimHostFactory: on_island = self._running_on_island(local_network_interfaces) logger.debug(f"This agent is running on the island: {on_island}") @@ -373,7 +373,7 @@ class InfectionMonkey: self._monkey_inbound_tunnel, self._cmd_island_ip, self._cmd_island_port, on_island ) - def _running_on_island(self, local_network_interfaces: List[NetworkInterface]) -> bool: + def _running_on_island(self, local_network_interfaces: List[IPv4Interface]) -> bool: server_ip, _ = address_to_ip_port(self._control_client.server_address) return server_ip in {interface.ip.compressed for interface in local_network_interfaces} diff --git a/monkey/infection_monkey/network/__init__.py b/monkey/infection_monkey/network/__init__.py index ba42da1ba..36117cbbe 100644 --- a/monkey/infection_monkey/network/__init__.py +++ b/monkey/infection_monkey/network/__init__.py @@ -1 +1 @@ -from .info import NetworkAddress, NetworkInterface +from .info import NetworkAddress diff --git a/monkey/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py index 99545d232..eee01040a 100644 --- a/monkey/infection_monkey/network/info.py +++ b/monkey/infection_monkey/network/info.py @@ -20,11 +20,10 @@ RTF_REJECT = 0x0200 # TODO: We can probably replace both of these namedtuples with classes in Python's ipaddress # library: https://docs.python.org/3/library/ipaddress.html -NetworkInterface = IPv4Interface NetworkAddress = namedtuple("NetworkAddress", ("ip", "domain")) -def get_local_network_interfaces() -> List[NetworkInterface]: +def get_local_network_interfaces() -> List[IPv4Interface]: return [IPv4Interface(f"{i['addr']}/{i['netmask']}") for i in get_host_subnets()] diff --git a/monkey/infection_monkey/network_scanning/scan_target_generator.py b/monkey/infection_monkey/network_scanning/scan_target_generator.py index a799bcce9..b4f5e4238 100644 --- a/monkey/infection_monkey/network_scanning/scan_target_generator.py +++ b/monkey/infection_monkey/network_scanning/scan_target_generator.py @@ -1,10 +1,11 @@ import itertools import logging import socket +from ipaddress import IPv4Interface from typing import Any, Dict, List from common.network.network_range import InvalidNetworkRangeError, NetworkRange -from infection_monkey.network import NetworkAddress, NetworkInterface +from infection_monkey.network import NetworkAddress logger = logging.getLogger(__name__) @@ -13,7 +14,7 @@ logger = logging.getLogger(__name__) def compile_scan_target_list( - local_network_interfaces: List[NetworkInterface], + local_network_interfaces: List[IPv4Interface], ranges_to_scan: List[str], inaccessible_subnets: List[str], blocklisted_ips: List[str], @@ -76,7 +77,7 @@ def _get_ips_from_ranges_to_scan(network_ranges: List[NetworkRange]) -> List[Net def _get_ips_to_scan_from_local_interface( - interfaces: List[NetworkInterface], + interfaces: List[IPv4Interface], ) -> List[NetworkAddress]: ranges = [ f"{interface.ip.compressed}/{interface.network.prefixlen}" for interface in interfaces @@ -89,7 +90,7 @@ def _get_ips_to_scan_from_local_interface( def _remove_interface_ips( - scan_targets: List[NetworkAddress], interfaces: List[NetworkInterface] + scan_targets: List[NetworkAddress], interfaces: List[IPv4Interface] ) -> List[NetworkAddress]: interface_ips = [interface.ip.compressed for interface in interfaces] return _remove_ips_from_scan_targets(scan_targets, interface_ips) @@ -114,7 +115,7 @@ def _remove_ips_from_scan_targets( def _get_segmentation_check_targets( - inaccessible_subnets: List[str], local_interfaces: List[NetworkInterface] + inaccessible_subnets: List[str], local_interfaces: List[IPv4Interface] ) -> List[NetworkAddress]: ips_to_scan = [] local_ips = [interface.ip.compressed for interface in local_interfaces] diff --git a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py index 0ebb6db76..b8ceec5dd 100644 --- a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py +++ b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py @@ -1,3 +1,4 @@ +from ipaddress import IPv4Interface from threading import Event from unittest.mock import MagicMock @@ -17,7 +18,7 @@ from infection_monkey.i_puppet import ( ) from infection_monkey.master import IPScanResults, Propagator from infection_monkey.model import VictimHost, VictimHostFactory -from infection_monkey.network import NetworkAddress, NetworkInterface +from infection_monkey.network import NetworkAddress from infection_monkey.telemetry.exploit_telem import ExploitTelem @@ -294,7 +295,7 @@ def test_exploiter_result_processing( def test_scan_target_generation( telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory, default_agent_configuration ): - local_network_interfaces = [NetworkInterface("10.0.0.9/29")] + local_network_interfaces = [IPv4Interface("10.0.0.9/29")] p = Propagator( telemetry_messenger_spy, mock_ip_scanner, diff --git a/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py b/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py index 2feff418b..25e5f9ed3 100644 --- a/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py +++ b/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py @@ -1,9 +1,10 @@ +from ipaddress import IPv4Interface from itertools import chain import pytest from common.network.network_range import InvalidNetworkRangeError -from infection_monkey.network import NetworkAddress, NetworkInterface +from infection_monkey.network import NetworkAddress from infection_monkey.network_scanning.scan_target_generator import compile_scan_target_list @@ -112,10 +113,10 @@ def test_only_ip_blocklisted(ranges_to_scan): def test_local_network_interface_ips_removed_from_targets(): local_network_interfaces = [ - NetworkInterface("10.0.0.5/24"), - NetworkInterface("10.0.0.32/24"), - NetworkInterface("10.0.0.119/24"), - NetworkInterface("192.168.1.33/24"), + IPv4Interface("10.0.0.5/24"), + IPv4Interface("10.0.0.32/24"), + IPv4Interface("10.0.0.119/24"), + IPv4Interface("192.168.1.33/24"), ] scan_targets = compile_scan_target_list( @@ -133,7 +134,7 @@ def test_local_network_interface_ips_removed_from_targets(): def test_no_redundant_targets(): local_network_interfaces = [ - NetworkInterface("10.0.0.5/24"), + IPv4Interface("10.0.0.5/24"), ] scan_targets = compile_scan_target_list( @@ -152,10 +153,10 @@ def test_no_redundant_targets(): @pytest.mark.parametrize("ranges_to_scan", [["10.0.0.5"], []]) def test_only_scan_ip_is_local(ranges_to_scan): local_network_interfaces = [ - NetworkInterface("10.0.0.5/24"), - NetworkInterface("10.0.0.32/24"), - NetworkInterface("10.0.0.119/24"), - NetworkInterface("192.168.1.33/24"), + IPv4Interface("10.0.0.5/24"), + IPv4Interface("10.0.0.32/24"), + IPv4Interface("10.0.0.119/24"), + IPv4Interface("192.168.1.33/24"), ] scan_targets = compile_scan_target_list( @@ -171,10 +172,10 @@ def test_only_scan_ip_is_local(ranges_to_scan): def test_local_network_interface_ips_and_blocked_ips_removed_from_targets(): local_network_interfaces = [ - NetworkInterface("10.0.0.5/24"), - NetworkInterface("10.0.0.32/24"), - NetworkInterface("10.0.0.119/24"), - NetworkInterface("192.168.1.33/24"), + IPv4Interface("10.0.0.5/24"), + IPv4Interface("10.0.0.32/24"), + IPv4Interface("10.0.0.119/24"), + IPv4Interface("192.168.1.33/24"), ] blocked_ips = ["10.0.0.63", "192.168.1.77", "0.0.0.0"] @@ -198,7 +199,7 @@ def test_local_network_interface_ips_and_blocked_ips_removed_from_targets(): def test_local_subnet_added(): - local_network_interfaces = [NetworkInterface("10.0.0.5/24")] + local_network_interfaces = [IPv4Interface("10.0.0.5/24")] scan_targets = compile_scan_target_list( local_network_interfaces=local_network_interfaces, @@ -216,8 +217,8 @@ def test_local_subnet_added(): def test_multiple_local_subnets_added(): local_network_interfaces = [ - NetworkInterface("10.0.0.5/24"), - NetworkInterface("172.33.66.99/24"), + IPv4Interface("10.0.0.5/24"), + IPv4Interface("172.33.66.99/24"), ] scan_targets = compile_scan_target_list( @@ -239,8 +240,8 @@ def test_multiple_local_subnets_added(): def test_blocklisted_ips_missing_from_local_subnets(): local_network_interfaces = [ - NetworkInterface("10.0.0.5/24"), - NetworkInterface("172.33.66.99/24"), + IPv4Interface("10.0.0.5/24"), + IPv4Interface("172.33.66.99/24"), ] blocklisted_ips = ["10.0.0.12", "10.0.0.13", "172.33.66.25"] @@ -259,7 +260,7 @@ def test_blocklisted_ips_missing_from_local_subnets(): def test_local_subnets_and_ranges_added(): - local_network_interfaces = [NetworkInterface("10.0.0.5/24")] + local_network_interfaces = [IPv4Interface("10.0.0.5/24")] scan_targets = compile_scan_target_list( local_network_interfaces=local_network_interfaces, @@ -281,7 +282,7 @@ def test_local_subnets_and_ranges_added(): def test_local_network_interfaces_specified_but_disabled(): - local_network_interfaces = [NetworkInterface("10.0.0.5/24")] + local_network_interfaces = [IPv4Interface("10.0.0.5/24")] scan_targets = compile_scan_target_list( local_network_interfaces=local_network_interfaces, @@ -299,8 +300,8 @@ def test_local_network_interfaces_specified_but_disabled(): def test_local_network_interfaces_subnet_masks(): local_network_interfaces = [ - NetworkInterface("172.60.145.109/30"), - NetworkInterface("172.60.145.144/30"), + IPv4Interface("172.60.145.109/30"), + IPv4Interface("172.60.145.144/30"), ] scan_targets = compile_scan_target_list( @@ -318,7 +319,7 @@ def test_local_network_interfaces_subnet_masks(): def test_segmentation_targets(): - local_network_interfaces = [NetworkInterface("172.60.145.109/24")] + local_network_interfaces = [IPv4Interface("172.60.145.109/24")] inaccessible_subnets = ["172.60.145.108/30", "172.60.145.144/30"] @@ -338,7 +339,7 @@ def test_segmentation_targets(): def test_segmentation_clash_with_blocked(): local_network_interfaces = [ - NetworkInterface("172.60.145.109/30"), + IPv4Interface("172.60.145.109/30"), ] inaccessible_subnets = ["172.60.145.108/30", "172.60.145.149/30"] @@ -358,7 +359,7 @@ def test_segmentation_clash_with_blocked(): def test_segmentation_clash_with_targets(): local_network_interfaces = [ - NetworkInterface("172.60.145.109/30"), + IPv4Interface("172.60.145.109/30"), ] inaccessible_subnets = ["172.60.145.108/30", "172.60.145.149/30"] @@ -381,7 +382,7 @@ def test_segmentation_clash_with_targets(): def test_segmentation_one_network(): local_network_interfaces = [ - NetworkInterface("172.60.145.109/30"), + IPv4Interface("172.60.145.109/30"), ] inaccessible_subnets = ["172.60.145.1/24"] @@ -401,8 +402,8 @@ def test_segmentation_one_network(): def test_segmentation_inaccessible_networks(): local_network_interfaces = [ - NetworkInterface("172.60.1.1/24"), - NetworkInterface("172.60.2.1/24"), + IPv4Interface("172.60.1.1/24"), + IPv4Interface("172.60.2.1/24"), ] inaccessible_subnets = ["172.60.144.1/24", "172.60.146.1/24"] @@ -420,7 +421,7 @@ def test_segmentation_inaccessible_networks(): def test_invalid_inputs(): local_network_interfaces = [ - NetworkInterface("172.60.145.109/30"), + IPv4Interface("172.60.145.109/30"), ] inaccessible_subnets = [ @@ -446,7 +447,7 @@ def test_invalid_inputs(): def test_invalid_blocklisted_ip(): - local_network_interfaces = [NetworkInterface("172.60.145.109/30")] + local_network_interfaces = [IPv4Interface("172.60.145.109/30")] inaccessible_subnets = ["172.60.147.8/30", "172.60.147.148/30"] From 2b55c35a65c28ba4f9585bb3e8659afbdca98085 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Mon, 29 Aug 2022 16:51:50 +0000 Subject: [PATCH 6/9] Agent: Use str() instead of .compressed --- monkey/infection_monkey/monkey.py | 6 +++--- .../network_scanning/scan_target_generator.py | 8 +++----- .../network_scanning/test_scan_target_generator.py | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index ff36ebb88..7fccc7616 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -239,10 +239,10 @@ class InfectionMonkey: ) @staticmethod - def _get_local_network_interfaces(): + def _get_local_network_interfaces() -> List[IPv4Interface]: local_network_interfaces = get_local_network_interfaces() for i in local_network_interfaces: - logger.debug(f"Found local interface {i.ip.compressed}/{i.network.prefixlen}") + logger.debug(f"Found local interface {str(i.ip)}/{i.network.prefixlen}") return local_network_interfaces @@ -375,7 +375,7 @@ class InfectionMonkey: def _running_on_island(self, local_network_interfaces: List[IPv4Interface]) -> bool: server_ip, _ = address_to_ip_port(self._control_client.server_address) - return server_ip in {interface.ip.compressed for interface in local_network_interfaces} + return server_ip in {str(interface.ip) for interface in local_network_interfaces} def _is_another_monkey_running(self): return not self._singleton.try_lock() diff --git a/monkey/infection_monkey/network_scanning/scan_target_generator.py b/monkey/infection_monkey/network_scanning/scan_target_generator.py index b4f5e4238..a579aac2f 100644 --- a/monkey/infection_monkey/network_scanning/scan_target_generator.py +++ b/monkey/infection_monkey/network_scanning/scan_target_generator.py @@ -79,9 +79,7 @@ def _get_ips_from_ranges_to_scan(network_ranges: List[NetworkRange]) -> List[Net def _get_ips_to_scan_from_local_interface( interfaces: List[IPv4Interface], ) -> List[NetworkAddress]: - ranges = [ - f"{interface.ip.compressed}/{interface.network.prefixlen}" for interface in interfaces - ] + ranges = [f"{str(interface.ip)}/{interface.network.prefixlen}" for interface in interfaces] ranges = NetworkRange.filter_invalid_ranges( ranges, "Local network interface returns an invalid IP:" @@ -92,7 +90,7 @@ def _get_ips_to_scan_from_local_interface( def _remove_interface_ips( scan_targets: List[NetworkAddress], interfaces: List[IPv4Interface] ) -> List[NetworkAddress]: - interface_ips = [interface.ip.compressed for interface in interfaces] + interface_ips = [str(interface.ip) for interface in interfaces] return _remove_ips_from_scan_targets(scan_targets, interface_ips) @@ -118,7 +116,7 @@ def _get_segmentation_check_targets( inaccessible_subnets: List[str], local_interfaces: List[IPv4Interface] ) -> List[NetworkAddress]: ips_to_scan = [] - local_ips = [interface.ip.compressed for interface in local_interfaces] + local_ips = [str(interface.ip) for interface in local_interfaces] local_ips = NetworkRange.filter_invalid_ranges(local_ips, "Invalid local IP found: ") inaccessible_subnets = NetworkRange.filter_invalid_ranges( diff --git a/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py b/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py index 25e5f9ed3..82179b618 100644 --- a/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py +++ b/monkey/tests/unit_tests/infection_monkey/network_scanning/test_scan_target_generator.py @@ -129,7 +129,7 @@ def test_local_network_interface_ips_removed_from_targets(): assert len(scan_targets) == 252 for interface in local_network_interfaces: - assert interface.ip.compressed not in scan_targets + assert str(interface.ip) not in scan_targets def test_no_redundant_targets(): @@ -192,7 +192,7 @@ def test_local_network_interface_ips_and_blocked_ips_removed_from_targets(): ) for interface in local_network_interfaces: - assert interface.ip.compressed not in scan_targets + assert str(interface.ip) not in scan_targets for ip in blocked_ips: assert ip not in scan_targets From e6663747eb3bd78e40efde4474d0154372b0dc8d Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Mon, 29 Aug 2022 20:32:47 +0000 Subject: [PATCH 7/9] Agent: Use IPv4Interface's string --- .../infection_monkey/network_scanning/scan_target_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/network_scanning/scan_target_generator.py b/monkey/infection_monkey/network_scanning/scan_target_generator.py index a579aac2f..b25cbdf25 100644 --- a/monkey/infection_monkey/network_scanning/scan_target_generator.py +++ b/monkey/infection_monkey/network_scanning/scan_target_generator.py @@ -79,7 +79,7 @@ def _get_ips_from_ranges_to_scan(network_ranges: List[NetworkRange]) -> List[Net def _get_ips_to_scan_from_local_interface( interfaces: List[IPv4Interface], ) -> List[NetworkAddress]: - ranges = [f"{str(interface.ip)}/{interface.network.prefixlen}" for interface in interfaces] + ranges = [str(interface) for interface in interfaces] ranges = NetworkRange.filter_invalid_ranges( ranges, "Local network interface returns an invalid IP:" From fedfe4e45dc9c1f1cb14817921c287d6f29e16b6 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 30 Aug 2022 11:58:57 +0000 Subject: [PATCH 8/9] Agent: Use str(interface) instead of building string --- monkey/infection_monkey/monkey.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 7fccc7616..220eba1f8 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -241,8 +241,8 @@ class InfectionMonkey: @staticmethod def _get_local_network_interfaces() -> List[IPv4Interface]: local_network_interfaces = get_local_network_interfaces() - for i in local_network_interfaces: - logger.debug(f"Found local interface {str(i.ip)}/{i.network.prefixlen}") + for interface in local_network_interfaces: + logger.debug(f"Found local interface {str(interface)}") return local_network_interfaces From 70a9251c5b81c50fff5ab7d1f72a46087a7508c5 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 30 Aug 2022 13:44:32 +0000 Subject: [PATCH 9/9] Agent: Fix type hints --- monkey/infection_monkey/network/info.py | 10 ++++++---- .../network_scanning/scan_target_generator.py | 4 ++-- vulture_allowlist.py | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py index eee01040a..2ae49b056 100644 --- a/monkey/infection_monkey/network/info.py +++ b/monkey/infection_monkey/network/info.py @@ -1,7 +1,7 @@ import itertools import socket import struct -from collections import namedtuple +from dataclasses import dataclass from ipaddress import IPv4Interface from random import randint # noqa: DUO102 from typing import List @@ -18,9 +18,11 @@ SIOCGIFNETMASK = 0x891B # get network PA mask RTF_UP = 0x0001 # Route usable RTF_REJECT = 0x0200 -# TODO: We can probably replace both of these namedtuples with classes in Python's ipaddress -# library: https://docs.python.org/3/library/ipaddress.html -NetworkAddress = namedtuple("NetworkAddress", ("ip", "domain")) + +@dataclass +class NetworkAddress: + ip: str + domain: str def get_local_network_interfaces() -> List[IPv4Interface]: diff --git a/monkey/infection_monkey/network_scanning/scan_target_generator.py b/monkey/infection_monkey/network_scanning/scan_target_generator.py index b25cbdf25..0efbaee1c 100644 --- a/monkey/infection_monkey/network_scanning/scan_target_generator.py +++ b/monkey/infection_monkey/network_scanning/scan_target_generator.py @@ -2,7 +2,7 @@ import itertools import logging import socket from ipaddress import IPv4Interface -from typing import Any, Dict, List +from typing import Dict, List from common.network.network_range import InvalidNetworkRangeError, NetworkRange from infection_monkey.network import NetworkAddress @@ -40,7 +40,7 @@ def compile_scan_target_list( def _remove_redundant_targets(targets: List[NetworkAddress]) -> List[NetworkAddress]: - reverse_dns: Dict[Any, Any] = {} + reverse_dns: Dict[str, str] = {} for target in targets: domain_name = target.domain ip = target.ip diff --git a/vulture_allowlist.py b/vulture_allowlist.py index b203a2bea..5ab45088a 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -218,6 +218,7 @@ stop_time parent_id cc_server hardware_id +network_interfaces connections # TODO DELETE AFTER RESOURCE REFACTORING