forked from p15670423/monkey
Merge pull request #650 from guardicore/feature/exploitation_redundancy_fix
Redundant exploitations fix
This commit is contained in:
commit
ed276895a8
|
@ -10,7 +10,13 @@ In order to execute the entire test suite, you must know the external IP of the
|
||||||
this information in the GCP Console `Compute Engine/VM Instances` under _External IP_.
|
this information in the GCP Console `Compute Engine/VM Instances` under _External IP_.
|
||||||
|
|
||||||
#### Running in command line
|
#### Running in command line
|
||||||
Run the following command:
|
Blackbox tests have following parameters:
|
||||||
|
- `--island=IP` Sets island's IP
|
||||||
|
- `--no-gcp` (Optional) Use for no interaction with the cloud (local test).
|
||||||
|
- `--quick-performance-tests` (Optional) If enabled performance tests won't reset island and won't send telemetries,
|
||||||
|
instead will just test performance of endpoints in already present island state.
|
||||||
|
|
||||||
|
Example run command:
|
||||||
|
|
||||||
`monkey\envs\monkey_zoo\blackbox>python -m pytest -s --island=35.207.152.72:5000 test_blackbox.py`
|
`monkey\envs\monkey_zoo\blackbox>python -m pytest -s --island=35.207.152.72:5000 test_blackbox.py`
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,23 @@ import pytest
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--island", action="store", default="",
|
parser.addoption("--island", action="store", default="",
|
||||||
help="Specify the Monkey Island address (host+port).")
|
help="Specify the Monkey Island address (host+port).")
|
||||||
|
parser.addoption("--no-gcp", action="store_true", default=False,
|
||||||
|
help="Use for no interaction with the cloud.")
|
||||||
|
parser.addoption("--quick-performance-tests", action="store_true", default=False,
|
||||||
|
help="If enabled performance tests won't reset island and won't send telemetries, "
|
||||||
|
"instead will just test performance of already present island state.")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module')
|
@pytest.fixture(scope='session')
|
||||||
def island(request):
|
def island(request):
|
||||||
return request.config.getoption("--island")
|
return request.config.getoption("--island")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def no_gcp(request):
|
||||||
|
return request.config.getoption("--no-gcp")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def quick_performance_tests(request):
|
||||||
|
return request.config.getoption("--quick-performance-tests")
|
||||||
|
|
|
@ -90,9 +90,10 @@ class MonkeyIslandRequests(object):
|
||||||
@_Decorators.refresh_jwt_token
|
@_Decorators.refresh_jwt_token
|
||||||
def patch(self, url, data: Dict):
|
def patch(self, url, data: Dict):
|
||||||
return requests.patch(self.addr + url, # noqa: DUO123
|
return requests.patch(self.addr + url, # noqa: DUO123
|
||||||
data=data,
|
data=data,
|
||||||
headers=self.get_jwt_header(),
|
headers=self.get_jwt_header(),
|
||||||
verify=False)
|
verify=False
|
||||||
|
)
|
||||||
|
|
||||||
@_Decorators.refresh_jwt_token
|
@_Decorators.refresh_jwt_token
|
||||||
def delete(self, url):
|
def delete(self, url):
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
"basic": {
|
"basic": {
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"exploit_password_list": [
|
"exploit_password_list": [
|
||||||
"Password1!",
|
"Xk8VDTsC",
|
||||||
"12345678",
|
"^NgDvY59~8",
|
||||||
"^NgDvY59~8"
|
"Ivrrw5zEzs",
|
||||||
|
"3Q=(Ge(+&w]*",
|
||||||
|
"`))jU7L(w}",
|
||||||
|
"t67TC5ZDmz"
|
||||||
],
|
],
|
||||||
"exploit_user_list": [
|
"exploit_user_list": [
|
||||||
"Administrator",
|
"m0nk3y"
|
||||||
"m0nk3y",
|
|
||||||
"user"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
|
@ -23,11 +24,38 @@
|
||||||
"local_network_scan": false,
|
"local_network_scan": false,
|
||||||
"subnet_scan_list": [
|
"subnet_scan_list": [
|
||||||
"10.2.2.2",
|
"10.2.2.2",
|
||||||
"10.2.2.4"
|
"10.2.2.3",
|
||||||
|
"10.2.2.4",
|
||||||
|
"10.2.2.5",
|
||||||
|
"10.2.2.8",
|
||||||
|
"10.2.2.9",
|
||||||
|
"10.2.1.10",
|
||||||
|
"10.2.0.11",
|
||||||
|
"10.2.0.12",
|
||||||
|
"10.2.2.11",
|
||||||
|
"10.2.2.12",
|
||||||
|
"10.2.2.14",
|
||||||
|
"10.2.2.15",
|
||||||
|
"10.2.2.16",
|
||||||
|
"10.2.2.18",
|
||||||
|
"10.2.2.19",
|
||||||
|
"10.2.2.20",
|
||||||
|
"10.2.2.21",
|
||||||
|
"10.2.2.23",
|
||||||
|
"10.2.2.24"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"network_analysis": {
|
"network_analysis": {
|
||||||
"inaccessible_subnets": []
|
"inaccessible_subnets": [
|
||||||
|
"10.2.2.0/30",
|
||||||
|
"10.2.2.8/30",
|
||||||
|
"10.2.2.24/32",
|
||||||
|
"10.2.2.23/32",
|
||||||
|
"10.2.2.21/32",
|
||||||
|
"10.2.2.19/32",
|
||||||
|
"10.2.2.18/32",
|
||||||
|
"10.2.2.17/32"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cnc": {
|
"cnc": {
|
||||||
|
@ -45,10 +73,17 @@
|
||||||
"exploits": {
|
"exploits": {
|
||||||
"general": {
|
"general": {
|
||||||
"exploiter_classes": [
|
"exploiter_classes": [
|
||||||
|
"SmbExploiter",
|
||||||
|
"WmiExploiter",
|
||||||
"SSHExploiter",
|
"SSHExploiter",
|
||||||
"MSSQLExploiter",
|
"ShellShockExploiter",
|
||||||
|
"SambaCryExploiter",
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
"HadoopExploiter"
|
"Struts2Exploiter",
|
||||||
|
"WebLogicExploiter",
|
||||||
|
"HadoopExploiter",
|
||||||
|
"VSFTPDExploiter",
|
||||||
|
"MSSQLExploiter"
|
||||||
],
|
],
|
||||||
"skip_exploit_if_file_exist": false
|
"skip_exploit_if_file_exist": false
|
||||||
},
|
},
|
||||||
|
@ -57,9 +92,6 @@
|
||||||
"remote_user_pass": "Password1!",
|
"remote_user_pass": "Password1!",
|
||||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||||
},
|
},
|
||||||
"rdp_grinder": {
|
|
||||||
"rdp_use_vbs_download": true
|
|
||||||
},
|
|
||||||
"sambacry": {
|
"sambacry": {
|
||||||
"sambacry_folder_paths_to_guess": [
|
"sambacry_folder_paths_to_guess": [
|
||||||
"/",
|
"/",
|
||||||
|
@ -109,7 +141,7 @@
|
||||||
"exploit_ssh_keys": []
|
"exploit_ssh_keys": []
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"keep_tunnel_open_time": 1,
|
"keep_tunnel_open_time": 60,
|
||||||
"monkey_dir_name": "monkey_dir",
|
"monkey_dir_name": "monkey_dir",
|
||||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||||
},
|
},
|
||||||
|
@ -123,6 +155,9 @@
|
||||||
"monkey_log_path_linux": "/tmp/user-1563",
|
"monkey_log_path_linux": "/tmp/user-1563",
|
||||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||||
"send_log_to_server": true
|
"send_log_to_server": true
|
||||||
|
},
|
||||||
|
"testing": {
|
||||||
|
"export_monkey_telems": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"monkey": {
|
"monkey": {
|
||||||
|
@ -137,24 +172,32 @@
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"alive": true,
|
"alive": true,
|
||||||
"post_breach_actions": []
|
"post_breach_actions": [
|
||||||
|
"CommunicateAsNewUser"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"life_cycle": {
|
"life_cycle": {
|
||||||
"max_iterations": 1,
|
"max_iterations": 1,
|
||||||
"retry_failed_explotation": true,
|
"retry_failed_explotation": true,
|
||||||
"timeout_between_iterations": 100,
|
"timeout_between_iterations": 100,
|
||||||
"victims_max_exploit": 7,
|
"victims_max_exploit": 15,
|
||||||
"victims_max_find": 30
|
"victims_max_find": 100
|
||||||
},
|
},
|
||||||
"system_info": {
|
"system_info": {
|
||||||
"collect_system_info": true,
|
"collect_system_info": true,
|
||||||
"extract_azure_creds": false,
|
"extract_azure_creds": true,
|
||||||
"should_use_mimikatz": true
|
"should_use_mimikatz": true,
|
||||||
|
"system_info_collectors_classes": [
|
||||||
|
"EnvironmentCollector",
|
||||||
|
"AwsCollector",
|
||||||
|
"HostnameCollector",
|
||||||
|
"ProcessListCollector"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"ping_scanner": {
|
"ping_scanner": {
|
||||||
"ping_scan_timeout": 500
|
"ping_scan_timeout": 1000
|
||||||
},
|
},
|
||||||
"tcp_scanner": {
|
"tcp_scanner": {
|
||||||
"HTTP_PORTS": [
|
"HTTP_PORTS": [
|
||||||
|
@ -166,7 +209,7 @@
|
||||||
],
|
],
|
||||||
"tcp_scan_get_banner": true,
|
"tcp_scan_get_banner": true,
|
||||||
"tcp_scan_interval": 0,
|
"tcp_scan_interval": 0,
|
||||||
"tcp_scan_timeout": 1000,
|
"tcp_scan_timeout": 3000,
|
||||||
"tcp_target_ports": [
|
"tcp_target_ports": [
|
||||||
22,
|
22,
|
||||||
2222,
|
2222,
|
||||||
|
@ -179,7 +222,8 @@
|
||||||
8008,
|
8008,
|
||||||
3306,
|
3306,
|
||||||
9200,
|
9200,
|
||||||
7001
|
7001,
|
||||||
|
8088
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,15 +27,16 @@ LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True, scope='session')
|
@pytest.fixture(autouse=True, scope='session')
|
||||||
def GCPHandler(request):
|
def GCPHandler(request, no_gcp):
|
||||||
GCPHandler = gcp_machine_handlers.GCPHandler()
|
if not no_gcp:
|
||||||
GCPHandler.start_machines(" ".join(GCP_TEST_MACHINE_LIST))
|
GCPHandler = gcp_machine_handlers.GCPHandler()
|
||||||
wait_machine_bootup()
|
GCPHandler.start_machines(" ".join(GCP_TEST_MACHINE_LIST))
|
||||||
|
wait_machine_bootup()
|
||||||
|
|
||||||
def fin():
|
def fin():
|
||||||
GCPHandler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST))
|
GCPHandler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST))
|
||||||
|
|
||||||
request.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True, scope='session')
|
@pytest.fixture(autouse=True, scope='session')
|
||||||
|
@ -49,9 +50,10 @@ def wait_machine_bootup():
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='class')
|
@pytest.fixture(scope='class')
|
||||||
def island_client(island):
|
def island_client(island, quick_performance_tests):
|
||||||
island_client_object = MonkeyIslandClient(island)
|
island_client_object = MonkeyIslandClient(island)
|
||||||
island_client_object.reset_env()
|
if not quick_performance_tests:
|
||||||
|
island_client_object.reset_env()
|
||||||
yield island_client_object
|
yield island_client_object
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,7 +132,7 @@ class TestMonkeyBlackbox(object):
|
||||||
def test_wmi_pth(self, island_client):
|
def test_wmi_pth(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, "WMI_PTH.conf", "WMI_PTH")
|
TestMonkeyBlackbox.run_exploitation_test(island_client, "WMI_PTH.conf", "WMI_PTH")
|
||||||
|
|
||||||
def test_report_generation_performance(self, island_client):
|
def test_report_generation_performance(self, island_client, quick_performance_tests):
|
||||||
"""
|
"""
|
||||||
This test includes the SSH + Elastic + Hadoop + MSSQL machines all in one test
|
This test includes the SSH + Elastic + Hadoop + MSSQL machines all in one test
|
||||||
for a total of 8 machines including the Monkey Island.
|
for a total of 8 machines including the Monkey Island.
|
||||||
|
@ -138,22 +140,30 @@ class TestMonkeyBlackbox(object):
|
||||||
Is has 2 analyzers - the regular one which checks all the Monkeys
|
Is has 2 analyzers - the regular one which checks all the Monkeys
|
||||||
and the Timing one which checks how long the report took to execute
|
and the Timing one which checks how long the report took to execute
|
||||||
"""
|
"""
|
||||||
TestMonkeyBlackbox.run_performance_test(ReportGenerationTest,
|
if not quick_performance_tests:
|
||||||
island_client,
|
TestMonkeyBlackbox.run_performance_test(ReportGenerationTest,
|
||||||
"PERFORMANCE.conf",
|
island_client,
|
||||||
timeout_in_seconds=10*60)
|
"PERFORMANCE.conf",
|
||||||
|
timeout_in_seconds=10*60)
|
||||||
|
else:
|
||||||
|
LOGGER.error("This test doesn't support 'quick_performance_tests' option.")
|
||||||
|
assert False
|
||||||
|
|
||||||
def test_map_generation_performance(self, island_client):
|
def test_map_generation_performance(self, island_client, quick_performance_tests):
|
||||||
TestMonkeyBlackbox.run_performance_test(MapGenerationTest,
|
if not quick_performance_tests:
|
||||||
island_client,
|
TestMonkeyBlackbox.run_performance_test(MapGenerationTest,
|
||||||
"PERFORMANCE.conf",
|
island_client,
|
||||||
timeout_in_seconds=10*60)
|
"PERFORMANCE.conf",
|
||||||
|
timeout_in_seconds=10*60)
|
||||||
|
else:
|
||||||
|
LOGGER.error("This test doesn't support 'quick_performance_tests' option.")
|
||||||
|
assert False
|
||||||
|
|
||||||
def test_report_generation_from_fake_telemetries(self, island_client):
|
def test_report_generation_from_fake_telemetries(self, island_client, quick_performance_tests):
|
||||||
ReportGenerationFromTelemetryTest(island_client).run()
|
ReportGenerationFromTelemetryTest(island_client, quick_performance_tests).run()
|
||||||
|
|
||||||
def test_map_generation_from_fake_telemetries(self, island_client):
|
def test_map_generation_from_fake_telemetries(self, island_client, quick_performance_tests):
|
||||||
MapGenerationFromTelemetryTest(island_client).run()
|
MapGenerationFromTelemetryTest(island_client, quick_performance_tests).run()
|
||||||
|
|
||||||
def test_telem_performance(self, island_client):
|
def test_telem_performance(self, island_client, quick_performance_tests):
|
||||||
TelemetryPerformanceTest(island_client).test_telemetry_performance()
|
TelemetryPerformanceTest(island_client, quick_performance_tests).test_telemetry_performance()
|
||||||
|
|
|
@ -17,9 +17,6 @@ class EndpointPerformanceTest(BasicTest):
|
||||||
self.island_client = island_client
|
self.island_client = island_client
|
||||||
|
|
||||||
def run(self) -> bool:
|
def run(self) -> bool:
|
||||||
if not self.island_client.is_all_monkeys_dead():
|
|
||||||
raise RuntimeError("Can't test report times since not all Monkeys have died.")
|
|
||||||
|
|
||||||
# Collect timings for all pages
|
# Collect timings for all pages
|
||||||
self.island_client.clear_caches()
|
self.island_client.clear_caches()
|
||||||
endpoint_timings = {}
|
endpoint_timings = {}
|
||||||
|
|
|
@ -17,7 +17,7 @@ class MapGenerationFromTelemetryTest(PerformanceTest):
|
||||||
|
|
||||||
TEST_NAME = "Map generation from fake telemetries test"
|
TEST_NAME = "Map generation from fake telemetries test"
|
||||||
|
|
||||||
def __init__(self, island_client, break_on_timeout=False):
|
def __init__(self, island_client, quick_performance_test: bool, break_on_timeout=False):
|
||||||
self.island_client = island_client
|
self.island_client = island_client
|
||||||
performance_config = PerformanceTestConfig(max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
|
performance_config = PerformanceTestConfig(max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
|
||||||
max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
|
max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
|
||||||
|
@ -25,7 +25,8 @@ class MapGenerationFromTelemetryTest(PerformanceTest):
|
||||||
break_on_timeout=break_on_timeout)
|
break_on_timeout=break_on_timeout)
|
||||||
self.performance_test_workflow = TelemetryPerformanceTestWorkflow(MapGenerationFromTelemetryTest.TEST_NAME,
|
self.performance_test_workflow = TelemetryPerformanceTestWorkflow(MapGenerationFromTelemetryTest.TEST_NAME,
|
||||||
self.island_client,
|
self.island_client,
|
||||||
performance_config)
|
performance_config,
|
||||||
|
quick_performance_test)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.performance_test_workflow.run()
|
self.performance_test_workflow.run()
|
||||||
|
|
|
@ -23,6 +23,8 @@ class PerformanceTestWorkflow(BasicTest):
|
||||||
self.island_client.kill_all_monkeys()
|
self.island_client.kill_all_monkeys()
|
||||||
self.exploitation_test.wait_until_monkeys_die()
|
self.exploitation_test.wait_until_monkeys_die()
|
||||||
self.exploitation_test.wait_for_monkey_process_to_finish()
|
self.exploitation_test.wait_for_monkey_process_to_finish()
|
||||||
|
if not self.island_client.is_all_monkeys_dead():
|
||||||
|
raise RuntimeError("Can't test report times since not all Monkeys have died.")
|
||||||
performance_test = EndpointPerformanceTest(self.name, self.performance_config, self.island_client)
|
performance_test = EndpointPerformanceTest(self.name, self.performance_config, self.island_client)
|
||||||
try:
|
try:
|
||||||
if not self.island_client.is_all_monkeys_dead():
|
if not self.island_client.is_all_monkeys_dead():
|
||||||
|
|
|
@ -21,7 +21,7 @@ class ReportGenerationFromTelemetryTest(PerformanceTest):
|
||||||
|
|
||||||
TEST_NAME = "Map generation from fake telemetries test"
|
TEST_NAME = "Map generation from fake telemetries test"
|
||||||
|
|
||||||
def __init__(self, island_client, break_on_timeout=False):
|
def __init__(self, island_client, quick_performance_test, break_on_timeout=False):
|
||||||
self.island_client = island_client
|
self.island_client = island_client
|
||||||
performance_config = PerformanceTestConfig(max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
|
performance_config = PerformanceTestConfig(max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
|
||||||
max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
|
max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
|
||||||
|
@ -29,7 +29,8 @@ class ReportGenerationFromTelemetryTest(PerformanceTest):
|
||||||
break_on_timeout=break_on_timeout)
|
break_on_timeout=break_on_timeout)
|
||||||
self.performance_test_workflow = TelemetryPerformanceTestWorkflow(ReportGenerationFromTelemetryTest.TEST_NAME,
|
self.performance_test_workflow = TelemetryPerformanceTestWorkflow(ReportGenerationFromTelemetryTest.TEST_NAME,
|
||||||
self.island_client,
|
self.island_client,
|
||||||
performance_config)
|
performance_config,
|
||||||
|
quick_performance_test)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.performance_test_workflow.run()
|
self.performance_test_workflow.run()
|
||||||
|
|
|
@ -10,9 +10,9 @@ class FakeMonkey:
|
||||||
self.original_guid = guid
|
self.original_guid = guid
|
||||||
self.fake_ip_generator = fake_ip_generator
|
self.fake_ip_generator = fake_ip_generator
|
||||||
self.on_island = on_island
|
self.on_island = on_island
|
||||||
self.fake_guid = str(random.randint(1000000000000, 9999999999999))
|
self.fake_guid = str(random.randint(1000000000000, 9999999999999)) # noqa: DUO102
|
||||||
self.fake_ips = fake_ip_generator.generate_fake_ips_for_real_ips(ips)
|
self.fake_ips = fake_ip_generator.generate_fake_ips_for_real_ips(ips)
|
||||||
|
|
||||||
def change_fake_data(self):
|
def change_fake_data(self):
|
||||||
self.fake_ips = self.fake_ip_generator.generate_fake_ips_for_real_ips(self.original_ips)
|
self.fake_ips = self.fake_ip_generator.generate_fake_ips_for_real_ips(self.original_ips)
|
||||||
self.fake_guid = str(random.randint(1000000000000, 9999999999999))
|
self.fake_guid = str(random.randint(1000000000000, 9999999999999)) # noqa: DUO102
|
||||||
|
|
|
@ -18,8 +18,9 @@ MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=60)
|
||||||
|
|
||||||
class TelemetryPerformanceTest:
|
class TelemetryPerformanceTest:
|
||||||
|
|
||||||
def __init__(self, island_client: MonkeyIslandClient):
|
def __init__(self, island_client: MonkeyIslandClient, quick_performance_test: bool):
|
||||||
self.island_client = island_client
|
self.island_client = island_client
|
||||||
|
self.quick_performance_test = quick_performance_test
|
||||||
|
|
||||||
def test_telemetry_performance(self):
|
def test_telemetry_performance(self):
|
||||||
LOGGER.info("Starting telemetry performance test.")
|
LOGGER.info("Starting telemetry performance test.")
|
||||||
|
@ -36,6 +37,8 @@ class TelemetryPerformanceTest:
|
||||||
telemetry_parse_times[telemetry_endpoint] = self.get_telemetry_time(telemetry)
|
telemetry_parse_times[telemetry_endpoint] = self.get_telemetry_time(telemetry)
|
||||||
test_config = PerformanceTestConfig(MAX_ALLOWED_SINGLE_TELEM_PARSE_TIME, MAX_ALLOWED_TOTAL_TIME)
|
test_config = PerformanceTestConfig(MAX_ALLOWED_SINGLE_TELEM_PARSE_TIME, MAX_ALLOWED_TOTAL_TIME)
|
||||||
PerformanceAnalyzer(test_config, telemetry_parse_times).analyze_test_results()
|
PerformanceAnalyzer(test_config, telemetry_parse_times).analyze_test_results()
|
||||||
|
if not self.quick_performance_test:
|
||||||
|
self.island_client.reset_env()
|
||||||
|
|
||||||
def get_telemetry_time(self, telemetry):
|
def get_telemetry_time(self, telemetry):
|
||||||
content = telemetry['content']
|
content = telemetry['content']
|
||||||
|
|
|
@ -6,15 +6,18 @@ from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test impor
|
||||||
|
|
||||||
class TelemetryPerformanceTestWorkflow(BasicTest):
|
class TelemetryPerformanceTestWorkflow(BasicTest):
|
||||||
|
|
||||||
def __init__(self, name, island_client, performance_config: PerformanceTestConfig):
|
def __init__(self, name, island_client, performance_config: PerformanceTestConfig, quick_performance_test):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.island_client = island_client
|
self.island_client = island_client
|
||||||
self.performance_config = performance_config
|
self.performance_config = performance_config
|
||||||
|
self.quick_performance_test = quick_performance_test
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
TelemetryPerformanceTest(island_client=self.island_client).test_telemetry_performance()
|
if not self.quick_performance_test:
|
||||||
|
TelemetryPerformanceTest(island_client=self.island_client).test_telemetry_performance()
|
||||||
performance_test = EndpointPerformanceTest(self.name, self.performance_config, self.island_client)
|
performance_test = EndpointPerformanceTest(self.name, self.performance_config, self.island_client)
|
||||||
assert performance_test.run()
|
assert performance_test.run()
|
||||||
finally:
|
finally:
|
||||||
self.island_client.reset_env()
|
if not self.quick_performance_test:
|
||||||
|
self.island_client.reset_env()
|
||||||
|
|
|
@ -15,10 +15,10 @@ class GCPHandler(object):
|
||||||
self.zone = zone
|
self.zone = zone
|
||||||
try:
|
try:
|
||||||
# pass the key file to gcp
|
# pass the key file to gcp
|
||||||
subprocess.call(GCPHandler.get_auth_command(key_path), shell=True)
|
subprocess.call(GCPHandler.get_auth_command(key_path), shell=True) # noqa: DUO116
|
||||||
LOGGER.info("GCP Handler passed key")
|
LOGGER.info("GCP Handler passed key")
|
||||||
# set project
|
# set project
|
||||||
subprocess.call(GCPHandler.get_set_project_command(project_id), shell=True)
|
subprocess.call(GCPHandler.get_set_project_command(project_id), shell=True) # noqa: DUO116
|
||||||
LOGGER.info("GCP Handler set project")
|
LOGGER.info("GCP Handler set project")
|
||||||
LOGGER.info("GCP Handler initialized successfully")
|
LOGGER.info("GCP Handler initialized successfully")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -32,14 +32,14 @@ class GCPHandler(object):
|
||||||
"""
|
"""
|
||||||
LOGGER.info("Setting up all GCP machines...")
|
LOGGER.info("Setting up all GCP machines...")
|
||||||
try:
|
try:
|
||||||
subprocess.call((GCPHandler.MACHINE_STARTING_COMMAND % (machine_list, self.zone)), shell=True)
|
subprocess.call((GCPHandler.MACHINE_STARTING_COMMAND % (machine_list, self.zone)), shell=True) # noqa: DUO116
|
||||||
LOGGER.info("GCP machines successfully started.")
|
LOGGER.info("GCP machines successfully started.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOGGER.error("GCP Handler failed to start GCP machines: %s" % e)
|
LOGGER.error("GCP Handler failed to start GCP machines: %s" % e)
|
||||||
|
|
||||||
def stop_machines(self, machine_list):
|
def stop_machines(self, machine_list):
|
||||||
try:
|
try:
|
||||||
subprocess.call((GCPHandler.MACHINE_STOPPING_COMMAND % (machine_list, self.zone)), shell=True)
|
subprocess.call((GCPHandler.MACHINE_STOPPING_COMMAND % (machine_list, self.zone)), shell=True) # noqa: DUO116
|
||||||
LOGGER.info("GCP machines stopped successfully.")
|
LOGGER.info("GCP machines stopped successfully.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOGGER.error("GCP Handler failed to stop network machines: %s" % e)
|
LOGGER.error("GCP Handler failed to stop network machines: %s" % e)
|
||||||
|
|
|
@ -13,6 +13,7 @@ GUID = str(uuid.getnode())
|
||||||
EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin')
|
EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin')
|
||||||
|
|
||||||
SENSITIVE_FIELDS = ["exploit_password_list", "exploit_user_list", "exploit_ssh_keys"]
|
SENSITIVE_FIELDS = ["exploit_password_list", "exploit_user_list", "exploit_ssh_keys"]
|
||||||
|
LOCAL_CONFIG_VARS = ["name", "id", "current_server", "max_depth"]
|
||||||
HIDDEN_FIELD_REPLACEMENT_CONTENT = "hidden"
|
HIDDEN_FIELD_REPLACEMENT_CONTENT = "hidden"
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,14 +23,17 @@ class Configuration(object):
|
||||||
for key, value in list(formatted_data.items()):
|
for key, value in list(formatted_data.items()):
|
||||||
if key.startswith('_'):
|
if key.startswith('_'):
|
||||||
continue
|
continue
|
||||||
if key in ["name", "id", "current_server"]:
|
if key in LOCAL_CONFIG_VARS:
|
||||||
continue
|
continue
|
||||||
if self._depth_from_commandline and key == "depth":
|
if self._depth_from_commandline and key == "depth":
|
||||||
|
self.max_depth = value
|
||||||
continue
|
continue
|
||||||
if hasattr(self, key):
|
if hasattr(self, key):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
else:
|
else:
|
||||||
unknown_items.append(key)
|
unknown_items.append(key)
|
||||||
|
if not self.max_depth:
|
||||||
|
self.max_depth = self.depth
|
||||||
return unknown_items
|
return unknown_items
|
||||||
|
|
||||||
def from_json(self, json_data):
|
def from_json(self, json_data):
|
||||||
|
@ -135,6 +139,8 @@ class Configuration(object):
|
||||||
|
|
||||||
# depth of propagation
|
# depth of propagation
|
||||||
depth = 2
|
depth = 2
|
||||||
|
max_depth = None
|
||||||
|
started_on_island = False
|
||||||
current_server = ""
|
current_server = ""
|
||||||
|
|
||||||
# Configuration servers to try to connect to, in this order.
|
# Configuration servers to try to connect to, in this order.
|
||||||
|
@ -232,6 +238,18 @@ class Configuration(object):
|
||||||
cred_list.append(cred)
|
cred_list.append(cred)
|
||||||
return cred_list
|
return cred_list
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def hash_sensitive_data(sensitive_data):
|
||||||
|
"""
|
||||||
|
Hash sensitive data (e.g. passwords). Used so the log won't contain sensitive data plain-text, as the log is
|
||||||
|
saved on client machines plain-text.
|
||||||
|
|
||||||
|
:param sensitive_data: the data to hash.
|
||||||
|
:return: the hashed data.
|
||||||
|
"""
|
||||||
|
password_hashed = hashlib.sha512(sensitive_data.encode()).hexdigest()
|
||||||
|
return password_hashed
|
||||||
|
|
||||||
exploit_user_list = ['Administrator', 'root', 'user']
|
exploit_user_list = ['Administrator', 'root', 'user']
|
||||||
exploit_password_list = ["Password1!", "1234", "password", "12345678"]
|
exploit_password_list = ["Password1!", "1234", "password", "12345678"]
|
||||||
exploit_lm_hash_list = []
|
exploit_lm_hash_list = []
|
||||||
|
@ -259,23 +277,22 @@ class Configuration(object):
|
||||||
|
|
||||||
extract_azure_creds = True
|
extract_azure_creds = True
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# post breach actions
|
||||||
|
###########################
|
||||||
post_breach_actions = []
|
post_breach_actions = []
|
||||||
custom_PBA_linux_cmd = ""
|
custom_PBA_linux_cmd = ""
|
||||||
custom_PBA_windows_cmd = ""
|
custom_PBA_windows_cmd = ""
|
||||||
PBA_linux_filename = None
|
PBA_linux_filename = None
|
||||||
PBA_windows_filename = None
|
PBA_windows_filename = None
|
||||||
|
|
||||||
@staticmethod
|
###########################
|
||||||
def hash_sensitive_data(sensitive_data):
|
# testing configuration
|
||||||
"""
|
###########################
|
||||||
Hash sensitive data (e.g. passwords). Used so the log won't contain sensitive data plain-text, as the log is
|
export_monkey_telems = False
|
||||||
saved on client machines plain-text.
|
|
||||||
|
|
||||||
:param sensitive_data: the data to hash.
|
def get_hop_distance_to_island(self):
|
||||||
:return: the hashed data.
|
return self.max_depth - self.depth
|
||||||
"""
|
|
||||||
password_hashed = hashlib.sha512(sensitive_data.encode()).hexdigest()
|
|
||||||
return password_hashed
|
|
||||||
|
|
||||||
|
|
||||||
WormConfiguration = Configuration()
|
WormConfiguration = Configuration()
|
||||||
|
|
|
@ -15,6 +15,8 @@ from infection_monkey.transport.tcp import TcpProxy
|
||||||
|
|
||||||
__author__ = 'hoffer'
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
from infection_monkey.utils.exceptions.planned_shutdown_exception import PlannedShutdownException
|
||||||
|
|
||||||
requests.packages.urllib3.disable_warnings()
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -321,3 +323,29 @@ class ControlClient(object):
|
||||||
proxies=ControlClient.proxies)
|
proxies=ControlClient.proxies)
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def should_monkey_run(vulnerable_port: str) -> bool:
|
||||||
|
if vulnerable_port and \
|
||||||
|
WormConfiguration.get_hop_distance_to_island() > 1 and \
|
||||||
|
ControlClient.can_island_see_port(vulnerable_port) and \
|
||||||
|
WormConfiguration.started_on_island:
|
||||||
|
raise PlannedShutdownException("Monkey shouldn't run on current machine "
|
||||||
|
"(it will be exploited later with more depth).")
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def can_island_see_port(port):
|
||||||
|
try:
|
||||||
|
url = f"https://{WormConfiguration.current_server}/api/monkey_control/check_remote_port/{port}"
|
||||||
|
response = requests.get(url, verify=False)
|
||||||
|
response = json.loads(response.content.decode())
|
||||||
|
return response['status'] == "port_visible"
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def report_start_on_island():
|
||||||
|
requests.post(f"https://{WormConfiguration.current_server}/api/monkey_control/started_on_island",
|
||||||
|
data=json.dumps({'started_on_island': True}),
|
||||||
|
verify=False)
|
||||||
|
|
|
@ -44,6 +44,7 @@ class MonkeyDrops(object):
|
||||||
arg_parser.add_argument('-s', '--server')
|
arg_parser.add_argument('-s', '--server')
|
||||||
arg_parser.add_argument('-d', '--depth', type=int)
|
arg_parser.add_argument('-d', '--depth', type=int)
|
||||||
arg_parser.add_argument('-l', '--location')
|
arg_parser.add_argument('-l', '--location')
|
||||||
|
arg_parser.add_argument('-vp', '--vulnerable-port')
|
||||||
self.monkey_args = args[1:]
|
self.monkey_args = args[1:]
|
||||||
self.opts, _ = arg_parser.parse_known_args(args)
|
self.opts, _ = arg_parser.parse_known_args(args)
|
||||||
|
|
||||||
|
@ -115,7 +116,12 @@ class MonkeyDrops(object):
|
||||||
LOG.warning("Cannot set reference date to destination file")
|
LOG.warning("Cannot set reference date to destination file")
|
||||||
|
|
||||||
monkey_options = \
|
monkey_options = \
|
||||||
build_monkey_commandline_explicitly(self.opts.parent, self.opts.tunnel, self.opts.server, self.opts.depth)
|
build_monkey_commandline_explicitly(parent=self.opts.parent,
|
||||||
|
tunnel=self.opts.tunnel,
|
||||||
|
server=self.opts.server,
|
||||||
|
depth=self.opts.depth,
|
||||||
|
location=None,
|
||||||
|
vulnerable_port=self.opts.vulnerable_port)
|
||||||
|
|
||||||
if OperatingSystem.Windows == SystemInfoCollector.get_os():
|
if OperatingSystem.Windows == SystemInfoCollector.get_os():
|
||||||
monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options
|
monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options
|
||||||
|
|
|
@ -73,7 +73,8 @@ class HadoopExploiter(WebRCE):
|
||||||
|
|
||||||
def build_command(self, path, http_path):
|
def build_command(self, path, http_path):
|
||||||
# Build command to execute
|
# Build command to execute
|
||||||
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=HadoopExploiter.HADOOP_PORTS[0][0])
|
||||||
if 'linux' in self.host.os['type']:
|
if 'linux' in self.host.os['type']:
|
||||||
base_command = HADOOP_LINUX_COMMAND
|
base_command = HADOOP_LINUX_COMMAND
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -133,6 +133,7 @@ class MSSQLExploiter(HostExploiter):
|
||||||
# Form monkey's launch command
|
# Form monkey's launch command
|
||||||
monkey_args = build_monkey_commandline(self.host,
|
monkey_args = build_monkey_commandline(self.host,
|
||||||
get_monkey_depth() - 1,
|
get_monkey_depth() - 1,
|
||||||
|
MSSQLExploiter.SQL_DEFAULT_TCP_PORT,
|
||||||
dst_path)
|
dst_path)
|
||||||
suffix = ">>{}".format(self.payload_file_path)
|
suffix = ">>{}".format(self.payload_file_path)
|
||||||
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
||||||
|
|
|
@ -329,7 +329,10 @@ class SambaCryExploiter(HostExploiter):
|
||||||
return open(get_binary_file_path(self.SAMBACRY_RUNNER_FILENAME_64), "rb")
|
return open(get_binary_file_path(self.SAMBACRY_RUNNER_FILENAME_64), "rb")
|
||||||
|
|
||||||
def get_monkey_commandline_file(self, location):
|
def get_monkey_commandline_file(self, location):
|
||||||
return BytesIO(DROPPER_ARG + build_monkey_commandline(self.host, get_monkey_depth() - 1, str(location)))
|
return BytesIO(DROPPER_ARG + build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
SambaCryExploiter.SAMBA_PORT,
|
||||||
|
str(location)))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_share_writable(smb_client, share):
|
def is_share_writable(smb_client, share):
|
||||||
|
|
|
@ -144,7 +144,11 @@ class ShellShockExploiter(HostExploiter):
|
||||||
|
|
||||||
# run the monkey
|
# run the monkey
|
||||||
cmdline = "%s %s" % (dropper_target_path_linux, DROPPER_ARG)
|
cmdline = "%s %s" % (dropper_target_path_linux, DROPPER_ARG)
|
||||||
cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_target_path_linux) + ' & '
|
cmdline += build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
HTTPTools.get_port_from_url(url),
|
||||||
|
dropper_target_path_linux)
|
||||||
|
cmdline += ' & '
|
||||||
run_path = exploit + cmdline
|
run_path = exploit + cmdline
|
||||||
self.attack_page(url, header, run_path)
|
self.attack_page(url, header, run_path)
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ class SmbExploiter(HostExploiter):
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
super(SmbExploiter, self).__init__(host)
|
super(SmbExploiter, self).__init__(host)
|
||||||
|
self.vulnerable_port = None
|
||||||
|
|
||||||
def is_os_supported(self):
|
def is_os_supported(self):
|
||||||
if super(SmbExploiter, self).is_os_supported():
|
if super(SmbExploiter, self).is_os_supported():
|
||||||
|
@ -36,11 +37,13 @@ class SmbExploiter(HostExploiter):
|
||||||
if not self.host.os.get('type'):
|
if not self.host.os.get('type'):
|
||||||
is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
|
is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
|
||||||
if is_smb_open:
|
if is_smb_open:
|
||||||
|
self.vulnerable_port = 445
|
||||||
smb_finger = SMBFinger()
|
smb_finger = SMBFinger()
|
||||||
smb_finger.get_host_fingerprint(self.host)
|
smb_finger.get_host_fingerprint(self.host)
|
||||||
else:
|
else:
|
||||||
is_nb_open, _ = check_tcp_port(self.host.ip_addr, 139)
|
is_nb_open, _ = check_tcp_port(self.host.ip_addr, 139)
|
||||||
if is_nb_open:
|
if is_nb_open:
|
||||||
|
self.vulnerable_port = 139
|
||||||
self.host.os['type'] = 'windows'
|
self.host.os['type'] = 'windows'
|
||||||
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
||||||
return False
|
return False
|
||||||
|
@ -103,10 +106,13 @@ class SmbExploiter(HostExploiter):
|
||||||
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
||||||
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \
|
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \
|
||||||
build_monkey_commandline(self.host, get_monkey_depth() - 1,
|
build_monkey_commandline(self.host, get_monkey_depth() - 1,
|
||||||
|
self.vulnerable_port,
|
||||||
self._config.dropper_target_path_win_32)
|
self._config.dropper_target_path_win_32)
|
||||||
else:
|
else:
|
||||||
cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \
|
cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \
|
||||||
build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=self.vulnerable_port)
|
||||||
|
|
||||||
smb_conn = False
|
smb_conn = False
|
||||||
for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values():
|
for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values():
|
||||||
|
|
|
@ -179,7 +179,9 @@ class SSHExploiter(HostExploiter):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cmdline = "%s %s" % (self._config.dropper_target_path_linux, MONKEY_ARG)
|
cmdline = "%s %s" % (self._config.dropper_target_path_linux, MONKEY_ARG)
|
||||||
cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
cmdline += build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=SSH_PORT)
|
||||||
cmdline += " > /dev/null 2>&1 &"
|
cmdline += " > /dev/null 2>&1 &"
|
||||||
ssh.exec_command(cmdline)
|
ssh.exec_command(cmdline)
|
||||||
|
|
||||||
|
|
|
@ -41,29 +41,32 @@ def get_target_monkey_by_os(is_windows, is_32bit):
|
||||||
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
|
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
|
||||||
|
|
||||||
|
|
||||||
def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, depth=None, location=None):
|
def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, depth=None, location=None,
|
||||||
|
vulnerable_port=None):
|
||||||
cmdline = ""
|
cmdline = ""
|
||||||
|
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
cmdline += " -p " + parent
|
cmdline += f" -p {parent}"
|
||||||
if tunnel is not None:
|
if tunnel is not None:
|
||||||
cmdline += " -t " + tunnel
|
cmdline += f" -t {tunnel}"
|
||||||
if server is not None:
|
if server is not None:
|
||||||
cmdline += " -s " + server
|
cmdline += f" -s {server}"
|
||||||
if depth is not None:
|
if depth is not None:
|
||||||
if depth < 0:
|
if int(depth) < 0:
|
||||||
depth = 0
|
depth = 0
|
||||||
cmdline += " -d %d" % depth
|
cmdline += f" -d {depth}"
|
||||||
if location is not None:
|
if location is not None:
|
||||||
cmdline += " -l %s" % location
|
cmdline += f" -l {location}"
|
||||||
|
if vulnerable_port is not None:
|
||||||
|
cmdline += f" -vp {vulnerable_port}"
|
||||||
|
|
||||||
return cmdline
|
return cmdline
|
||||||
|
|
||||||
|
|
||||||
def build_monkey_commandline(target_host, depth, location=None):
|
def build_monkey_commandline(target_host, depth, vulnerable_port, location=None):
|
||||||
from infection_monkey.config import GUID
|
from infection_monkey.config import GUID
|
||||||
return build_monkey_commandline_explicitly(
|
return build_monkey_commandline_explicitly(
|
||||||
GUID, target_host.default_tunnel, target_host.default_server, depth, location)
|
GUID, target_host.default_tunnel, target_host.default_server, depth, location, vulnerable_port)
|
||||||
|
|
||||||
|
|
||||||
def get_monkey_depth():
|
def get_monkey_depth():
|
||||||
|
|
|
@ -73,6 +73,10 @@ class HTTPTools(object):
|
||||||
lock.acquire()
|
lock.acquire()
|
||||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.parse.quote(os.path.basename(src_path))), httpd
|
return "http://%s:%s/%s" % (local_ip, local_port, urllib.parse.quote(os.path.basename(src_path))), httpd
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_port_from_url(url: str) -> int:
|
||||||
|
return urllib.parse.urlparse(url).port
|
||||||
|
|
||||||
|
|
||||||
class MonkeyHTTPServer(HTTPTools):
|
class MonkeyHTTPServer(HTTPTools):
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from infection_monkey.exploit.tools.helpers import build_monkey_commandline_explicitly
|
||||||
|
|
||||||
|
|
||||||
|
class TestHelpers(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_build_monkey_commandline_explicitly(self):
|
||||||
|
test1 = " -p 101010 -t 10.10.101.10 -s 127.127.127.127:5000 -d 0 -l C:\\windows\\abc -vp 80"
|
||||||
|
result1 = build_monkey_commandline_explicitly(101010,
|
||||||
|
"10.10.101.10",
|
||||||
|
"127.127.127.127:5000",
|
||||||
|
0,
|
||||||
|
"C:\\windows\\abc",
|
||||||
|
80)
|
||||||
|
|
||||||
|
test2 = " -p parent -s 127.127.127.127:5000 -d 0 -vp 80"
|
||||||
|
result2 = build_monkey_commandline_explicitly(parent="parent",
|
||||||
|
server="127.127.127.127:5000",
|
||||||
|
depth="0",
|
||||||
|
vulnerable_port="80")
|
||||||
|
|
||||||
|
self.assertEqual(test1, result1)
|
||||||
|
self.assertEqual(test2, result2)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -132,7 +132,9 @@ class VSFTPDExploiter(HostExploiter):
|
||||||
T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send()
|
T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send()
|
||||||
|
|
||||||
# Run monkey on the machine
|
# Run monkey on the machine
|
||||||
parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
parameters = build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=FTP_PORT)
|
||||||
run_monkey = RUN_MONKEY % {'monkey_path': monkey_path, 'monkey_type': MONKEY_ARG, 'parameters': parameters}
|
run_monkey = RUN_MONKEY % {'monkey_path': monkey_path, 'monkey_type': MONKEY_ARG, 'parameters': parameters}
|
||||||
|
|
||||||
# Set unlimited to memory
|
# Set unlimited to memory
|
||||||
|
|
|
@ -42,6 +42,8 @@ class WebRCE(HostExploiter):
|
||||||
self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
|
self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
|
||||||
self.skip_exist = self._config.skip_exploit_if_file_exist
|
self.skip_exist = self._config.skip_exploit_if_file_exist
|
||||||
self.vulnerable_urls = []
|
self.vulnerable_urls = []
|
||||||
|
self.target_url = None
|
||||||
|
self.vulnerable_port = None
|
||||||
|
|
||||||
def get_exploit_config(self):
|
def get_exploit_config(self):
|
||||||
"""
|
"""
|
||||||
|
@ -87,27 +89,30 @@ class WebRCE(HostExploiter):
|
||||||
if not self.vulnerable_urls:
|
if not self.vulnerable_urls:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
self.target_url = self.vulnerable_urls[0]
|
||||||
|
self.vulnerable_port = HTTPTools.get_port_from_url(self.target_url)
|
||||||
|
|
||||||
# Skip if monkey already exists and this option is given
|
# Skip if monkey already exists and this option is given
|
||||||
if not exploit_config['blind_exploit'] and self.skip_exist and self.check_remote_files(self.vulnerable_urls[0]):
|
if not exploit_config['blind_exploit'] and self.skip_exist and self.check_remote_files(self.target_url):
|
||||||
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
|
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Check for targets architecture (if it's 32 or 64 bit)
|
# Check for targets architecture (if it's 32 or 64 bit)
|
||||||
if not exploit_config['blind_exploit'] and not self.set_host_arch(self.vulnerable_urls[0]):
|
if not exploit_config['blind_exploit'] and not self.set_host_arch(self.target_url):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Upload the right monkey to target
|
# Upload the right monkey to target
|
||||||
data = self.upload_monkey(self.vulnerable_urls[0], exploit_config['upload_commands'])
|
data = self.upload_monkey(self.target_url, exploit_config['upload_commands'])
|
||||||
|
|
||||||
if data is False:
|
if data is False:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Change permissions to transform monkey into executable file
|
# Change permissions to transform monkey into executable file
|
||||||
if self.change_permissions(self.vulnerable_urls[0], data['path']) is False:
|
if self.change_permissions(self.target_url, data['path']) is False:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Execute remote monkey
|
# Execute remote monkey
|
||||||
if self.execute_remote_monkey(self.vulnerable_urls[0], data['path'], exploit_config['dropper']) is False:
|
if self.execute_remote_monkey(self.target_url, data['path'], exploit_config['dropper']) is False:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -403,10 +408,15 @@ class WebRCE(HostExploiter):
|
||||||
default_path = self.get_default_dropper_path()
|
default_path = self.get_default_dropper_path()
|
||||||
if default_path is False:
|
if default_path is False:
|
||||||
return False
|
return False
|
||||||
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1, default_path)
|
monkey_cmd = build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
self.vulnerable_port,
|
||||||
|
default_path)
|
||||||
command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': DROPPER_ARG, 'parameters': monkey_cmd}
|
command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': DROPPER_ARG, 'parameters': monkey_cmd}
|
||||||
else:
|
else:
|
||||||
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
monkey_cmd = build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
self.vulnerable_port)
|
||||||
command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': MONKEY_ARG, 'parameters': monkey_cmd}
|
command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': MONKEY_ARG, 'parameters': monkey_cmd}
|
||||||
try:
|
try:
|
||||||
LOG.info("Trying to execute monkey using command: {}".format(command))
|
LOG.info("Trying to execute monkey using command: {}".format(command))
|
||||||
|
@ -489,3 +499,6 @@ class WebRCE(HostExploiter):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOG.debug("Target's machine type was not set. Using win-32 dropper path.")
|
LOG.debug("Target's machine type was not set. Using win-32 dropper path.")
|
||||||
return self._config.dropper_target_path_win_32
|
return self._config.dropper_target_path_win_32
|
||||||
|
|
||||||
|
def set_vulnerable_port_from_url(self, url):
|
||||||
|
self.vulnerable_port = HTTPTools.get_port_from_url(url)
|
||||||
|
|
|
@ -234,11 +234,15 @@ class Ms08_067_Exploiter(HostExploiter):
|
||||||
# execute the remote dropper in case the path isn't final
|
# execute the remote dropper in case the path isn't final
|
||||||
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
||||||
cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \
|
cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \
|
||||||
build_monkey_commandline(self.host, get_monkey_depth() - 1,
|
build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
SRVSVC_Exploit.TELNET_PORT,
|
||||||
self._config.dropper_target_path_win_32)
|
self._config.dropper_target_path_win_32)
|
||||||
else:
|
else:
|
||||||
cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \
|
cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \
|
||||||
build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
vulnerable_port=SRVSVC_Exploit.TELNET_PORT)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sock.send("start %s\r\n" % (cmdline,))
|
sock.send("start %s\r\n" % (cmdline,))
|
||||||
|
|
|
@ -21,6 +21,7 @@ class WmiExploiter(HostExploiter):
|
||||||
_TARGET_OS_TYPE = ['windows']
|
_TARGET_OS_TYPE = ['windows']
|
||||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||||
_EXPLOITED_SERVICE = 'WMI (Windows Management Instrumentation)'
|
_EXPLOITED_SERVICE = 'WMI (Windows Management Instrumentation)'
|
||||||
|
VULNERABLE_PORT = 135
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
super(WmiExploiter, self).__init__(host)
|
super(WmiExploiter, self).__init__(host)
|
||||||
|
@ -94,11 +95,15 @@ class WmiExploiter(HostExploiter):
|
||||||
# execute the remote dropper in case the path isn't final
|
# execute the remote dropper in case the path isn't final
|
||||||
elif remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
elif remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
||||||
cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \
|
cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \
|
||||||
build_monkey_commandline(
|
build_monkey_commandline(self.host,
|
||||||
self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32)
|
get_monkey_depth() - 1,
|
||||||
|
WmiExploiter.VULNERABLE_PORT,
|
||||||
|
self._config.dropper_target_path_win_32)
|
||||||
else:
|
else:
|
||||||
cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \
|
cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \
|
||||||
build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
build_monkey_commandline(self.host,
|
||||||
|
get_monkey_depth() - 1,
|
||||||
|
WmiExploiter.VULNERABLE_PORT)
|
||||||
|
|
||||||
# execute the remote monkey
|
# execute the remote monkey
|
||||||
result = WmiTools.get_object(wmi_connection, "Win32_Process").Create(cmdline,
|
result = WmiTools.get_object(wmi_connection, "Win32_Process").Create(cmdline,
|
||||||
|
|
|
@ -10,6 +10,7 @@ from infection_monkey.network.HostFinger import HostFinger
|
||||||
from infection_monkey.utils.monkey_dir import create_monkey_dir, get_monkey_dir_path, remove_monkey_dir
|
from infection_monkey.utils.monkey_dir import create_monkey_dir, get_monkey_dir_path, remove_monkey_dir
|
||||||
from infection_monkey.utils.monkey_log_path import get_monkey_log_path
|
from infection_monkey.utils.monkey_log_path import get_monkey_log_path
|
||||||
from infection_monkey.utils.environment import is_windows_os
|
from infection_monkey.utils.environment import is_windows_os
|
||||||
|
from infection_monkey.utils.exceptions.planned_shutdown_exception import PlannedShutdownException
|
||||||
from infection_monkey.config import WormConfiguration
|
from infection_monkey.config import WormConfiguration
|
||||||
from infection_monkey.control import ControlClient
|
from infection_monkey.control import ControlClient
|
||||||
from infection_monkey.model import DELAY_DELETE_CMD
|
from infection_monkey.model import DELAY_DELETE_CMD
|
||||||
|
@ -26,12 +27,13 @@ from infection_monkey.telemetry.trace_telem import TraceTelem
|
||||||
from infection_monkey.telemetry.tunnel_telem import TunnelTelem
|
from infection_monkey.telemetry.tunnel_telem import TunnelTelem
|
||||||
from infection_monkey.windows_upgrader import WindowsUpgrader
|
from infection_monkey.windows_upgrader import WindowsUpgrader
|
||||||
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
||||||
from infection_monkey.network.tools import get_interface_to_target
|
from infection_monkey.network.tools import get_interface_to_target, is_running_on_server
|
||||||
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError
|
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError
|
||||||
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
||||||
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
|
||||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||||
|
from monkey_island.cc.network_utils import remove_port_from_ip_string
|
||||||
|
|
||||||
MAX_DEPTH_REACHED_MESSAGE = "Reached max depth, shutting down"
|
MAX_DEPTH_REACHED_MESSAGE = "Reached max depth, shutting down"
|
||||||
|
|
||||||
|
@ -40,10 +42,6 @@ __author__ = 'itamar'
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PlannedShutdownException(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InfectionMonkey(object):
|
class InfectionMonkey(object):
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
self._keep_running = False
|
self._keep_running = False
|
||||||
|
@ -74,7 +72,9 @@ class InfectionMonkey(object):
|
||||||
arg_parser.add_argument('-t', '--tunnel')
|
arg_parser.add_argument('-t', '--tunnel')
|
||||||
arg_parser.add_argument('-s', '--server')
|
arg_parser.add_argument('-s', '--server')
|
||||||
arg_parser.add_argument('-d', '--depth', type=int)
|
arg_parser.add_argument('-d', '--depth', type=int)
|
||||||
|
arg_parser.add_argument('-vp', '--vulnerable-port')
|
||||||
self._opts, self._args = arg_parser.parse_known_args(self._args)
|
self._opts, self._args = arg_parser.parse_known_args(self._args)
|
||||||
|
self.log_arguments()
|
||||||
|
|
||||||
self._parent = self._opts.parent
|
self._parent = self._opts.parent
|
||||||
self._default_tunnel = self._opts.tunnel
|
self._default_tunnel = self._opts.tunnel
|
||||||
|
@ -119,6 +119,10 @@ class InfectionMonkey(object):
|
||||||
|
|
||||||
self.shutdown_by_not_alive_config()
|
self.shutdown_by_not_alive_config()
|
||||||
|
|
||||||
|
if self.is_started_on_island():
|
||||||
|
ControlClient.report_start_on_island()
|
||||||
|
ControlClient.should_monkey_run(self._opts.vulnerable_port)
|
||||||
|
|
||||||
if firewall.is_enabled():
|
if firewall.is_enabled():
|
||||||
firewall.add_firewall_rule()
|
firewall.add_firewall_rule()
|
||||||
|
|
||||||
|
@ -380,3 +384,11 @@ class InfectionMonkey(object):
|
||||||
raise PlannedShutdownException("Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel))
|
raise PlannedShutdownException("Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel))
|
||||||
self._default_server = WormConfiguration.current_server
|
self._default_server = WormConfiguration.current_server
|
||||||
LOG.debug("default server set to: %s" % self._default_server)
|
LOG.debug("default server set to: %s" % self._default_server)
|
||||||
|
|
||||||
|
def is_started_on_island(self):
|
||||||
|
island_ip = remove_port_from_ip_string(self._default_server)
|
||||||
|
return is_running_on_server(island_ip) and WormConfiguration.depth == WormConfiguration.max_depth
|
||||||
|
|
||||||
|
def log_arguments(self):
|
||||||
|
arg_string = " ".join([f"{key}: {value}" for key, value in vars(self._opts).items()])
|
||||||
|
LOG.info(f"Monkey started with arguments: {arg_string}")
|
||||||
|
|
|
@ -7,7 +7,7 @@ import struct
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from infection_monkey.network.info import get_routes
|
from infection_monkey.network.info import get_routes, local_ips
|
||||||
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
||||||
from infection_monkey.utils.environment import is_64bit_python
|
from infection_monkey.utils.environment import is_64bit_python
|
||||||
|
|
||||||
|
@ -309,3 +309,7 @@ def get_interface_to_target(dst):
|
||||||
paths.sort()
|
paths.sort()
|
||||||
ret = paths[-1][1]
|
ret = paths[-1][1]
|
||||||
return ret[1]
|
return ret[1]
|
||||||
|
|
||||||
|
|
||||||
|
def is_running_on_server(ip: str) -> bool:
|
||||||
|
return ip in local_ips()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
class PlannedShutdownException(Exception):
|
||||||
|
pass
|
|
@ -16,10 +16,12 @@ from monkey_island.cc.resources.island_logs import IslandLog
|
||||||
from monkey_island.cc.resources.monkey import Monkey
|
from monkey_island.cc.resources.monkey import Monkey
|
||||||
from monkey_island.cc.resources.monkey_configuration import MonkeyConfiguration
|
from monkey_island.cc.resources.monkey_configuration import MonkeyConfiguration
|
||||||
from monkey_island.cc.resources.island_configuration import IslandConfiguration
|
from monkey_island.cc.resources.island_configuration import IslandConfiguration
|
||||||
|
from monkey_island.cc.resources.monkey_control.started_on_island import StartedOnIsland
|
||||||
from monkey_island.cc.resources.monkey_download import MonkeyDownload
|
from monkey_island.cc.resources.monkey_download import MonkeyDownload
|
||||||
from monkey_island.cc.resources.netmap import NetMap
|
from monkey_island.cc.resources.netmap import NetMap
|
||||||
from monkey_island.cc.resources.node import Node
|
from monkey_island.cc.resources.node import Node
|
||||||
from monkey_island.cc.resources.node_states import NodeStates
|
from monkey_island.cc.resources.node_states import NodeStates
|
||||||
|
from monkey_island.cc.resources.monkey_control.remote_port_check import RemotePortCheck
|
||||||
from monkey_island.cc.resources.remote_run import RemoteRun
|
from monkey_island.cc.resources.remote_run import RemoteRun
|
||||||
from monkey_island.cc.resources.reporting.report import Report
|
from monkey_island.cc.resources.reporting.report import Report
|
||||||
from monkey_island.cc.resources.root import Root
|
from monkey_island.cc.resources.root import Root
|
||||||
|
@ -121,6 +123,8 @@ def init_api_resources(api):
|
||||||
api.add_resource(AttackConfiguration, '/api/attack')
|
api.add_resource(AttackConfiguration, '/api/attack')
|
||||||
api.add_resource(AttackReport, '/api/attack/report')
|
api.add_resource(AttackReport, '/api/attack/report')
|
||||||
api.add_resource(VersionUpdate, '/api/version-update', '/api/version-update/')
|
api.add_resource(VersionUpdate, '/api/version-update', '/api/version-update/')
|
||||||
|
api.add_resource(RemotePortCheck, '/api/monkey_control/check_remote_port/<string:port>')
|
||||||
|
api.add_resource(StartedOnIsland, '/api/monkey_control/started_on_island')
|
||||||
|
|
||||||
api.add_resource(MonkeyTest, '/api/test/monkey')
|
api.add_resource(MonkeyTest, '/api/test/monkey')
|
||||||
api.add_resource(ClearCaches, '/api/test/clear_caches')
|
api.add_resource(ClearCaches, '/api/test/clear_caches')
|
||||||
|
|
|
@ -10,7 +10,7 @@ import pymongo
|
||||||
from monkey_island.cc.environment import Environment
|
from monkey_island.cc.environment import Environment
|
||||||
|
|
||||||
# Disable "unverified certificate" warnings when sending requests to island
|
# Disable "unverified certificate" warnings when sending requests to island
|
||||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # noqa: DUO131
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,9 @@ class BootloaderHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||||
post_data = self.rfile.read(content_length).decode()
|
post_data = self.rfile.read(content_length).decode()
|
||||||
island_server_path = BootloaderHTTPRequestHandler.get_bootloader_resource_url(self.request.getsockname()[0])
|
island_server_path = BootloaderHTTPRequestHandler.get_bootloader_resource_url(self.request.getsockname()[0])
|
||||||
island_server_path = parse.urljoin(island_server_path, self.path[1:])
|
island_server_path = parse.urljoin(island_server_path, self.path[1:])
|
||||||
r = requests.post(url=island_server_path, data=post_data, verify=False)
|
# The island server doesn't always have a correct SSL cert installed (By default it comes with a self signed one),
|
||||||
|
# that's why we're not verifying the cert in this request.
|
||||||
|
r = requests.post(url=island_server_path, data=post_data, verify=False) # noqa: DUO123
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
|
|
|
@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from monkey_island.cc.app import init_app
|
from monkey_island.cc.app import init_app
|
||||||
from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list
|
from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list
|
||||||
from monkey_island.cc.utils import local_ip_addresses
|
from monkey_island.cc.network_utils import local_ip_addresses
|
||||||
from monkey_island.cc.environment.environment import env
|
from monkey_island.cc.environment.environment import env
|
||||||
from monkey_island.cc.database import is_db_server_up, get_db_version
|
from monkey_island.cc.database import is_db_server_up, get_db_version
|
||||||
from monkey_island.cc.resources.monkey_download import MonkeyDownload
|
from monkey_island.cc.resources.monkey_download import MonkeyDownload
|
||||||
|
|
|
@ -8,7 +8,7 @@ import ring
|
||||||
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.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS
|
from monkey_island.cc.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS
|
||||||
from monkey_island.cc.models.command_control_channel import CommandControlChannel
|
from monkey_island.cc.models.command_control_channel import CommandControlChannel
|
||||||
from monkey_island.cc.utils import local_ip_addresses
|
from monkey_island.cc.network_utils import local_ip_addresses
|
||||||
from common.cloud import environment_names
|
from common.cloud import environment_names
|
||||||
|
|
||||||
MAX_MONKEYS_AMOUNT_TO_CACHE = 100
|
MAX_MONKEYS_AMOUNT_TO_CACHE = 100
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
import array
|
||||||
|
import collections
|
||||||
|
import ipaddress
|
||||||
import socket
|
import socket
|
||||||
|
import struct
|
||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List
|
||||||
import collections
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import array
|
|
||||||
|
|
||||||
import struct
|
|
||||||
import ipaddress
|
|
||||||
from netifaces import interfaces, ifaddresses, AF_INET
|
from netifaces import interfaces, ifaddresses, AF_INET
|
||||||
from ring import lru
|
from ring import lru
|
||||||
|
|
||||||
|
@ -86,3 +86,8 @@ def get_subnets():
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
return subnets
|
return subnets
|
||||||
|
|
||||||
|
|
||||||
|
def remove_port_from_ip_string(ip_string: str) -> str:
|
||||||
|
url = urlparse("http://" + ip_string)
|
||||||
|
return str(url.hostname)
|
|
@ -10,7 +10,7 @@ from monkey_island.cc.environment.environment import env
|
||||||
from monkey_island.cc.models import Monkey
|
from monkey_island.cc.models import Monkey
|
||||||
from monkey_island.cc.resources.monkey_download import get_monkey_executable
|
from monkey_island.cc.resources.monkey_download import get_monkey_executable
|
||||||
from monkey_island.cc.services.node import NodeService
|
from monkey_island.cc.services.node import NodeService
|
||||||
from monkey_island.cc.utils import local_ip_addresses
|
from monkey_island.cc.network_utils import local_ip_addresses
|
||||||
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
||||||
|
|
||||||
__author__ = 'Barak'
|
__author__ = 'Barak'
|
||||||
|
|
|
@ -58,6 +58,7 @@ class Monkey(flask_restful.Resource):
|
||||||
return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False)
|
return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False)
|
||||||
|
|
||||||
# Used by monkey. can't secure.
|
# Used by monkey. can't secure.
|
||||||
|
# Called on monkey wakeup to initialize local configuration
|
||||||
@TestTelemStore.store_test_telem
|
@TestTelemStore.store_test_telem
|
||||||
def post(self, **kw):
|
def post(self, **kw):
|
||||||
monkey_json = json.loads(request.data)
|
monkey_json = json.loads(request.data)
|
||||||
|
@ -74,16 +75,11 @@ class Monkey(flask_restful.Resource):
|
||||||
|
|
||||||
# if new monkey telem, change config according to "new monkeys" config.
|
# if new monkey telem, change config according to "new monkeys" config.
|
||||||
db_monkey = mongo.db.monkey.find_one({"guid": monkey_json["guid"]})
|
db_monkey = mongo.db.monkey.find_one({"guid": monkey_json["guid"]})
|
||||||
if not db_monkey:
|
|
||||||
# we pull it encrypted because we then decrypt it for the monkey in get
|
# Update monkey configuration
|
||||||
new_config = ConfigService.get_flat_config(False, False)
|
new_config = ConfigService.get_flat_config(False, False)
|
||||||
monkey_json['config'] = monkey_json.get('config', {})
|
monkey_json['config'] = monkey_json.get('config', {})
|
||||||
monkey_json['config'].update(new_config)
|
monkey_json['config'].update(new_config)
|
||||||
else:
|
|
||||||
db_config = db_monkey.get('config', {})
|
|
||||||
if 'current_server' in db_config:
|
|
||||||
del db_config['current_server']
|
|
||||||
monkey_json.get('config', {}).update(db_config)
|
|
||||||
|
|
||||||
# try to find new monkey parent
|
# try to find new monkey parent
|
||||||
parent = monkey_json.get('parent')
|
parent = monkey_json.get('parent')
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import flask_restful
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
from monkey_island.cc.services.remote_port_check import check_tcp_port
|
||||||
|
|
||||||
|
|
||||||
|
class RemotePortCheck(flask_restful.Resource):
|
||||||
|
|
||||||
|
# Used by monkey. can't secure.
|
||||||
|
def get(self, port):
|
||||||
|
if port and check_tcp_port(request.remote_addr, port):
|
||||||
|
return {"status": "port_visible"}
|
||||||
|
else:
|
||||||
|
return {"status": "port_invisible"}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
import flask_restful
|
||||||
|
from flask import request, make_response
|
||||||
|
|
||||||
|
from monkey_island.cc.services.config import ConfigService
|
||||||
|
|
||||||
|
|
||||||
|
class StartedOnIsland(flask_restful.Resource):
|
||||||
|
|
||||||
|
# Used by monkey. can't secure.
|
||||||
|
def post(self):
|
||||||
|
data = json.loads(request.data)
|
||||||
|
if data['started_on_island']:
|
||||||
|
ConfigService.set_started_on_island(True)
|
||||||
|
return make_response({}, 200)
|
|
@ -8,7 +8,7 @@ from monkey_island.cc.auth import jwt_required
|
||||||
from monkey_island.cc.database import mongo
|
from monkey_island.cc.database import mongo
|
||||||
from monkey_island.cc.services.database import Database
|
from monkey_island.cc.services.database import Database
|
||||||
from monkey_island.cc.services.infection_lifecycle import InfectionLifecycle
|
from monkey_island.cc.services.infection_lifecycle import InfectionLifecycle
|
||||||
from monkey_island.cc.utils import local_ip_addresses
|
from monkey_island.cc.network_utils import local_ip_addresses
|
||||||
|
|
||||||
__author__ = 'Barak'
|
__author__ = 'Barak'
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class TestTelemStore:
|
class TestTelemStore:
|
||||||
|
|
||||||
|
TELEMS_EXPORTED = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def store_test_telem(f):
|
def store_test_telem(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@ -46,6 +48,7 @@ class TestTelemStore:
|
||||||
for test_telem in TestTelem.objects():
|
for test_telem in TestTelem.objects():
|
||||||
with open(TestTelemStore.get_unique_file_path_for_test_telem(TELEM_SAMPLE_DIR, test_telem), 'w') as file:
|
with open(TestTelemStore.get_unique_file_path_for_test_telem(TELEM_SAMPLE_DIR, test_telem), 'w') as file:
|
||||||
file.write(test_telem.to_json(indent=2))
|
file.write(test_telem.to_json(indent=2))
|
||||||
|
TestTelemStore.TELEMS_EXPORTED = True
|
||||||
logger.info("Telemetries exported!")
|
logger.info("Telemetries exported!")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -7,7 +7,7 @@ import monkey_island.cc.services.post_breach_files
|
||||||
|
|
||||||
from monkey_island.cc.database import mongo
|
from monkey_island.cc.database import mongo
|
||||||
from monkey_island.cc.environment.environment import env
|
from monkey_island.cc.environment.environment import env
|
||||||
from monkey_island.cc.utils import local_ip_addresses
|
from monkey_island.cc.network_utils import local_ip_addresses
|
||||||
from .config_schema import SCHEMA
|
from .config_schema import SCHEMA
|
||||||
from monkey_island.cc.encryptor import encryptor
|
from monkey_island.cc.encryptor import encryptor
|
||||||
|
|
||||||
|
@ -74,6 +74,12 @@ class ConfigService:
|
||||||
mongo.db.config.update({'name': 'newconfig'},
|
mongo.db.config.update({'name': 'newconfig'},
|
||||||
{"$set": {mongo_key: value}})
|
{"$set": {mongo_key: value}})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def append_to_config_array(config_key_as_arr, value):
|
||||||
|
mongo_key = ".".join(config_key_as_arr)
|
||||||
|
mongo.db.config.update({'name': 'newconfig'},
|
||||||
|
{"$push": {mongo_key: value}})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_flat_config(is_initial_config=False, should_decrypt=True):
|
def get_flat_config(is_initial_config=False, should_decrypt=True):
|
||||||
config_json = ConfigService.get_config(is_initial_config, should_decrypt)
|
config_json = ConfigService.get_config(is_initial_config, should_decrypt)
|
||||||
|
@ -311,3 +317,7 @@ class ConfigService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_test_telem_export_enabled():
|
def is_test_telem_export_enabled():
|
||||||
return ConfigService.get_config_value(['internal', 'testing', 'export_monkey_telems'])
|
return ConfigService.get_config_value(['internal', 'testing', 'export_monkey_telems'])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_started_on_island(value: bool):
|
||||||
|
ConfigService.set_config_value(['internal', 'general', 'started_on_island'], value)
|
||||||
|
|
|
@ -564,6 +564,13 @@ SCHEMA = {
|
||||||
"default": r"monkey_dir",
|
"default": r"monkey_dir",
|
||||||
"description": "Directory name for the directory which will contain all of the monkey files"
|
"description": "Directory name for the directory which will contain all of the monkey files"
|
||||||
},
|
},
|
||||||
|
"started_on_island": {
|
||||||
|
"title": "Started on island",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
"description": "Was exploitation started from island"
|
||||||
|
"(did monkey with max depth ran on island)"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"classes": {
|
"classes": {
|
||||||
|
|
|
@ -47,5 +47,5 @@ class InfectionLifecycle:
|
||||||
# we want to skip and reply.
|
# we want to skip and reply.
|
||||||
if not is_report_being_generated() and not ReportService.is_latest_report_exists():
|
if not is_report_being_generated() and not ReportService.is_latest_report_exists():
|
||||||
safe_generate_reports()
|
safe_generate_reports()
|
||||||
if ConfigService.is_test_telem_export_enabled():
|
if ConfigService.is_test_telem_export_enabled() and not TestTelemStore.TELEMS_EXPORTED:
|
||||||
TestTelemStore.export_test_telems()
|
TestTelemStore.export_test_telems()
|
||||||
|
|
|
@ -8,7 +8,7 @@ import monkey_island.cc.services.log
|
||||||
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.services.edge import EdgeService
|
from monkey_island.cc.services.edge import EdgeService
|
||||||
from monkey_island.cc.utils import local_ip_addresses, is_local_ips
|
from monkey_island.cc.network_utils import local_ip_addresses, is_local_ips
|
||||||
from monkey_island.cc import models
|
from monkey_island.cc import models
|
||||||
from monkey_island.cc.services.utils.node_states import NodeStates
|
from monkey_island.cc.services.utils.node_states import NodeStates
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT = 5 # Seconds
|
||||||
|
|
||||||
|
|
||||||
|
def check_tcp_port(ip: str, port: str, timeout=DEFAULT_TIMEOUT) -> bool:
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.settimeout(timeout)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sock.connect((ip, int(port)))
|
||||||
|
except socket.timeout:
|
||||||
|
return False
|
||||||
|
except socket.error:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
sock.close()
|
||||||
|
return True
|
|
@ -16,7 +16,7 @@ from monkey_island.cc.services.node import NodeService
|
||||||
from monkey_island.cc.services.reporting.pth_report import PTHReportService
|
from monkey_island.cc.services.reporting.pth_report import PTHReportService
|
||||||
from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager
|
from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager
|
||||||
from monkey_island.cc.services.reporting.report_generation_synchronisation import safe_generate_regular_report
|
from monkey_island.cc.services.reporting.report_generation_synchronisation import safe_generate_regular_report
|
||||||
from monkey_island.cc.utils import local_ip_addresses, get_subnets
|
from monkey_island.cc.network_utils import local_ip_addresses, get_subnets
|
||||||
|
|
||||||
__author__ = "itay.mizeretz"
|
__author__ = "itay.mizeretz"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from ipaddress import ip_address
|
||||||
|
|
||||||
from monkey_island.cc.encryptor import encryptor
|
from monkey_island.cc.encryptor import encryptor
|
||||||
from monkey_island.cc.services import mimikatz_utils
|
from monkey_island.cc.services import mimikatz_utils
|
||||||
|
|
|
@ -30,7 +30,6 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
lastAction: 'none',
|
lastAction: 'none',
|
||||||
sections: [],
|
sections: [],
|
||||||
selectedSection: 'attack',
|
selectedSection: 'attack',
|
||||||
monkeysRan: false,
|
|
||||||
PBAwinFile: [],
|
PBAwinFile: [],
|
||||||
PBAlinuxFile: [],
|
PBAlinuxFile: [],
|
||||||
showAttackAlert: false
|
showAttackAlert: false
|
||||||
|
@ -70,7 +69,11 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
cnc: {},
|
cnc: {},
|
||||||
network: {},
|
network: {},
|
||||||
exploits: {},
|
exploits: {},
|
||||||
internal: {}
|
internal: {
|
||||||
|
general: {
|
||||||
|
started_on_island: {'ui:widget': 'hidden'}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +111,6 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
selectedSection: 'attack'
|
selectedSection: 'attack'
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
this.updateMonkeysRunning();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
updateConfig = () => {
|
updateConfig = () => {
|
||||||
|
@ -359,14 +361,6 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
event.target.value = null;
|
event.target.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateMonkeysRunning = () => {
|
|
||||||
this.authFetch('/api')
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(res => {
|
|
||||||
this.setState({monkeysRan: res['completed_steps']['run_monkey']});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
PBAwindows = () => {
|
PBAwindows = () => {
|
||||||
return (<FilePond
|
return (<FilePond
|
||||||
server={{
|
server={{
|
||||||
|
@ -464,19 +458,6 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
</div>)
|
</div>)
|
||||||
};
|
};
|
||||||
|
|
||||||
renderConfigWontChangeWarning = () => {
|
|
||||||
return (<div>
|
|
||||||
{this.state.monkeysRan ?
|
|
||||||
<div className="alert alert-warning">
|
|
||||||
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
|
|
||||||
Changed configuration will only apply to new infections.
|
|
||||||
"Start over" to run again with different configuration.
|
|
||||||
</div>
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
</div>)
|
|
||||||
};
|
|
||||||
|
|
||||||
renderBasicNetworkWarning = () => {
|
renderBasicNetworkWarning = () => {
|
||||||
if (this.state.selectedSection === 'basic_network') {
|
if (this.state.selectedSection === 'basic_network') {
|
||||||
return (<div className="alert alert-info">
|
return (<div className="alert alert-info">
|
||||||
|
@ -514,7 +495,6 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
{this.renderAttackAlertModal()}
|
{this.renderAttackAlertModal()}
|
||||||
<h1 className="page-title">Monkey Configuration</h1>
|
<h1 className="page-title">Monkey Configuration</h1>
|
||||||
{this.renderNav()}
|
{this.renderNav()}
|
||||||
{this.renderConfigWontChangeWarning()}
|
|
||||||
{content}
|
{content}
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<button type="submit" onClick={this.onSubmit} className="btn btn-success btn-lg" style={{margin: '5px'}}>
|
<button type="submit" onClick={this.onSubmit} className="btn btn-success btn-lg" style={{margin: '5px'}}>
|
||||||
|
|
Loading…
Reference in New Issue