Skip to content

Commit fd36aca

Browse files
authored
Merge pull request #2017 from guardicore/1996-island-worm-config-decouple
1996 island worm config decouple
2 parents ac172dc + 10f069d commit fd36aca

File tree

15 files changed

+46
-92
lines changed

15 files changed

+46
-92
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
1010
- credentials.json file for storing Monkey Island user login information. #1206
1111
- "GET /api/propagation-credentials/<string:guid>" endpoint for agents to
1212
retrieve updated credentials from the Island. #1538
13+
- "GET /api/island/ip-addresses" endpoint to get IP addresses of the Island server
14+
network interfaces. #1996
1315
- SSHCollector as a configurable System info Collector. #1606
1416
- deployment_scrips/install-infection-monkey-service.sh to install an AppImage
1517
as a service. #1552
@@ -36,6 +38,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
3638
- Update MongoDB version to 4.4.x. #1924
3739
- Endpoint to get agent binaries from "/api/agent/download/<string:os>" to
3840
"/api/agent-binaries/<string:os>". #1978
41+
- Agent configuration structure. #1996, #1998, #1961, #1997, #1994, #1741, #1761, #1695, #1605
3942

4043
### Removed
4144
- VSFTPD exploiter. #1533

monkey/common/config_value_paths.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
CURRENT_SERVER_PATH = ["internal", "island_server", "current_server"]
21
SSH_KEYS_PATH = ["internal", "exploits", "exploit_ssh_keys"]
32
INACCESSIBLE_SUBNETS_PATH = ["basic_network", "network_analysis", "inaccessible_subnets"]
43
USER_LIST_PATH = ["basic", "credentials", "exploit_user_list"]

monkey/monkey_island/cc/app.py

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from monkey_island.cc.resources.edge import Edge
2828
from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation
2929
from monkey_island.cc.resources.exploitations.monkey_exploitation import MonkeyExploitation
30+
from monkey_island.cc.resources.ip_addresses import IpAddresses
3031
from monkey_island.cc.resources.island_configuration import IslandConfiguration
3132
from monkey_island.cc.resources.island_logs import IslandLog
3233
from monkey_island.cc.resources.island_mode import IslandMode
@@ -171,6 +172,7 @@ def init_api_resources(api: FlaskDIWrapper):
171172
api.add_resource(TelemetryFeed)
172173
api.add_resource(Log)
173174
api.add_resource(IslandLog)
175+
api.add_resource(IpAddresses)
174176

175177
# API Spec: These two should be the same resource, GET for download and POST for upload
176178
api.add_resource(PBAFileDownload)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import Mapping, Sequence
2+
3+
from monkey_island.cc.resources.AbstractResource import AbstractResource
4+
from monkey_island.cc.resources.request_authentication import jwt_required
5+
from monkey_island.cc.services.utils.network_utils import local_ip_addresses
6+
7+
8+
class IpAddresses(AbstractResource):
9+
urls = ["/api/island/ip-addresses"]
10+
11+
@jwt_required
12+
def get(self) -> Mapping[str, Sequence[str]]:
13+
"""
14+
Gets the IP addresses of the Island network interfaces
15+
16+
:return: a dictionary with "ip_addresses" key that points to a list of IP's
17+
"""
18+
local_ips = local_ip_addresses()
19+
20+
return {"ip_addresses": local_ips}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
from common.config_value_paths import CURRENT_SERVER_PATH
1+
from typing import Sequence
2+
3+
from common.network.network_utils import address_to_ip_port
24
from common.utils.attack_utils import ScanStatus
5+
from monkey_island.cc.models.telemetries.telemetry import Telemetry
6+
from monkey_island.cc.server_utils.consts import ISLAND_PORT
37
from monkey_island.cc.services.attack.technique_reports import AttackTechnique
4-
from monkey_island.cc.services.config import ConfigService
58

69

710
class T1065(AttackTechnique):
@@ -10,10 +13,16 @@ class T1065(AttackTechnique):
1013
unscanned_msg = ""
1114
scanned_msg = ""
1215
used_msg = ""
13-
message = "Monkey used port %s to communicate to C2 server."
16+
message = "Monkey used ports %s to communicate to C2 server."
1417

1518
@staticmethod
1619
def get_report_data():
17-
port = ConfigService.get_config_value(CURRENT_SERVER_PATH).split(":")[1]
18-
T1065.used_msg = T1065.message % port
20+
tunneling_ports = T1065.get_tunnel_ports()
21+
non_standard_ports = [*tunneling_ports, str(ISLAND_PORT)]
22+
T1065.used_msg = T1065.message % ", ".join(non_standard_ports)
1923
return T1065.get_base_data_by_status(ScanStatus.USED.value)
24+
25+
@staticmethod
26+
def get_tunnel_ports() -> Sequence[str]:
27+
telems = Telemetry.objects(telem_category="tunnel", data__proxy__ne=None)
28+
return [address_to_ip_port(telem["data"]["proxy"])[1] for telem in telems]

