diff --git a/monkey/common/network/network_utils.py b/monkey/common/network/network_utils.py index d211474a5..f04e5a64e 100644 --- a/monkey/common/network/network_utils.py +++ b/monkey/common/network/network_utils.py @@ -1,4 +1,26 @@ -from typing import Optional, Tuple +import ipaddress +from ipaddress import IPv4Interface +from typing import List, Optional, Sequence, Tuple + +from netifaces import AF_INET, ifaddresses, interfaces + + +def get_my_ip_addresses() -> Sequence[str]: + return [str(interface.ip) for interface in get_network_interfaces()] + + +def get_network_interfaces() -> List[IPv4Interface]: + local_interfaces = [] + for interface in interfaces(): + addresses = ifaddresses(interface).get(AF_INET, []) + local_interfaces.extend( + [ + ipaddress.IPv4Interface(link["addr"] + "/" + link["netmask"]) + for link in addresses + if link["addr"] != "127.0.0.1" + ] + ) + return local_interfaces # TODO: `address_to_port()` should return the port as an integer. diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 7f161ecbd..58ab43fa6 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -7,8 +7,9 @@ import requests from urllib3 import disable_warnings from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT +from common.network.network_utils import get_my_ip_addresses from infection_monkey.config import GUID -from infection_monkey.network.info import get_host_subnets, local_ips +from infection_monkey.network.info import get_host_subnets from infection_monkey.utils import agent_process disable_warnings() # noqa DUO131 @@ -38,7 +39,7 @@ class ControlClient: monkey = { "guid": GUID, "hostname": hostname, - "ip_addresses": local_ips(), + "ip_addresses": get_my_ip_addresses(), "networks": get_host_subnets(), "description": " ".join(platform.uname()), "parent": parent, diff --git a/monkey/infection_monkey/master/control_channel.py b/monkey/infection_monkey/master/control_channel.py index b1b4bae81..76be63b5d 100644 --- a/monkey/infection_monkey/master/control_channel.py +++ b/monkey/infection_monkey/master/control_channel.py @@ -11,8 +11,8 @@ from common import AgentRegistrationData from common.agent_configuration import AgentConfiguration from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT from common.credentials import Credentials +from common.network.network_utils import get_network_interfaces from infection_monkey.i_control_channel import IControlChannel, IslandCommunicationError -from infection_monkey.network.info import get_network_interfaces from infection_monkey.utils import agent_process from infection_monkey.utils.ids import get_agent_id, get_machine_id diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index dfc34bec2..17a66d6b3 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -15,7 +15,11 @@ from common.event_serializers import ( register_common_agent_event_serializers, ) from common.events import CredentialsStolenEvent -from common.network.network_utils import address_to_ip_port +from common.network.network_utils import ( + address_to_ip_port, + get_my_ip_addresses, + get_network_interfaces, +) from common.utils.argparse_types import positive_int from common.utils.attack_utils import ScanStatus, UsageEnum from common.version import get_version @@ -45,7 +49,7 @@ from infection_monkey.master import AutomatedMaster from infection_monkey.master.control_channel import ControlChannel from infection_monkey.model import VictimHostFactory from infection_monkey.network.firewall import app as firewall -from infection_monkey.network.info import get_free_tcp_port, get_network_interfaces, local_ips +from infection_monkey.network.info import get_free_tcp_port from infection_monkey.network.relay import TCPRelay from infection_monkey.network.relay.utils import ( find_server, @@ -201,7 +205,7 @@ class InfectionMonkey: self._cmd_island_port, client_disconnect_timeout=config.keep_tunnel_open_time, ) - relay_servers = [f"{ip}:{relay_port}" for ip in local_ips()] + relay_servers = [f"{ip}:{relay_port}" for ip in get_my_ip_addresses()] if not maximum_depth_reached(config.propagation.maximum_depth, self._current_depth): self._relay.start() @@ -220,7 +224,7 @@ class InfectionMonkey: return agent_event_serializer_registry def _build_master(self, relay_servers: List[str]): - local_network_interfaces = InfectionMonkey._get_local_network_interfaces() + local_network_interfaces = get_network_interfaces() # TODO control_channel and control_client have same responsibilities, merge them propagation_credentials_repository = AggregatingPropagationCredentialsRepository( @@ -235,7 +239,7 @@ class InfectionMonkey: self._agent_event_serializer_registry, ) - puppet = self._build_puppet(propagation_credentials_repository, event_queue) + puppet = self._build_puppet(event_queue) victim_host_factory = self._build_victim_host_factory(local_network_interfaces) @@ -271,17 +275,8 @@ class InfectionMonkey: AgentEventForwarder(server_address, agent_event_serializer_registry).send_event ) - @staticmethod - def _get_local_network_interfaces() -> List[IPv4Interface]: - local_network_interfaces = get_network_interfaces() - for interface in local_network_interfaces: - logger.debug(f"Found local interface {str(interface)}") - - return local_network_interfaces - def _build_puppet( self, - propagation_credentials_repository: IPropagationCredentialsRepository, event_queue: IAgentEventQueue, ) -> IPuppet: puppet = Puppet() diff --git a/monkey/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py index 4bd5b763e..8cc12038d 100644 --- a/monkey/infection_monkey/network/info.py +++ b/monkey/infection_monkey/network/info.py @@ -2,10 +2,9 @@ import itertools import socket import struct from dataclasses import dataclass -from ipaddress import IPv4Interface from random import shuffle # noqa: DUO102 from threading import Lock -from typing import Dict, List, Set +from typing import Dict, Set import netifaces import psutil @@ -29,10 +28,6 @@ class NetworkAddress: domain: str -def get_network_interfaces() -> List[IPv4Interface]: - return [IPv4Interface(f"{i['addr']}/{i['netmask']}") for i in get_host_subnets()] - - def get_host_subnets(): """ Returns a list of subnets visible to host (omitting loopback and auto conf networks) @@ -60,20 +55,12 @@ def get_host_subnets(): if is_windows_os(): - def local_ips(): - local_hostname = socket.gethostname() - return socket.gethostbyname_ex(local_hostname)[2] - def get_routes(): raise NotImplementedError() else: from fcntl import ioctl - def local_ips(): - valid_ips = [network["addr"] for network in get_host_subnets()] - return valid_ips - def get_routes(): # based on scapy implementation for route parsing try: f = open("/proc/net/route", "r") diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index cf15c3cd4..a3e96baca 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -16,10 +16,10 @@ from mongoengine import ( StringField, ) +from common.network.network_utils import get_my_ip_addresses from monkey_island.cc.models.command_control_channel import CommandControlChannel from monkey_island.cc.models.monkey_ttl import MonkeyTtl, create_monkey_ttl_document from monkey_island.cc.server_utils.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS -from monkey_island.cc.server_utils.network_utils import get_ip_addresses class ParentNotFoundError(Exception): @@ -123,7 +123,8 @@ class Monkey(Document): def get_label_by_id(object_id): current_monkey = Monkey.get_single_monkey_by_id(object_id) label = Monkey.get_hostname_by_id(object_id) + " : " + current_monkey.ip_addresses[0] - if len(set(current_monkey.ip_addresses).intersection(get_ip_addresses())) > 0: + local_ips = map(str, get_my_ip_addresses()) + if len(set(current_monkey.ip_addresses).intersection(local_ips)) > 0: label = "MonkeyIsland - " + label return label diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 4f6989e12..084a18d9e 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -27,6 +27,7 @@ from common.event_serializers import ( # noqa: E402 EventSerializerRegistry, register_common_agent_event_serializers, ) +from common.network.network_utils import get_my_ip_addresses # noqa: E402 from common.version import get_version # noqa: E402 from monkey_island.cc.app import init_app # noqa: E402 from monkey_island.cc.arg_parser import IslandCmdArgs # noqa: E402 @@ -37,7 +38,6 @@ from monkey_island.cc.server_utils.consts import ( # noqa: E402 MONKEY_ISLAND_ABS_PATH, ) from monkey_island.cc.server_utils.island_logger import reset_logger, setup_logging # noqa: E402 -from monkey_island.cc.server_utils.network_utils import get_ip_addresses # noqa: E402 from monkey_island.cc.services.initialize import initialize_services # noqa: E402 from monkey_island.cc.setup import island_config_options_validator # noqa: E402 from monkey_island.cc.setup import ( # noqa: E402 @@ -106,7 +106,7 @@ def _configure_logging(config_options): def _collect_system_info() -> Tuple[Sequence[str], Deployment, Version]: deployment = _get_deployment() version = Version(get_version(), deployment) - return (get_ip_addresses(), deployment, version) + return (get_my_ip_addresses(), deployment, version) def _get_deployment() -> Deployment: diff --git a/monkey/monkey_island/cc/server_utils/network_utils.py b/monkey/monkey_island/cc/server_utils/network_utils.py deleted file mode 100644 index 60c397c77..000000000 --- a/monkey/monkey_island/cc/server_utils/network_utils.py +++ /dev/null @@ -1,39 +0,0 @@ -import ipaddress -from typing import Sequence - -from netifaces import AF_INET, ifaddresses, interfaces -from ring import lru - -# TODO: This functionality is duplicated in the agent. Unify them after 2216-tcp-relay is merged - - -# The local IP addresses list should not change often. Therefore, we can cache the result and never -# call this function more than once. This stopgap measure is here since this function is called a -# lot of times during the report generation. This means that if the interfaces of the Island machine -# change, the Island process needs to be restarted. -@lru(maxsize=1) -def get_ip_addresses() -> Sequence[str]: - ip_list = [] - for interface in interfaces(): - addresses = ifaddresses(interface).get(AF_INET, []) - ip_list.extend([link["addr"] for link in addresses if link["addr"] != "127.0.0.1"]) - return ip_list - - -# The subnets list should not change often. Therefore, we can cache the result and never call this -# function more than once. This stopgap measure is here since this function is called a lot of times -# during the report generation. This means that if the interfaces or subnets of the Island machine -# change, the Island process needs to be restarted. -@lru(maxsize=1) -def get_subnets(): - subnets = [] - for interface in interfaces(): - addresses = ifaddresses(interface).get(AF_INET, []) - subnets.extend( - [ - ipaddress.ip_interface(link["addr"] + "/" + link["netmask"]).network - for link in addresses - if link["addr"] != "127.0.0.1" - ] - ) - return subnets diff --git a/monkey/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py index 2a7f0f661..0bacb53b8 100644 --- a/monkey/monkey_island/cc/services/node.py +++ b/monkey/monkey_island/cc/services/node.py @@ -4,10 +4,10 @@ from datetime import datetime from bson import ObjectId import monkey_island.cc.services.log +from common.network.network_utils import get_my_ip_addresses from monkey_island.cc import models from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey -from monkey_island.cc.server_utils.network_utils import get_ip_addresses from monkey_island.cc.services.edge.displayed_edge import DisplayedEdgeService from monkey_island.cc.services.edge.edge import EdgeService from monkey_island.cc.services.utils.node_states import NodeStates @@ -110,7 +110,7 @@ class NodeService: def get_monkey_label(monkey): # todo label = monkey["hostname"] + " : " + monkey["ip_addresses"][0] - ip_addresses = get_ip_addresses() + ip_addresses = get_my_ip_addresses() if len(set(monkey["ip_addresses"]).intersection(ip_addresses)) > 0: label = "MonkeyIsland - " + label return label @@ -118,7 +118,7 @@ class NodeService: @staticmethod def get_monkey_group(monkey): keywords = [] - if len(set(monkey["ip_addresses"]).intersection(get_ip_addresses())) != 0: + if len(set(monkey["ip_addresses"]).intersection(get_my_ip_addresses())) != 0: keywords.extend(["island", "monkey"]) else: monkey_type = "manual" if NodeService.get_monkey_manual_run(monkey) else "monkey" @@ -275,7 +275,7 @@ class NodeService: # It's better to just initialize the island machine on reset I think @staticmethod def get_monkey_island_monkey(): - ip_addresses = get_ip_addresses() + ip_addresses = get_my_ip_addresses() for ip_address in ip_addresses: monkey = NodeService.get_monkey_by_ip(ip_address) if monkey is not None: @@ -297,7 +297,7 @@ class NodeService: @staticmethod def get_monkey_island_node(): island_node = NodeService.get_monkey_island_pseudo_net_node() - island_node["ip_addresses"] = get_ip_addresses() + island_node["ip_addresses"] = get_my_ip_addresses() island_node["domain_name"] = socket.gethostname() return island_node diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 01ce711d1..b80f3c3b6 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -5,12 +5,12 @@ from itertools import chain, product from typing import List from common.network.network_range import NetworkRange +from common.network.network_utils import get_my_ip_addresses, get_network_interfaces from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey from monkey_island.cc.models.report import get_report, save_report from monkey_island.cc.repository import IAgentConfigurationRepository, ICredentialsRepository -from monkey_island.cc.server_utils.network_utils import get_ip_addresses, get_subnets from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.reporting.exploitations.manual_exploitation import get_manual_monkeys from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import ( @@ -175,7 +175,7 @@ class ReportService: @staticmethod def get_island_cross_segment_issues(): issues = [] - island_ips = get_ip_addresses() + island_ips = get_my_ip_addresses() for monkey in mongo.db.monkey.find( {"tunnel": {"$exists": False}}, {"tunnel": 1, "guid": 1, "hostname": 1} ): @@ -194,7 +194,9 @@ class ReportService: "type": "island_cross_segment", "machine": monkey["hostname"], "networks": [str(subnet) for subnet in monkey_subnets], - "server_networks": [str(subnet) for subnet in get_subnets()], + "server_networks": [ + str(interface.network) for interface in get_network_interfaces() + ], } )