diff --git a/CHANGELOG.md b/CHANGELOG.md index e01d64d49..5fd696565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - credentials.json file for storing Monkey Island user login information. #1206 - "GET /api/propagation-credentials/" endpoint for agents to retrieve updated credentials from the Island. #1538 +- "GET /api/island/ip-addresses" endpoint to get IP addresses of the Island server + network interfaces. #1996 - SSHCollector as a configurable System info Collector. #1606 - deployment_scrips/install-infection-monkey-service.sh to install an AppImage as a service. #1552 @@ -36,6 +38,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Update MongoDB version to 4.4.x. #1924 - Endpoint to get agent binaries from "/api/agent/download/" to "/api/agent-binaries/". #1978 +- Agent configuration structure. #1996, #1998, #1961, #1997, #1994, #1741, #1761, #1695, #1605 ### Removed - VSFTPD exploiter. #1533 diff --git a/monkey/common/config_value_paths.py b/monkey/common/config_value_paths.py index e65444147..c6bcf0dc0 100644 --- a/monkey/common/config_value_paths.py +++ b/monkey/common/config_value_paths.py @@ -1,4 +1,3 @@ -CURRENT_SERVER_PATH = ["internal", "island_server", "current_server"] SSH_KEYS_PATH = ["internal", "exploits", "exploit_ssh_keys"] INACCESSIBLE_SUBNETS_PATH = ["basic_network", "network_analysis", "inaccessible_subnets"] USER_LIST_PATH = ["basic", "credentials", "exploit_user_list"] diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 46dd8543a..9db06d486 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -27,6 +27,7 @@ from monkey_island.cc.resources.configuration_import import ConfigurationImport from monkey_island.cc.resources.edge import Edge from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation from monkey_island.cc.resources.exploitations.monkey_exploitation import MonkeyExploitation +from monkey_island.cc.resources.ip_addresses import IpAddresses from monkey_island.cc.resources.island_configuration import IslandConfiguration from monkey_island.cc.resources.island_logs import IslandLog from monkey_island.cc.resources.island_mode import IslandMode @@ -171,6 +172,7 @@ def init_api_resources(api: FlaskDIWrapper): api.add_resource(TelemetryFeed) api.add_resource(Log) api.add_resource(IslandLog) + api.add_resource(IpAddresses) # API Spec: These two should be the same resource, GET for download and POST for upload api.add_resource(PBAFileDownload) diff --git a/monkey/monkey_island/cc/resources/ip_addresses.py b/monkey/monkey_island/cc/resources/ip_addresses.py new file mode 100644 index 000000000..ef0e0fa4b --- /dev/null +++ b/monkey/monkey_island/cc/resources/ip_addresses.py @@ -0,0 +1,20 @@ +from typing import Mapping, Sequence + +from monkey_island.cc.resources.AbstractResource import AbstractResource +from monkey_island.cc.resources.request_authentication import jwt_required +from monkey_island.cc.services.utils.network_utils import local_ip_addresses + + +class IpAddresses(AbstractResource): + urls = ["/api/island/ip-addresses"] + + @jwt_required + def get(self) -> Mapping[str, Sequence[str]]: + """ + Gets the IP addresses of the Island network interfaces + + :return: a dictionary with "ip_addresses" key that points to a list of IP's + """ + local_ips = local_ip_addresses() + + return {"ip_addresses": local_ips} diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py index 7615a46c2..d28c5e9e0 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py @@ -1,7 +1,10 @@ -from common.config_value_paths import CURRENT_SERVER_PATH +from typing import Sequence + +from common.network.network_utils import address_to_ip_port from common.utils.attack_utils import ScanStatus +from monkey_island.cc.models.telemetries.telemetry import Telemetry +from monkey_island.cc.server_utils.consts import ISLAND_PORT from monkey_island.cc.services.attack.technique_reports import AttackTechnique -from monkey_island.cc.services.config import ConfigService class T1065(AttackTechnique): @@ -10,10 +13,16 @@ class T1065(AttackTechnique): unscanned_msg = "" scanned_msg = "" used_msg = "" - message = "Monkey used port %s to communicate to C2 server." + message = "Monkey used ports %s to communicate to C2 server." @staticmethod def get_report_data(): - port = ConfigService.get_config_value(CURRENT_SERVER_PATH).split(":")[1] - T1065.used_msg = T1065.message % port + tunneling_ports = T1065.get_tunnel_ports() + non_standard_ports = [*tunneling_ports, str(ISLAND_PORT)] + T1065.used_msg = T1065.message % ", ".join(non_standard_ports) return T1065.get_base_data_by_status(ScanStatus.USED.value) + + @staticmethod + def get_tunnel_ports() -> Sequence[str]: + telems = Telemetry.objects(telem_category="tunnel", data__proxy__ne=None) + return [address_to_ip_port(telem["data"]["proxy"])[1] for telem in telems] diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index 2220bf38b..46bdaf91c 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -18,7 +18,6 @@ from common.config_value_paths import ( USER_LIST_PATH, ) from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.consts import ISLAND_PORT from monkey_island.cc.server_utils.encryption import ( SensitiveField, StringEncryptor, @@ -30,7 +29,6 @@ from monkey_island.cc.services.config_manipulator import update_config_per_mode from monkey_island.cc.services.config_schema.config_schema import SCHEMA from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode from monkey_island.cc.services.post_breach_files import PostBreachFilesService -from monkey_island.cc.services.utils.network_utils import local_ip_addresses logger = logging.getLogger(__name__) @@ -255,7 +253,6 @@ class ConfigService: def reset_config(): PostBreachFilesService.remove_PBA_files() config = ConfigService.get_default_config(True) - ConfigService.set_server_ips_in_config(config) try: mode = get_mode() update_config_per_mode(mode, config, should_encrypt=False) @@ -263,17 +260,6 @@ class ConfigService: ConfigService.update_config(config, should_encrypt=False) logger.info("Monkey config reset was called") - @staticmethod - def set_server_ips_in_config(config): - ips = local_ip_addresses() - config["internal"]["island_server"]["command_servers"] = [ - "%s:%d" % (ip, ISLAND_PORT) for ip in ips - ] - config["internal"]["island_server"]["current_server"] = "%s:%d" % ( - ips[0], - ISLAND_PORT, - ) - @staticmethod def _extend_config_with_default(validator_class): validate_properties = validator_class.VALIDATORS["properties"] @@ -407,8 +393,6 @@ class ConfigService: "linux_filename": config.get(flat_linux_filename_field, ""), "windows_command": config.get(flat_windows_command_field, ""), "windows_filename": config.get(flat_windows_filename_field, ""), - # Current server is used for attack telemetry - "current_server": config.get("current_server"), } config["post_breach_actions"] = formatted_pbas_config diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py index b54370ac9..e0575ab36 100644 --- a/monkey/monkey_island/cc/services/config_schema/internal.py +++ b/monkey/monkey_island/cc/services/config_schema/internal.py @@ -15,28 +15,6 @@ INTERNAL = { }, }, }, - "island_server": { - "title": "Island server", - "type": "object", - "properties": { - "command_servers": { - "title": "Island server's IP's", - "type": "array", - "uniqueItems": True, - "items": {"type": "string"}, - "default": ["192.0.2.0:5000"], - "description": "List of command servers/network interfaces to try to " - "communicate with " - "(format is :)", - }, - "current_server": { - "title": "Current server", - "type": "string", - "default": "192.0.2.0:5000", - "description": "The current command server the monkey is communicating with", - }, - }, - }, "network": { "title": "Network", "type": "object", diff --git a/monkey/monkey_island/cc/services/utils/network_utils.py b/monkey/monkey_island/cc/services/utils/network_utils.py index 3aa204ee3..bdd2cc404 100644 --- a/monkey/monkey_island/cc/services/utils/network_utils.py +++ b/monkey/monkey_island/cc/services/utils/network_utils.py @@ -3,6 +3,7 @@ import ipaddress import socket import struct import sys +from typing import Sequence from netifaces import AF_INET, ifaddresses, interfaces from ring import lru @@ -60,7 +61,7 @@ else: # This means that if the interfaces of the Island machine change, the Island process needs to be # restarted. @lru(maxsize=1) -def local_ip_addresses(): +def local_ip_addresses() -> Sequence[str]: ip_list = [] for interface in interfaces(): addresses = ifaddresses(interface).get(AF_INET, []) diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/InternalConfig.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/InternalConfig.js index 89632d926..c14b777f1 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/InternalConfig.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/InternalConfig.js @@ -4,11 +4,9 @@ import {Nav} from 'react-bootstrap'; const sectionOrder = [ 'network', - 'island_server', 'exploits', 'classes', - 'general', - 'testing' + 'general' ]; const initialSection = sectionOrder[0]; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js index bbefb64ac..99f7f92b7 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js @@ -9,7 +9,7 @@ import {faExpandArrowsAlt} from '@fortawesome/free-solid-svg-icons'; import RunOnIslandButton from './RunOnIslandButton'; import AWSRunButton from './RunOnAWS/AWSRunButton'; -const CONFIG_URL = '/api/configuration/island'; +const IP_ADDRESSES_URL = '/api/island/ip-addresses'; function RunOptions(props) { @@ -21,13 +21,10 @@ function RunOptions(props) { useEffect(() => { if (initialized === false) { - authComponent.authFetch(CONFIG_URL) + authComponent.authFetch(IP_ADDRESSES_URL) .then(res => res.json()) .then(res => { - let commandServers = res.configuration.internal.island_server.command_servers; - let ipAddresses = commandServers.map(ip => { - return ip.split(':', 1); - }); + let ipAddresses = res.ip_addresses; setIps(ipAddresses); setInitialized(true); }); diff --git a/monkey/tests/data_for_tests/monkey_configs/automated_master_config.json b/monkey/tests/data_for_tests/monkey_configs/automated_master_config.json index 0a98d0cbb..7fcc2285d 100644 --- a/monkey/tests/data_for_tests/monkey_configs/automated_master_config.json +++ b/monkey/tests/data_for_tests/monkey_configs/automated_master_config.json @@ -62,8 +62,6 @@ }, "PBA_linux_filename": "", "PBA_windows_filename": "", - "command_servers": ["10.197.94.72:5000"], - "current_server": "localhost:5000", "custom_pbas": { "linux_command": "", "windows_command": "" diff --git a/monkey/tests/data_for_tests/monkey_configs/flat_config.json b/monkey/tests/data_for_tests/monkey_configs/flat_config.json index 59f8602ed..33bf50da1 100644 --- a/monkey/tests/data_for_tests/monkey_configs/flat_config.json +++ b/monkey/tests/data_for_tests/monkey_configs/flat_config.json @@ -11,10 +11,6 @@ "PBA_windows_filename": "test.ps1", "alive": true, "blocked_ips": ["192.168.1.1", "192.168.1.100"], - "command_servers": [ - "10.197.94.72:5000" - ], - "current_server": "10.197.94.72:5000", "custom_PBA_linux_cmd": "bash test.sh", "custom_PBA_windows_cmd": "powershell test.ps1", "depth": 2, diff --git a/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json b/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json index 0abd3924b..678023ebb 100644 --- a/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json +++ b/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json @@ -41,14 +41,6 @@ "general": { "keep_tunnel_open_time": 60 }, - "island_server": { - "command_servers": [ - "192.168.1.37:5000", - "10.0.3.1:5000", - "172.17.0.1:5000" - ], - "current_server": "192.168.1.37:5000" - }, "network": { "tcp_scanner": { "HTTP_PORTS": [ diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py index 213dbaefc..aaf3c0abe 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py @@ -14,8 +14,7 @@ def PORT(): @pytest.fixture -def config(monkeypatch, IPS, PORT): - monkeypatch.setattr("monkey_island.cc.services.config.local_ip_addresses", lambda: IPS) +def config(monkeypatch): config = ConfigService.get_default_config(True) return config diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py index bc804c68a..9e01b8365 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py @@ -6,11 +6,6 @@ from monkey_island.cc.services.config import ConfigService # monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js -@pytest.fixture(scope="function", autouse=True) -def mock_port(monkeypatch, PORT): - monkeypatch.setattr("monkey_island.cc.services.config.ISLAND_PORT", PORT) - - @pytest.fixture(autouse=True) def mock_flat_config(monkeypatch, flat_monkey_config): monkeypatch.setattr( @@ -18,22 +13,6 @@ def mock_flat_config(monkeypatch, flat_monkey_config): ) -@pytest.mark.slow -@pytest.mark.usefixtures("uses_encryptor") -def test_set_server_ips_in_config_command_servers(config, IPS, PORT): - ConfigService.set_server_ips_in_config(config) - expected_config_command_servers = [f"{ip}:{PORT}" for ip in IPS] - assert config["internal"]["island_server"]["command_servers"] == expected_config_command_servers - - -@pytest.mark.slow -@pytest.mark.usefixtures("uses_encryptor") -def test_set_server_ips_in_config_current_server(config, IPS, PORT): - ConfigService.set_server_ips_in_config(config) - expected_config_current_server = f"{IPS[0]}:{PORT}" - assert config["internal"]["island_server"]["current_server"] == expected_config_current_server - - def test_format_config_for_agent__credentials_removed(): flat_monkey_config = ConfigService.format_flat_config_for_agent() @@ -91,7 +70,6 @@ def test_format_config_for_custom_pbas(): "windows_command": "powershell test.ps1", "linux_filename": "test.sh", "windows_filename": "test.ps1", - "current_server": "10.197.94.72:5000", } flat_monkey_config = ConfigService.format_flat_config_for_agent()