monkey/monkey_island/cc/services/config.py

-16
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
USER_LIST_PATH,
1919
)
2020
from monkey_island.cc.database import mongo
21-
from monkey_island.cc.server_utils.consts import ISLAND_PORT
2221
from monkey_island.cc.server_utils.encryption import (
2322
SensitiveField,
2423
StringEncryptor,
@@ -30,7 +29,6 @@
3029
from monkey_island.cc.services.config_schema.config_schema import SCHEMA
3130
from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode
3231
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
33-
from monkey_island.cc.services.utils.network_utils import local_ip_addresses
3432

3533
logger = logging.getLogger(__name__)
3634

@@ -255,25 +253,13 @@ def init_config():
255253
def reset_config():
256254
PostBreachFilesService.remove_PBA_files()
257255
config = ConfigService.get_default_config(True)
258-
ConfigService.set_server_ips_in_config(config)
259256
try:
260257
mode = get_mode()
261258
update_config_per_mode(mode, config, should_encrypt=False)
262259
except ModeNotSetError:
263260
ConfigService.update_config(config, should_encrypt=False)
264261
logger.info("Monkey config reset was called")
265262

266-
@staticmethod
267-
def set_server_ips_in_config(config):
268-
ips = local_ip_addresses()
269-
config["internal"]["island_server"]["command_servers"] = [
270-
"%s:%d" % (ip, ISLAND_PORT) for ip in ips
271-
]
272-
config["internal"]["island_server"]["current_server"] = "%s:%d" % (
273-
ips[0],
274-
ISLAND_PORT,
275-
)
276-
277263
@staticmethod
278264
def _extend_config_with_default(validator_class):
279265
validate_properties = validator_class.VALIDATORS["properties"]
@@ -407,8 +393,6 @@ def _format_pbas_from_flat_config(config: Dict):
407393
"linux_filename": config.get(flat_linux_filename_field, ""),
408394
"windows_command": config.get(flat_windows_command_field, ""),
409395
"windows_filename": config.get(flat_windows_filename_field, ""),
410-
# Current server is used for attack telemetry
411-
"current_server": config.get("current_server"),
412396
}
413397

414398
config["post_breach_actions"] = formatted_pbas_config

monkey/monkey_island/cc/services/config_schema/internal.py

