forked from p34709852/monkey
Merge pull request #2017 from guardicore/1996-island-worm-config-decouple
1996 island worm config decouple
This commit is contained in:
commit
fd36acab3a
|
@ -10,6 +10,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- credentials.json file for storing Monkey Island user login information. #1206
|
- credentials.json file for storing Monkey Island user login information. #1206
|
||||||
- "GET /api/propagation-credentials/<string:guid>" endpoint for agents to
|
- "GET /api/propagation-credentials/<string:guid>" endpoint for agents to
|
||||||
retrieve updated credentials from the Island. #1538
|
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
|
- SSHCollector as a configurable System info Collector. #1606
|
||||||
- deployment_scrips/install-infection-monkey-service.sh to install an AppImage
|
- deployment_scrips/install-infection-monkey-service.sh to install an AppImage
|
||||||
as a service. #1552
|
as a service. #1552
|
||||||
|
@ -36,6 +38,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Update MongoDB version to 4.4.x. #1924
|
- Update MongoDB version to 4.4.x. #1924
|
||||||
- Endpoint to get agent binaries from "/api/agent/download/<string:os>" to
|
- Endpoint to get agent binaries from "/api/agent/download/<string:os>" to
|
||||||
"/api/agent-binaries/<string:os>". #1978
|
"/api/agent-binaries/<string:os>". #1978
|
||||||
|
- Agent configuration structure. #1996, #1998, #1961, #1997, #1994, #1741, #1761, #1695, #1605
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- VSFTPD exploiter. #1533
|
- VSFTPD exploiter. #1533
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
CURRENT_SERVER_PATH = ["internal", "island_server", "current_server"]
|
|
||||||
SSH_KEYS_PATH = ["internal", "exploits", "exploit_ssh_keys"]
|
SSH_KEYS_PATH = ["internal", "exploits", "exploit_ssh_keys"]
|
||||||
INACCESSIBLE_SUBNETS_PATH = ["basic_network", "network_analysis", "inaccessible_subnets"]
|
INACCESSIBLE_SUBNETS_PATH = ["basic_network", "network_analysis", "inaccessible_subnets"]
|
||||||
USER_LIST_PATH = ["basic", "credentials", "exploit_user_list"]
|
USER_LIST_PATH = ["basic", "credentials", "exploit_user_list"]
|
||||||
|
|
|
@ -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.edge import Edge
|
||||||
from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation
|
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.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_configuration import IslandConfiguration
|
||||||
from monkey_island.cc.resources.island_logs import IslandLog
|
from monkey_island.cc.resources.island_logs import IslandLog
|
||||||
from monkey_island.cc.resources.island_mode import IslandMode
|
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(TelemetryFeed)
|
||||||
api.add_resource(Log)
|
api.add_resource(Log)
|
||||||
api.add_resource(IslandLog)
|
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 Spec: These two should be the same resource, GET for download and POST for upload
|
||||||
api.add_resource(PBAFileDownload)
|
api.add_resource(PBAFileDownload)
|
||||||
|
|
|
@ -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}
|
|
@ -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 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.attack.technique_reports import AttackTechnique
|
||||||
from monkey_island.cc.services.config import ConfigService
|
|
||||||
|
|
||||||
|
|
||||||
class T1065(AttackTechnique):
|
class T1065(AttackTechnique):
|
||||||
|
@ -10,10 +13,16 @@ class T1065(AttackTechnique):
|
||||||
unscanned_msg = ""
|
unscanned_msg = ""
|
||||||
scanned_msg = ""
|
scanned_msg = ""
|
||||||
used_msg = ""
|
used_msg = ""
|
||||||
message = "Monkey used port %s to communicate to C2 server."
|
message = "Monkey used ports %s to communicate to C2 server."
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_report_data():
|
def get_report_data():
|
||||||
port = ConfigService.get_config_value(CURRENT_SERVER_PATH).split(":")[1]
|
tunneling_ports = T1065.get_tunnel_ports()
|
||||||
T1065.used_msg = T1065.message % port
|
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)
|
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]
|
||||||
|
|
|
@ -18,7 +18,6 @@ from common.config_value_paths import (
|
||||||
USER_LIST_PATH,
|
USER_LIST_PATH,
|
||||||
)
|
)
|
||||||
from monkey_island.cc.database import mongo
|
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 (
|
from monkey_island.cc.server_utils.encryption import (
|
||||||
SensitiveField,
|
SensitiveField,
|
||||||
StringEncryptor,
|
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.config_schema.config_schema import SCHEMA
|
||||||
from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode
|
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.post_breach_files import PostBreachFilesService
|
||||||
from monkey_island.cc.services.utils.network_utils import local_ip_addresses
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -255,7 +253,6 @@ class ConfigService:
|
||||||
def reset_config():
|
def reset_config():
|
||||||
PostBreachFilesService.remove_PBA_files()
|
PostBreachFilesService.remove_PBA_files()
|
||||||
config = ConfigService.get_default_config(True)
|
config = ConfigService.get_default_config(True)
|
||||||
ConfigService.set_server_ips_in_config(config)
|
|
||||||
try:
|
try:
|
||||||
mode = get_mode()
|
mode = get_mode()
|
||||||
update_config_per_mode(mode, config, should_encrypt=False)
|
update_config_per_mode(mode, config, should_encrypt=False)
|
||||||
|
@ -263,17 +260,6 @@ class ConfigService:
|
||||||
ConfigService.update_config(config, should_encrypt=False)
|
ConfigService.update_config(config, should_encrypt=False)
|
||||||
logger.info("Monkey config reset was called")
|
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
|
@staticmethod
|
||||||
def _extend_config_with_default(validator_class):
|
def _extend_config_with_default(validator_class):
|
||||||
validate_properties = validator_class.VALIDATORS["properties"]
|
validate_properties = validator_class.VALIDATORS["properties"]
|
||||||
|
@ -407,8 +393,6 @@ class ConfigService:
|
||||||
"linux_filename": config.get(flat_linux_filename_field, ""),
|
"linux_filename": config.get(flat_linux_filename_field, ""),
|
||||||
"windows_command": config.get(flat_windows_command_field, ""),
|
"windows_command": config.get(flat_windows_command_field, ""),
|
||||||
"windows_filename": config.get(flat_windows_filename_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
|
config["post_breach_actions"] = formatted_pbas_config
|
||||||
|
|
|
@ -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 <ip>:<port>)",
|
|
||||||
},
|
|
||||||
"current_server": {
|
|
||||||
"title": "Current server",
|
|
||||||
"type": "string",
|
|
||||||
"default": "192.0.2.0:5000",
|
|
||||||
"description": "The current command server the monkey is communicating with",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"network": {
|
"network": {
|
||||||
"title": "Network",
|
"title": "Network",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
@ -3,6 +3,7 @@ import ipaddress
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
from netifaces import AF_INET, ifaddresses, interfaces
|
from netifaces import AF_INET, ifaddresses, interfaces
|
||||||
from ring import lru
|
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
|
# This means that if the interfaces of the Island machine change, the Island process needs to be
|
||||||
# restarted.
|
# restarted.
|
||||||
@lru(maxsize=1)
|
@lru(maxsize=1)
|
||||||
def local_ip_addresses():
|
def local_ip_addresses() -> Sequence[str]:
|
||||||
ip_list = []
|
ip_list = []
|
||||||
for interface in interfaces():
|
for interface in interfaces():
|
||||||
addresses = ifaddresses(interface).get(AF_INET, [])
|
addresses = ifaddresses(interface).get(AF_INET, [])
|
||||||
|
|
|
@ -4,11 +4,9 @@ import {Nav} from 'react-bootstrap';
|
||||||
|
|
||||||
const sectionOrder = [
|
const sectionOrder = [
|
||||||
'network',
|
'network',
|
||||||
'island_server',
|
|
||||||
'exploits',
|
'exploits',
|
||||||
'classes',
|
'classes',
|
||||||
'general',
|
'general'
|
||||||
'testing'
|
|
||||||
];
|
];
|
||||||
const initialSection = sectionOrder[0];
|
const initialSection = sectionOrder[0];
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {faExpandArrowsAlt} from '@fortawesome/free-solid-svg-icons';
|
||||||
import RunOnIslandButton from './RunOnIslandButton';
|
import RunOnIslandButton from './RunOnIslandButton';
|
||||||
import AWSRunButton from './RunOnAWS/AWSRunButton';
|
import AWSRunButton from './RunOnAWS/AWSRunButton';
|
||||||
|
|
||||||
const CONFIG_URL = '/api/configuration/island';
|
const IP_ADDRESSES_URL = '/api/island/ip-addresses';
|
||||||
|
|
||||||
function RunOptions(props) {
|
function RunOptions(props) {
|
||||||
|
|
||||||
|
@ -21,13 +21,10 @@ function RunOptions(props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialized === false) {
|
if (initialized === false) {
|
||||||
authComponent.authFetch(CONFIG_URL)
|
authComponent.authFetch(IP_ADDRESSES_URL)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(res => {
|
.then(res => {
|
||||||
let commandServers = res.configuration.internal.island_server.command_servers;
|
let ipAddresses = res.ip_addresses;
|
||||||
let ipAddresses = commandServers.map(ip => {
|
|
||||||
return ip.split(':', 1);
|
|
||||||
});
|
|
||||||
setIps(ipAddresses);
|
setIps(ipAddresses);
|
||||||
setInitialized(true);
|
setInitialized(true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -62,8 +62,6 @@
|
||||||
},
|
},
|
||||||
"PBA_linux_filename": "",
|
"PBA_linux_filename": "",
|
||||||
"PBA_windows_filename": "",
|
"PBA_windows_filename": "",
|
||||||
"command_servers": ["10.197.94.72:5000"],
|
|
||||||
"current_server": "localhost:5000",
|
|
||||||
"custom_pbas": {
|
"custom_pbas": {
|
||||||
"linux_command": "",
|
"linux_command": "",
|
||||||
"windows_command": ""
|
"windows_command": ""
|
||||||
|
|
|
@ -11,10 +11,6 @@
|
||||||
"PBA_windows_filename": "test.ps1",
|
"PBA_windows_filename": "test.ps1",
|
||||||
"alive": true,
|
"alive": true,
|
||||||
"blocked_ips": ["192.168.1.1", "192.168.1.100"],
|
"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_linux_cmd": "bash test.sh",
|
||||||
"custom_PBA_windows_cmd": "powershell test.ps1",
|
"custom_PBA_windows_cmd": "powershell test.ps1",
|
||||||
"depth": 2,
|
"depth": 2,
|
||||||
|
|
|
@ -41,14 +41,6 @@
|
||||||
"general": {
|
"general": {
|
||||||
"keep_tunnel_open_time": 60
|
"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": {
|
"network": {
|
||||||
"tcp_scanner": {
|
"tcp_scanner": {
|
||||||
"HTTP_PORTS": [
|
"HTTP_PORTS": [
|
||||||
|
|
|
@ -14,8 +14,7 @@ def PORT():
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def config(monkeypatch, IPS, PORT):
|
def config(monkeypatch):
|
||||||
monkeypatch.setattr("monkey_island.cc.services.config.local_ip_addresses", lambda: IPS)
|
|
||||||
config = ConfigService.get_default_config(True)
|
config = ConfigService.get_default_config(True)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,6 @@ from monkey_island.cc.services.config import ConfigService
|
||||||
# monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
|
# 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)
|
@pytest.fixture(autouse=True)
|
||||||
def mock_flat_config(monkeypatch, flat_monkey_config):
|
def mock_flat_config(monkeypatch, flat_monkey_config):
|
||||||
monkeypatch.setattr(
|
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():
|
def test_format_config_for_agent__credentials_removed():
|
||||||
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
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",
|
"windows_command": "powershell test.ps1",
|
||||||
"linux_filename": "test.sh",
|
"linux_filename": "test.sh",
|
||||||
"windows_filename": "test.ps1",
|
"windows_filename": "test.ps1",
|
||||||
"current_server": "10.197.94.72:5000",
|
|
||||||
}
|
}
|
||||||
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue