Merge pull request #2298 from guardicore/2216-redundant-ip-lookup

Island, Agent: Move local IP and interface retrieval to common
This commit is contained in:
Mike Salvatore 2022-09-16 09:12:26 -04:00 committed by GitHub
commit b02bc0ceb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 52 additions and 83 deletions

View File

@ -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. # TODO: `address_to_port()` should return the port as an integer.

View File

@ -7,8 +7,9 @@ import requests
from urllib3 import disable_warnings from urllib3 import disable_warnings
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT
from common.network.network_utils import get_my_ip_addresses
from infection_monkey.config import GUID 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 from infection_monkey.utils import agent_process
disable_warnings() # noqa DUO131 disable_warnings() # noqa DUO131
@ -38,7 +39,7 @@ class ControlClient:
monkey = { monkey = {
"guid": GUID, "guid": GUID,
"hostname": hostname, "hostname": hostname,
"ip_addresses": local_ips(), "ip_addresses": get_my_ip_addresses(),
"networks": get_host_subnets(), "networks": get_host_subnets(),
"description": " ".join(platform.uname()), "description": " ".join(platform.uname()),
"parent": parent, "parent": parent,

View File

@ -11,8 +11,8 @@ from common import AgentRegistrationData
from common.agent_configuration import AgentConfiguration from common.agent_configuration import AgentConfiguration
from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT
from common.credentials import Credentials 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.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 import agent_process
from infection_monkey.utils.ids import get_agent_id, get_machine_id from infection_monkey.utils.ids import get_agent_id, get_machine_id

View File

@ -15,7 +15,11 @@ from common.event_serializers import (
register_common_agent_event_serializers, register_common_agent_event_serializers,
) )
from common.events import CredentialsStolenEvent 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.argparse_types import positive_int
from common.utils.attack_utils import ScanStatus, UsageEnum from common.utils.attack_utils import ScanStatus, UsageEnum
from common.version import get_version from common.version import get_version
@ -45,7 +49,7 @@ from infection_monkey.master import AutomatedMaster
from infection_monkey.master.control_channel import ControlChannel from infection_monkey.master.control_channel import ControlChannel
from infection_monkey.model import VictimHostFactory from infection_monkey.model import VictimHostFactory
from infection_monkey.network.firewall import app as firewall from infection_monkey.network.firewall import app as firewall
from infection_monkey.network.info import get_free_tcp_port, 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 import TCPRelay
from infection_monkey.network.relay.utils import ( from infection_monkey.network.relay.utils import (
find_server, find_server,
@ -201,7 +205,7 @@ class InfectionMonkey:
self._cmd_island_port, self._cmd_island_port,
client_disconnect_timeout=config.keep_tunnel_open_time, 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): if not maximum_depth_reached(config.propagation.maximum_depth, self._current_depth):
self._relay.start() self._relay.start()
@ -220,7 +224,7 @@ class InfectionMonkey:
return agent_event_serializer_registry return agent_event_serializer_registry
def _build_master(self, relay_servers: List[str]): 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 # TODO control_channel and control_client have same responsibilities, merge them
propagation_credentials_repository = AggregatingPropagationCredentialsRepository( propagation_credentials_repository = AggregatingPropagationCredentialsRepository(
@ -235,7 +239,7 @@ class InfectionMonkey:
self._agent_event_serializer_registry, 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) 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 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( def _build_puppet(
self, self,
propagation_credentials_repository: IPropagationCredentialsRepository,
event_queue: IAgentEventQueue, event_queue: IAgentEventQueue,
) -> IPuppet: ) -> IPuppet:
puppet = Puppet() puppet = Puppet()

View File

@ -2,10 +2,9 @@ import itertools
import socket import socket
import struct import struct
from dataclasses import dataclass from dataclasses import dataclass
from ipaddress import IPv4Interface
from random import shuffle # noqa: DUO102 from random import shuffle # noqa: DUO102
from threading import Lock from threading import Lock
from typing import Dict, List, Set from typing import Dict, Set
import netifaces import netifaces
import psutil import psutil
@ -29,10 +28,6 @@ class NetworkAddress:
domain: str 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(): def get_host_subnets():
""" """
Returns a list of subnets visible to host (omitting loopback and auto conf networks) 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(): if is_windows_os():
def local_ips():
local_hostname = socket.gethostname()
return socket.gethostbyname_ex(local_hostname)[2]
def get_routes(): def get_routes():
raise NotImplementedError() raise NotImplementedError()
else: else:
from fcntl import ioctl 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 def get_routes(): # based on scapy implementation for route parsing
try: try:
f = open("/proc/net/route", "r") f = open("/proc/net/route", "r")

View File

@ -16,10 +16,10 @@ from mongoengine import (
StringField, 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.command_control_channel import CommandControlChannel
from monkey_island.cc.models.monkey_ttl import MonkeyTtl, create_monkey_ttl_document 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.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS
from monkey_island.cc.server_utils.network_utils import get_ip_addresses
class ParentNotFoundError(Exception): class ParentNotFoundError(Exception):
@ -123,7 +123,8 @@ class Monkey(Document):
def get_label_by_id(object_id): def get_label_by_id(object_id):
current_monkey = Monkey.get_single_monkey_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] 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 label = "MonkeyIsland - " + label
return label return label

View File

@ -27,6 +27,7 @@ from common.event_serializers import ( # noqa: E402
EventSerializerRegistry, EventSerializerRegistry,
register_common_agent_event_serializers, 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 common.version import get_version # noqa: E402
from monkey_island.cc.app import init_app # noqa: E402 from monkey_island.cc.app import init_app # noqa: E402
from monkey_island.cc.arg_parser import IslandCmdArgs # 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, 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.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.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 island_config_options_validator # noqa: E402
from monkey_island.cc.setup import ( # 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]: def _collect_system_info() -> Tuple[Sequence[str], Deployment, Version]:
deployment = _get_deployment() deployment = _get_deployment()
version = Version(get_version(), deployment) version = Version(get_version(), deployment)
return (get_ip_addresses(), deployment, version) return (get_my_ip_addresses(), deployment, version)
def _get_deployment() -> Deployment: def _get_deployment() -> Deployment:

View File

@ -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

View File

@ -4,10 +4,10 @@ from datetime import datetime
from bson import ObjectId from bson import ObjectId
import monkey_island.cc.services.log 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 import models
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.models import Monkey 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.displayed_edge import DisplayedEdgeService
from monkey_island.cc.services.edge.edge import EdgeService from monkey_island.cc.services.edge.edge import EdgeService
from monkey_island.cc.services.utils.node_states import NodeStates from monkey_island.cc.services.utils.node_states import NodeStates
@ -110,7 +110,7 @@ class NodeService:
def get_monkey_label(monkey): def get_monkey_label(monkey):
# todo # todo
label = monkey["hostname"] + " : " + monkey["ip_addresses"][0] 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: if len(set(monkey["ip_addresses"]).intersection(ip_addresses)) > 0:
label = "MonkeyIsland - " + label label = "MonkeyIsland - " + label
return label return label
@ -118,7 +118,7 @@ class NodeService:
@staticmethod @staticmethod
def get_monkey_group(monkey): def get_monkey_group(monkey):
keywords = [] 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"]) keywords.extend(["island", "monkey"])
else: else:
monkey_type = "manual" if NodeService.get_monkey_manual_run(monkey) else "monkey" 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 # It's better to just initialize the island machine on reset I think
@staticmethod @staticmethod
def get_monkey_island_monkey(): def get_monkey_island_monkey():
ip_addresses = get_ip_addresses() ip_addresses = get_my_ip_addresses()
for ip_address in ip_addresses: for ip_address in ip_addresses:
monkey = NodeService.get_monkey_by_ip(ip_address) monkey = NodeService.get_monkey_by_ip(ip_address)
if monkey is not None: if monkey is not None:
@ -297,7 +297,7 @@ class NodeService:
@staticmethod @staticmethod
def get_monkey_island_node(): def get_monkey_island_node():
island_node = NodeService.get_monkey_island_pseudo_net_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() island_node["domain_name"] = socket.gethostname()
return island_node return island_node

View File

@ -5,12 +5,12 @@ from itertools import chain, product
from typing import List from typing import List
from common.network.network_range import NetworkRange 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 common.network.segmentation_utils import get_ip_in_src_and_not_in_dst
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.models import Monkey from monkey_island.cc.models import Monkey
from monkey_island.cc.models.report import get_report, save_report from monkey_island.cc.models.report import get_report, save_report
from monkey_island.cc.repository import IAgentConfigurationRepository, ICredentialsRepository 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.node import NodeService
from monkey_island.cc.services.reporting.exploitations.manual_exploitation import get_manual_monkeys from monkey_island.cc.services.reporting.exploitations.manual_exploitation import get_manual_monkeys
from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import ( from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import (
@ -175,7 +175,7 @@ class ReportService:
@staticmethod @staticmethod
def get_island_cross_segment_issues(): def get_island_cross_segment_issues():
issues = [] issues = []
island_ips = get_ip_addresses() island_ips = get_my_ip_addresses()
for monkey in mongo.db.monkey.find( for monkey in mongo.db.monkey.find(
{"tunnel": {"$exists": False}}, {"tunnel": 1, "guid": 1, "hostname": 1} {"tunnel": {"$exists": False}}, {"tunnel": 1, "guid": 1, "hostname": 1}
): ):
@ -194,7 +194,9 @@ class ReportService:
"type": "island_cross_segment", "type": "island_cross_segment",
"machine": monkey["hostname"], "machine": monkey["hostname"],
"networks": [str(subnet) for subnet in monkey_subnets], "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()
],
} }
) )