-22
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,6 @@
1515
},
1616
},
1717
},
18-
"island_server": {
19-
"title": "Island server",
20-
"type": "object",
21-
"properties": {
22-
"command_servers": {
23-
"title": "Island server's IP's",
24-
"type": "array",
25-
"uniqueItems": True,
26-
"items": {"type": "string"},
27-
"default": ["192.0.2.0:5000"],
28-
"description": "List of command servers/network interfaces to try to "
29-
"communicate with "
30-
"(format is <ip>:<port>)",
31-
},
32-
"current_server": {
33-
"title": "Current server",
34-
"type": "string",
35-
"default": "192.0.2.0:5000",
36-
"description": "The current command server the monkey is communicating with",
37-
},
38-
},
39-
},
4018
"network": {
4119
"title": "Network",
4220
"type": "object",

monkey/monkey_island/cc/services/utils/network_utils.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import socket
44
import struct
55
import sys
6+
from typing import Sequence
67

78
from netifaces import AF_INET, ifaddresses, interfaces
89
from ring import lru
@@ -60,7 +61,7 @@ def local_ips():
6061
# This means that if the interfaces of the Island machine change, the Island process needs to be
6162
# restarted.
6263
@lru(maxsize=1)
63-
def local_ip_addresses():
64+
def local_ip_addresses() -> Sequence[str]:
6465
ip_list = []
6566
for interface in interfaces():
6667
addresses = ifaddresses(interface).get(AF_INET, [])

monkey/monkey_island/cc/ui/src/components/configuration-components/InternalConfig.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ import {Nav} from 'react-bootstrap';
44

55
const sectionOrder = [
66
'network',
7-
'island_server',
87
'exploits',
98
'classes',
10-
'general',
11-
'testing'
9+
'general'
1210
];
1311
const initialSection = sectionOrder[0];
1412

monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {faExpandArrowsAlt} from '@fortawesome/free-solid-svg-icons';
99
import RunOnIslandButton from './RunOnIslandButton';
1010
import AWSRunButton from './RunOnAWS/AWSRunButton';
1111

12-
const CONFIG_URL = '/api/configuration/island';
12+
const IP_ADDRESSES_URL = '/api/island/ip-addresses';
1313

1414
function RunOptions(props) {
1515

@@ -21,13 +21,10 @@ function RunOptions(props) {
2121

2222
useEffect(() => {
2323
if (initialized === false) {
24-
authComponent.authFetch(CONFIG_URL)
24+
authComponent.authFetch(IP_ADDRESSES_URL)
2525
.then(res => res.json())
2626
.then(res => {
27-
let commandServers = res.configuration.internal.island_server.command_servers;
28-
let ipAddresses = commandServers.map(ip => {
29-
return ip.split(':', 1);
30-
});
27+
let ipAddresses = res.ip_addresses;
3128
setIps(ipAddresses);
3229
setInitialized(true);
3330
});

monkey/tests/data_for_tests/monkey_configs/automated_master_config.json

-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@
6262
},
6363
"PBA_linux_filename": "",
6464
"PBA_windows_filename": "",
65-
"command_servers": ["10.197.94.72:5000"],
66-
"current_server": "localhost:5000",
6765
"custom_pbas": {
6866
"linux_command": "",
6967
"windows_command": ""

monkey/tests/data_for_tests/monkey_configs/flat_config.json

-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111
"PBA_windows_filename": "test.ps1",
1212
"alive": true,
1313
"blocked_ips": ["192.168.1.1", "192.168.1.100"],
14-
"command_servers": [
15-
"10.197.94.72:5000"
16-
],
17-
"current_server": "10.197.94.72:5000",
1814
"custom_PBA_linux_cmd": "bash test.sh",
1915
"custom_PBA_windows_cmd": "powershell test.ps1",
2016
"depth": 2,

monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json

-8
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,6 @@
4141
"general": {
4242
"keep_tunnel_open_time": 60
4343
},
44-
"island_server": {
45-
"command_servers": [
46-
"192.168.1.37:5000",
47-
"10.0.3.1:5000",
48-
"172.17.0.1:5000"
49-
],
50-
"current_server": "192.168.1.37:5000"
51-
},
5244
"network": {
5345
"tcp_scanner": {
5446
"HTTP_PORTS": [

monkey/tests/unit_tests/monkey_island/cc/services/conftest.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ def PORT():
1414

1515

1616
@pytest.fixture
17-
def config(monkeypatch, IPS, PORT):
18-
monkeypatch.setattr("monkey_island.cc.services.config.local_ip_addresses", lambda: IPS)
17+
def config(monkeypatch):
1918
config = ConfigService.get_default_config(True)
2019
return config
2120

monkey/tests/unit_tests/monkey_island/cc/services/test_config.py

-22
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,13 @@
66
# monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
77

88

9-
@pytest.fixture(scope="function", autouse=True)
10-
def mock_port(monkeypatch, PORT):
11-
monkeypatch.setattr("monkey_island.cc.services.config.ISLAND_PORT", PORT)
12-
13-
149
@pytest.fixture(autouse=True)
1510
def mock_flat_config(monkeypatch, flat_monkey_config):
1611
monkeypatch.setattr(
1712
"monkey_island.cc.services.config.ConfigService.get_flat_config", lambda: flat_monkey_config
1813
)
1914

2015

21-
@pytest.mark.slow
22-
@pytest.mark.usefixtures("uses_encryptor")
23-
def test_set_server_ips_in_config_command_servers(config, IPS, PORT):
24-
ConfigService.set_server_ips_in_config(config)
25-
expected_config_command_servers = [f"{ip}:{PORT}" for ip in IPS]
26-
assert config["internal"]["island_server"]["command_servers"] == expected_config_command_servers
27-
28-
29-
@pytest.mark.slow
30-
@pytest.mark.usefixtures("uses_encryptor")
31-
def test_set_server_ips_in_config_current_server(config, IPS, PORT):
32-
ConfigService.set_server_ips_in_config(config)
33-
expected_config_current_server = f"{IPS[0]}:{PORT}"
34-
assert config["internal"]["island_server"]["current_server"] == expected_config_current_server
35-
36-
3716
def test_format_config_for_agent__credentials_removed():
3817
flat_monkey_config = ConfigService.format_flat_config_for_agent()
3918

@@ -91,7 +70,6 @@ def test_format_config_for_custom_pbas():
9170
"windows_command": "powershell test.ps1",
9271
"linux_filename": "test.sh",
9372
"windows_filename": "test.ps1",
94-
"current_server": "10.197.94.72:5000",
9573
}
9674
flat_monkey_config = ConfigService.format_flat_config_for_agent()
9775

0 commit comments

Comments
 (0)