diff --git a/.swm/JFXftJml8DpmuCPBA9rL.swm b/.swm/JFXftJml8DpmuCPBA9rL.swm index 154c473bd..d0206a862 100644 --- a/.swm/JFXftJml8DpmuCPBA9rL.swm +++ b/.swm/JFXftJml8DpmuCPBA9rL.swm @@ -22,17 +22,18 @@ "hunkComments": [] }, "hunkDiffLines": [ - "@@ -62,15 +62,7 @@", + "@@ -68,16 +68,7 @@", " \"Removes the file afterwards.\",", " \"attack_techniques\": [\"T1166\"]", " },", "- {", - "+ # Swimmer: ADD DETAILS HERE!\",", + "+ # Swimmer: ADD DETAILS HERE!", "- \"type\": \"string\",", "- \"enum\": [", "- \"ScheduleJobs\"", "- ],", "- \"title\": \"Job scheduling\",", + "- \"safe\": True,", "- \"info\": \"Attempts to create a scheduled job on the system and remove it.\",", "- \"attack_techniques\": [\"T1168\", \"T1053\"]", "- },", @@ -45,5 +46,9 @@ } }, "app_version": "0.3.5-1", - "file_version": "1.0.4" + "file_version": "1.0.4", + "hunksOrder": [ + "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py_0" + ], + "last_commit_sha_for_swimm_patch": "9d9e8168fb2c23367b9947273aa1a041687b3e2e" } \ No newline at end of file diff --git a/.swm/VW4rf3AxRslfT7lwaug7.swm b/.swm/VW4rf3AxRslfT7lwaug7.swm index c4e8df928..7af1a816c 100644 --- a/.swm/VW4rf3AxRslfT7lwaug7.swm +++ b/.swm/VW4rf3AxRslfT7lwaug7.swm @@ -23,26 +23,29 @@ "hunkComments": [] }, "hunkDiffLines": [ - "@@ -10,12 +10,6 @@", + "@@ -10,11 +10,5 @@", " \"\"\"", " ", " def __init__(self):", "- linux_cmds, windows_cmds = get_commands_to_schedule_jobs()", "+ pass", - " ", - "- super(ScheduleJobs, self).__init__(name=POST_BREACH_JOB_SCHEDULING,", + "-", "+ # Swimmer: IMPLEMENT HERE!", + "- super(ScheduleJobs, self).__init__(name=POST_BREACH_JOB_SCHEDULING,", "- linux_cmd=' '.join(linux_cmds),", "- windows_cmd=windows_cmds)", "- ", "- def run(self):", - "- super(ScheduleJobs, self).run()", - "- remove_scheduled_jobs()" + "- super(ScheduleJobs, self).run()" ] } ] } }, "app_version": "0.3.5-1", - "file_version": "1.0.4" + "file_version": "1.0.4", + "hunksOrder": [ + "monkey/infection_monkey/post_breach/actions/schedule_jobs.py_0" + ], + "last_commit_sha_for_swimm_patch": "44fd1ab69cfbab33cec638dcbbaa8831992a9a9f" } \ No newline at end of file diff --git a/.swm/tbxb2cGgUiJQ8Btma0fp.swm b/.swm/tbxb2cGgUiJQ8Btma0fp.swm index 406ded500..78a13c872 100644 --- a/.swm/tbxb2cGgUiJQ8Btma0fp.swm +++ b/.swm/tbxb2cGgUiJQ8Btma0fp.swm @@ -81,7 +81,7 @@ "@@ -1,5 +1,5 @@", " from common.data.post_breach_consts import (", "- POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER)", - "+ POST_BREACH_BACKDOOR_USER)", + "+ POST_BREACH_COMMUNICATE_AS_NEW_USER)", " from monkey_island.cc.services.attack.technique_reports.pba_technique import \\", " PostBreachTechnique", " " @@ -97,7 +97,7 @@ " 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_BACKDOOR_USER]" + "+ pba_names = [POST_BREACH_COMMUNICATE_AS_NEW_USER]" ] } ] @@ -111,7 +111,7 @@ "hunkComments": [] }, "hunkDiffLines": [ - "@@ -4,15 +4,7 @@", + "@@ -4,16 +4,7 @@", " \"might do after breaching a new machine. Used in ATT&CK and Zero trust reports.\",", " \"type\": \"string\",", " \"anyOf\": [", @@ -122,6 +122,7 @@ "- \"BackdoorUser\"", "- ],", "- \"title\": \"Back door user\",", + "- \"safe\": True,", "- \"info\": \"Attempts to create a new user on the system and delete it afterwards.\",", "- \"attack_techniques\": [\"T1136\"]", "- },", @@ -134,5 +135,13 @@ } }, "app_version": "0.3.5-1", - "file_version": "1.0.4" + "file_version": "1.0.4", + "hunksOrder": [ + "monkey/common/data/post_breach_consts.py_0", + "monkey/infection_monkey/post_breach/actions/add_user.py_0", + "monkey/monkey_island/cc/services/attack/technique_reports/T1136.py_0", + "monkey/monkey_island/cc/services/attack/technique_reports/T1136.py_1", + "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py_0" + ], + "last_commit_sha_for_swimm_patch": "9d9e8168fb2c23367b9947273aa1a041687b3e2e" } \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 887b7cc67..ca03d20b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,23 +60,20 @@ before_script: script: # Check Python code -## Check syntax errors and fail the build if any are found. -- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics +- flake8 ./monkey --config=./ci_scripts/flake8_syntax_check.ini ## Warn about linter issues. ### --exit-zero forces Flake8 to use the exit status code 0 even if there are errors, which means this will NOT fail the build. -### --count will print the total number of errors. -### --statistics Count the number of occurrences of each error/warning code and print a report. ### The output is redirected to a file. -- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics > flake8_warnings.txt +- flake8 ./monkey --exit-zero --config=./ci_scripts/flake8_linter_check.ini > ./ci_scripts/flake8_warnings.txt ## Display the linter issues -- cat flake8_warnings.txt +- cat ./ci_scripts/flake8_warnings.txt ## Make sure that we haven't increased the amount of warnings. - PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT=80 -- if [ $(tail -n 1 flake8_warnings.txt) -gt $PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT ]; then echo "Too many python linter warnings! Failing this build. Lower the amount of linter errors in this and try again. " && exit 1; fi +- if [ $(tail -n 1 ./ci_scripts/flake8_warnings.txt) -gt $PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT ]; then echo "Too many python linter warnings! Failing this build. Lower the amount of linter errors in this and try again. " && exit 1; fi ## Check import order -- python -m isort . -c -p common -p infection_monkey -p monkey_island +- python -m isort ./monkey --settings-file ./ci_scripts/isort.cfg ## Run unit tests - cd monkey # This is our source dir @@ -98,10 +95,11 @@ script: # verify swimm - cd $TRAVIS_BUILD_DIR -- wget https://firebasestorage.googleapis.com/v0/b/swimmio.appspot.com/o/Release%2Fv018%2Fswimm-0.1.8-linux-executable\?alt\=media\&token\=e59c0a18-577f-4b77-bb3b-91b22c3d8b2a -O swimm +- wget "https://firebasestorage.googleapis.com/v0/b/swimmio.appspot.com/o/Release%2Fv029%2FSwimm_0.2.9_Setup.deb?alt=media&token=774ebd98-cb4e-4615-900c-aada224c1608" -O swimm +- sudo dpkg -i swimm || (sudo apt-get update && sudo apt-get -f install) - chmod +x ./swimm -- ./swimm --version -- ./swimm verify +- swimm --version +- swimm verify after_success: # Upload code coverage results to codecov.io, see https://github.com/codecov/codecov-bash for more information diff --git a/ci_scripts/.gitignore b/ci_scripts/.gitignore new file mode 100644 index 000000000..67f93fcdc --- /dev/null +++ b/ci_scripts/.gitignore @@ -0,0 +1,2 @@ +./validation-env +./flake8_warnings.txt diff --git a/ci_scripts/README.md b/ci_scripts/README.md new file mode 100644 index 000000000..09330d298 --- /dev/null +++ b/ci_scripts/README.md @@ -0,0 +1,8 @@ +# About + +Run this script to validate your code locally and auto fix/format the problems before pushing. + +# Usage + +You've got to manually download swimm for swimm validation. +run from `infection_monkey` directory: `powershell .\ci_scripts\validate.ps1` diff --git a/ci_scripts/flake8_linter_check.ini b/ci_scripts/flake8_linter_check.ini new file mode 100644 index 000000000..b8daeaf70 --- /dev/null +++ b/ci_scripts/flake8_linter_check.ini @@ -0,0 +1,15 @@ +[flake8] +## Warn about linter issues. + +exclude = ../monkey/monkey_island/cc/ui, + ../monkey/common/cloud +show-source = True +max-complexity = 10 +max-line-length = 127 + +### --statistics Count the number of occurrences of each error/warning code and print a report. +statistics = True + +### --count will print the total number of errors. +count = True + diff --git a/ci_scripts/flake8_syntax_check.ini b/ci_scripts/flake8_syntax_check.ini new file mode 100644 index 000000000..969379326 --- /dev/null +++ b/ci_scripts/flake8_syntax_check.ini @@ -0,0 +1,15 @@ +[flake8] + +## Check syntax errors and fail the build if any are found. +exclude = + ../monkey/monkey_island/cc/ui, + ../monkey/common/cloud +select = + E901, + E999, + F821, + F822, + F823 +count = True +show-source = True +statistics = True diff --git a/ci_scripts/install_requirements.ps1 b/ci_scripts/install_requirements.ps1 new file mode 100644 index 000000000..de42d8599 --- /dev/null +++ b/ci_scripts/install_requirements.ps1 @@ -0,0 +1,5 @@ +python -m venv validation-env +.\validation-env\Scripts\activate.ps1 +python -m pip install -r .\requirements.txt +npm i -g eslint +deactivate diff --git a/ci_scripts/isort.cfg b/ci_scripts/isort.cfg new file mode 100644 index 000000000..d8651febd --- /dev/null +++ b/ci_scripts/isort.cfg @@ -0,0 +1,6 @@ +[isort] + +# Possible options: https://pycqa.github.io/isort/docs/configuration/options/ + +known_first_party=common,infection_monkey,monkey_island +skip=monkey/common/cloud/scoutsuite,monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators_list.py,monkey/monkey_island/cc/ui,monkey/common/cloud/scoutsuite diff --git a/ci_scripts/requirements.txt b/ci_scripts/requirements.txt new file mode 100644 index 000000000..2b7db1909 --- /dev/null +++ b/ci_scripts/requirements.txt @@ -0,0 +1,6 @@ +flake8 +pytest +dlint +isort +coverage +black diff --git a/ci_scripts/validate.ps1 b/ci_scripts/validate.ps1 new file mode 100644 index 000000000..d85da6a2a --- /dev/null +++ b/ci_scripts/validate.ps1 @@ -0,0 +1,39 @@ +.\ci_scripts\validation-env\Scripts\activate.ps1 +$ErrorActionPreference = "Stop" +python -m pip install -r monkey/monkey_island/requirements.txt +python -m pip install -r monkey/infection_monkey/requirements.txt +flake8 ./monkey --config ./ci_scripts/flake8_syntax_check.cfg +flake8 ./monkey --exit-zero --config ./ci_scripts/flake8_linter_check.cfg | Out-File -FilePath .\ci_scripts\flake8_warnings.txt +Get-Content -Path .\ci_scripts\flake8_warnings.txt +$PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT = 80 +if ((Get-Item -Path .\ci_scripts\flake8_warnings.txt | Get-Content -Tail 1) -gt $PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT){ + "Too many python linter warnings! Failing this build. Lower the amount of linter errors in this and try again. " + exit +} +python -m isort ./monkey -c --settings-file ./ci_scripts/isort.cfg +if (!$?) { + $confirmation = Read-Host "Isort found errors. Do you want to attmpt to fix them automatically? (y/n)" + if ($confirmation -eq 'y') { + python -m isort ./monkey --settings-file ./ci_scripts/isort.cfg + } +} +Push-Location -Path ./monkey +python ./monkey_island/cc/environment/set_server_config.py testing +python -m pytest +$lastCommandSucceeded = $? +python ./monkey_island/cc/environment/set_server_config.py restore +Pop-Location + +if (!$lastCommandSucceeded) { + exit +} + +Push-Location -Path .\monkey\monkey_island\cc\ui +eslint ./src -c ./.eslintrc +Pop-Location + +swimm verify + +Write-Host "Script finished. Press any key to continue" +$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'); +deactivate diff --git a/deployment_scripts/README.md b/deployment_scripts/README.md index 16b150852..4ee91b5b4 100644 --- a/deployment_scripts/README.md +++ b/deployment_scripts/README.md @@ -39,6 +39,7 @@ Your user must have root permissions; however, don't run the script as root! ```sh wget https://raw.githubusercontent.com/guardicore/monkey/develop/deployment_scripts/deploy_linux.sh +chmod u+x ./deploy_linux.sh ``` This will download our deploy script. It's a good idea to read it quickly before executing it! @@ -52,4 +53,13 @@ After downloading that script, execute it in a shell. The first argument should - `./deploy_linux.sh "" "master"` (deploys master branch in script directory) - `./deploy_linux.sh "/home/user/new" "master"` (if directory "new" is not found creates it and clones master branch into it) -You may also pass in an optional third `false` parameter to disable downloading the latest agent binaries. \ No newline at end of file +You may also pass in an optional third `false` parameter to disable downloading the latest agent binaries. + +### Run on Linux + +After the `deploy_linux.sh` script completes, you can start the monkey island. + +```sh +cd infection_monkey/monkey +./monkey_island/linux/run.sh +``` diff --git a/deployment_scripts/config b/deployment_scripts/config index bda54e390..cad04a01c 100644 --- a/deployment_scripts/config +++ b/deployment_scripts/config @@ -4,41 +4,42 @@ export MONKEY_FOLDER_NAME="infection_monkey" # Url of public git repository that contains monkey's source code export MONKEY_GIT_URL="https://github.com/guardicore/monkey" -get_latest_release() { - curl --silent "https://api.github.com/repos/$1/releases/latest" | # Get latest release from GitHub API - grep '"tag_name":' | # Get tag line - sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value +exists() { + command -v "$1" >/dev/null 2>&1 } -MONKEY_LATEST_RELEASE=$(get_latest_release "monkey/guardicore") +get_latest_release() { + RELEASE_URL="https://api.github.com/repos/$1/releases/latest" + + if exists wget; then + RELEASE_INFO=$(wget --quiet -O - "$RELEASE_URL") # Get latest release from GitHub API + else + RELEASE_INFO=$(curl --silent "$RELEASE_URL") # Get latest release from GitHub API + fi + + echo "$RELEASE_INFO" | + grep '"tag_name":' | # Get tag line + sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value +} + +MONKEY_LATEST_RELEASE=$(get_latest_release "guardicore/monkey") # Monkey binaries -LINUX_32_BINARY_NAME="monkey-linux-32" -LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/monkey-linux-32" -export LINUX_32_BINARY_URL -export LINUX_32_BINARY_NAME +export LINUX_32_BINARY_NAME="monkey-linux-32" +export LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-linux-32" -LINUX_64_BINARY_NAME="monkey-linux-64" -LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/monkey-linux-64" -export LINUX_64_BINARY_URL -export LINUX_64_BINARY_NAME +export LINUX_64_BINARY_NAME="monkey-linux-64" +export LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-linux-64" -WINDOWS_32_BINARY_NAME="monkey-windows-32.exe" -WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/monkey-windows-32.exe" -export WINDOWS_32_BINARY_URL -export WINDOWS_32_BINARY_NAME +export WINDOWS_32_BINARY_NAME="monkey-windows-32.exe" +export WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-windows-32.exe" -WINDOWS_64_BINARY_NAME="monkey-windows-64.exe" -WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/monkey-windows-64.exe" -export WINDOWS_64_BINARY_URL -export WINDOWS_64_BINARY_NAME +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 -TRACEROUTE_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/traceroute64" -export TRACEROUTE_64_BINARY_URL -TRACEROUTE_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/traceroute32" -export TRACEROUTE_32_BINARY_URL -SAMBACRY_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/sc_monkey_runner64.so" -export SAMBACRY_64_BINARY_URL -SAMBACRY_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/sc_monkey_runner32.so" -export SAMBACRY_32_BINARY_URL +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" diff --git a/deployment_scripts/deploy_linux.sh b/deployment_scripts/deploy_linux.sh index d13478018..728e2f52d 100755 --- a/deployment_scripts/deploy_linux.sh +++ b/deployment_scripts/deploy_linux.sh @@ -10,7 +10,7 @@ is_root() { has_sudo() { # 0 true, 1 false - timeout 1 sudo id && return 0 || return 1 + return $(sudo -nv > /dev/null 2>&1) } handle_error() { @@ -23,6 +23,11 @@ log_message() { echo -e "DEPLOYMENT SCRIPT: $1" } +if is_root; then + log_message "Please don't run this script as root" + exit 1 +fi + config_branch=${2:-"develop"} config_url="https://raw.githubusercontent.com/guardicore/monkey/${config_branch}/deployment_scripts/config" @@ -62,14 +67,9 @@ ISLAND_BINARIES_PATH="$ISLAND_PATH/cc/binaries" INFECTION_MONKEY_DIR="$monkey_home/monkey/infection_monkey" MONKEY_BIN_DIR="$INFECTION_MONKEY_DIR/bin" -if is_root; then - log_message "Please don't run this script as root" - exit 1 -fi - -HAS_SUDO=$(has_sudo) -if [[ ! $HAS_SUDO ]]; then - log_message "You need root permissions for some of this script operations. Quiting." +if ! has_sudo; then + log_message "You need root permissions for some of this script operations. \ +Run \`sudo -v\`, enter your password, and then re-run this script." exit 1 fi @@ -110,13 +110,16 @@ if [[ ${python_cmd} == "" ]]; then log_message "Python 3.7 command not found. Installing python 3.7." sudo add-apt-repository ppa:deadsnakes/ppa sudo apt-get update - sudo apt install python3.7 python3.7-dev + sudo apt-get install -y python3.7 python3.7-dev log_message "Python 3.7 is now available with command 'python3.7'." python_cmd="python3.7" fi log_message "Installing build-essential" -sudo apt install build-essential +sudo apt-get install -y build-essential + +log_message "Installing python3-distutils" +sudo apt-get install -y python3-distutils log_message "Installing or updating pip" # shellcheck disable=SC2086 @@ -134,7 +137,7 @@ requirements_island="$ISLAND_PATH/requirements.txt" ${python_cmd} -m pip install -r "${requirements_island}" --user --upgrade || handle_error log_message "Installing monkey requirements" -sudo apt-get install libffi-dev upx libssl-dev libc++1 +sudo apt-get install -y libffi-dev upx libssl-dev libc++1 requirements_monkey="$INFECTION_MONKEY_DIR/requirements.txt" ${python_cmd} -m pip install -r "${requirements_monkey}" --user --upgrade || handle_error @@ -162,15 +165,19 @@ chmod a+x "$ISLAND_BINARIES_PATH/$LINUX_64_BINARY_NAME" # If a user haven't installed mongo manually check if we can install it with our script if ! exists mongod; then + log_message "Installing libcurl4" + sudo apt-get install -y libcurl4 + log_message "Installing MongoDB" "${ISLAND_PATH}"/linux/install_mongo.sh ${MONGO_PATH} || handle_error fi log_message "Installing openssl" -sudo apt-get install openssl +sudo apt-get install -y openssl # Generate SSL certificate log_message "Generating certificate" +chmod u+x "${ISLAND_PATH}"/linux/create_certificate.sh "${ISLAND_PATH}"/linux/create_certificate.sh ${ISLAND_PATH}/cc # Update node diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 258cd4c0a..f76c0f3b1 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -9,6 +9,7 @@ Here are some of the most common questions we receive about the Infection Monkey - [Where can I get the latest Monkey version? π°](#where-can-i-get-the-latest-monkey-version) - [How long does a single Monkey run for? Is there a time limit?](#how-long-does-a-single-monkey-run-for-is-there-a-time-limit) +- [How to reset the password?](#how-to-reset-the-password) - [Should I run the Monkey continuously?](#should-i-run-the-monkey-continuously) - [Which queries does Monkey perform to the Internet exactly?](#which-queries-does-monkey-perform-to-the-internet-exactly) - [Where can I find the log files of the Monkey and the Monkey Island, and how can I read them?](#where-can-i-find-the-log-files-of-the-monkey-and-the-monkey-island-and-how-can-i-read-them) @@ -35,6 +36,23 @@ If you want to see what has changed between versions, refer to the [releases pag The Monkey shuts off either when it can't find new victims, or when it has exceeded the quota of victims as defined in the configuration. +## How to reset the password? + +On your first access of Monkey Island server, you'll be prompted to create an account. If you forgot the credentials you + entered or just want to change them, you need to manually alter the `server_config.json` file. On Linux, this file is + located on `/var/monkey/monkey_island/cc/server_config.json`. On windows, it's based on your install directory (typically + `C:\Program Files\Guardicore\Monkey Island\monkey_island\cc\server_config.json`). Reset the contents of this file + leaving the **deployment option unchanged** (it might be "vmware" or "linux" in your case): + +```json +{ + "server_config": "password", + "deployment": "windows" +} +``` + Then reset the Island process (`sudo systemctl restart monkey-island.service` for linux, restart program for windows). + Finally, go to the Island's URL and create a new account. + ## Should I run the Monkey continuously? Yes! This will allow you to verify that no new security issues were identified by the Monkey since the last time you ran it. diff --git a/docs/content/usage/reports/_index.md b/docs/content/reports/_index.md similarity index 76% rename from docs/content/usage/reports/_index.md rename to docs/content/reports/_index.md index 8d1da79b9..62996d8a7 100644 --- a/docs/content/usage/reports/_index.md +++ b/docs/content/reports/_index.md @@ -1,7 +1,7 @@ +++ title = "Reports" date = 2020-06-24T21:16:03+03:00 -weight = 5 +weight = 40 chapter = true pre = " " +++ @@ -10,4 +10,4 @@ pre = " " The Monkey offers three reports: -{{% children %}} +{{% children description=true style="p"%}} diff --git a/docs/content/usage/reports/mitre.md b/docs/content/reports/mitre.md similarity index 96% rename from docs/content/usage/reports/mitre.md rename to docs/content/reports/mitre.md index 77cd4ac82..760893534 100644 --- a/docs/content/usage/reports/mitre.md +++ b/docs/content/reports/mitre.md @@ -1,5 +1,6 @@ --- title: "MITRE ATT&CK report" +description: "Maps the Monkey's actions to the MITRE ATT&CK knowledge base" date: 2020-06-24T21:17:18+03:00 draft: false --- diff --git a/docs/content/usage/reports/security.files/infection_monkey_security_report_example.pdf b/docs/content/reports/security.files/infection_monkey_security_report_example.pdf similarity index 100% rename from docs/content/usage/reports/security.files/infection_monkey_security_report_example.pdf rename to docs/content/reports/security.files/infection_monkey_security_report_example.pdf diff --git a/docs/content/usage/reports/security.md b/docs/content/reports/security.md similarity index 97% rename from docs/content/usage/reports/security.md rename to docs/content/reports/security.md index a36106183..e3203d731 100644 --- a/docs/content/usage/reports/security.md +++ b/docs/content/reports/security.md @@ -2,6 +2,7 @@ title: "Security report" date: 2020-06-24T21:16:10+03:00 draft: false +description: "Provides actionable recommendations and insight into an attacker's view of your network" --- {{% notice info %}} diff --git a/docs/content/usage/reports/zero-trust.md b/docs/content/reports/zero-trust.md similarity index 90% rename from docs/content/usage/reports/zero-trust.md rename to docs/content/reports/zero-trust.md index 8d6c55aaa..54c9aff31 100644 --- a/docs/content/usage/reports/zero-trust.md +++ b/docs/content/reports/zero-trust.md @@ -2,6 +2,7 @@ title: "Zero Trust report" date: 2020-06-24T21:16:18+03:00 draft: false +description: "Generates a status report with detailed explanations of Zero Trust security gaps and prescriptive instructions on how to rectify them" --- {{% notice info %}} @@ -17,7 +18,7 @@ This diagram provides a quick glance at how your organization scores on each com - {{< label danger Failed >}} At least one of the tests related to this component failed. This means that the Infection Monkey detected an unmet Zero Trust requirement. - {{< label warning Verify >}} At least one of the testsβ results related to this component requires further manual verification. - {{< label success Passed >}} All Tests related to this pillar passed. No violation of a Zero Trust guiding principle was detected. -- {{< label other Unexecuted >}} This status means no tests were executed for this pillar. +- {{< label unused Unexecuted >}} This status means no tests were executed for this pillar. ![Zero Trust Report summary](/images/usage/reports/ztreport1.png "Zero Trust Report summary") diff --git a/docs/content/usage/integrations/aws-run-on-ec2-machine.md b/docs/content/usage/integrations/aws-run-on-ec2-machine.md index 0183dc241..e30a8b554 100644 --- a/docs/content/usage/integrations/aws-run-on-ec2-machine.md +++ b/docs/content/usage/integrations/aws-run-on-ec2-machine.md @@ -54,16 +54,15 @@ See [Amazon's documentation about working with SSM agents](https://docs.aws.amaz ### Running the monkey -When you run the monkey island on an AWS instance, the island detects it's running on AWS and present the following option in the _"Run Monkey"_ page, like so: +When you run the Monkey Island on an AWS instance, the island detects it's running on AWS and present the following option in the _"Run Monkey"_ page, like so: ![Running a Monkey on EC2 Instance](/images/usage/integrations/monkey-island-aws-screenshot-1.png "Running a Monkey on EC2 Instance") -And then you can choose one of the available instances as "patient zero" like so: +After you click on "AWS run" you can choose one of the available instances as "patient zero" like so: -1. Click on "Run on AWS" -2. Choose the relevant Network Interface -3. Select the machines you'd like to run the Monkey on -4. Click "Run on Selected Machines", and watch the monkey go! π +1. Choose the relevant Network Interface +2. Select the machines you'd like to run the Monkey on +3. Click "Run on Selected Machines", and watch the monkey go! π ![Running a Monkey on EC2 Instance](/images/usage/integrations/monkey-island-aws-screenshot-2.png "Running a Monkey on EC2 Instance") diff --git a/docs/static/images/usage/integrations/monkey-island-aws-screenshot-1.png b/docs/static/images/usage/integrations/monkey-island-aws-screenshot-1.png index 0b1af5fae..ba61e2b7c 100644 Binary files a/docs/static/images/usage/integrations/monkey-island-aws-screenshot-1.png and b/docs/static/images/usage/integrations/monkey-island-aws-screenshot-1.png differ diff --git a/docs/static/images/usage/integrations/monkey-island-aws-screenshot-2.png b/docs/static/images/usage/integrations/monkey-island-aws-screenshot-2.png index f6442e82b..ad9f6ed81 100644 Binary files a/docs/static/images/usage/integrations/monkey-island-aws-screenshot-2.png and b/docs/static/images/usage/integrations/monkey-island-aws-screenshot-2.png differ diff --git a/docs/static/images/usage/use-cases/network-breach.PNG b/docs/static/images/usage/use-cases/network-breach.PNG index 5dfd38ffb..871a36bb6 100644 Binary files a/docs/static/images/usage/use-cases/network-breach.PNG and b/docs/static/images/usage/use-cases/network-breach.PNG differ diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index f9548b6bf..edec8018e 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -228,7 +228,6 @@ class ShellShockExploiter(HostExploiter): Checks if which urls exist :return: Sequence of URLs to try and attack """ - import requests attack_path = 'http://' if is_https: attack_path = 'https://' diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 153f64ac7..c28887820 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -1,7 +1,6 @@ from logging import getLogger from impacket.dcerpc.v5 import scmr, transport -from impacket.smbconnection import SMB_DIALECT from common.utils.attack_utils import ScanStatus, UsageEnum from common.utils.exploit_enum import ExploitType @@ -104,7 +103,7 @@ class SmbExploiter(HostExploiter): LOG.debug("Exploiter SmbExec is giving up...") return False - self.set_vulnerable_port(self.host) + self.set_vulnerable_port() # execute the remote dropper in case the path isn't final if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower(): cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \ @@ -121,8 +120,7 @@ class SmbExploiter(HostExploiter): for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values(): rpctransport = transport.DCERPCTransportFactory(str_bind_format % (self.host.ip_addr,)) rpctransport.set_dport(port) - if hasattr(rpctransport, 'preferred_dialect'): - rpctransport.preferred_dialect(SMB_DIALECT) + rpctransport.setRemoteHost(self.host.ip_addr) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(user, password, '', lm_hash, ntlm_hash, None) @@ -168,7 +166,7 @@ class SmbExploiter(HostExploiter): SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])) return True - def set_vulnerable_port(self, host: VictimHost): + def set_vulnerable_port(self): if 'tcp-445' in self.host.services: self.vulnerable_port = "445" elif 'tcp-139' in self.host.services: diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 0f489d0a6..f5db3e92f 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -56,7 +56,7 @@ class WebRCE(HostExploiter): Method that creates a dictionary of configuration values for exploit :return: configuration dict """ - exploit_config = dict() + exploit_config = {} # dropper: If true monkey will use dropper parameter that will detach monkey's process and try to copy # it's file to the default destination path. diff --git a/monkey/infection_monkey/network/network_scanner.py b/monkey/infection_monkey/network/network_scanner.py index 6abf409ad..95277b4b9 100644 --- a/monkey/infection_monkey/network/network_scanner.py +++ b/monkey/infection_monkey/network/network_scanner.py @@ -113,7 +113,7 @@ class NetworkScanner(object): :return: Victim or None if victim isn't alive """ LOG.debug("Scanning target address: %r", victim) - if any([scanner.is_host_alive(victim) for scanner in self.scanners]): + if any(scanner.is_host_alive(victim) for scanner in self.scanners): LOG.debug("Found potential target_ip: %r", victim) return victim else: diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py index 049b30838..99b21d81c 100644 --- a/monkey/infection_monkey/network/tools.py +++ b/monkey/infection_monkey/network/tools.py @@ -139,7 +139,7 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): timeout = int(round(timeout)) # clamp to integer, to avoid checking input sockets_to_try = possible_ports[:] connected_ports_sockets = [] - while (timeout >= 0) and len(sockets_to_try): + while (timeout >= 0) and sockets_to_try: sock_objects = [s[1] for s in sockets_to_try] _, writeable_sockets, _ = select.select(sock_objects, sock_objects, sock_objects, 0) diff --git a/monkey/infection_monkey/system_info/windows_cred_collector/pypykatz_handler.py b/monkey/infection_monkey/system_info/windows_cred_collector/pypykatz_handler.py index cba4c330a..35ff67678 100644 --- a/monkey/infection_monkey/system_info/windows_cred_collector/pypykatz_handler.py +++ b/monkey/infection_monkey/system_info/windows_cred_collector/pypykatz_handler.py @@ -67,8 +67,8 @@ def _get_windows_cred(pypykatz_cred: PypykatzCredential): def _hash_to_string(hash_: Any): - if type(hash_) == str: + if type(hash_) is str: return hash_ - if type(hash_) == bytes: + if type(hash_) is bytes: return binascii.hexlify(bytearray(hash_)).decode() raise Exception(f"Can't convert hash_ to string, unsupported hash_ type {type(hash_)}") diff --git a/monkey/monkey_island.py b/monkey/monkey_island.py index b3b74e6b3..cd452066c 100644 --- a/monkey/monkey_island.py +++ b/monkey/monkey_island.py @@ -1,3 +1,7 @@ +from gevent import monkey as gevent_monkey + +gevent_monkey.patch_all() + from monkey_island.cc.main import main @@ -5,8 +9,9 @@ def parse_cli_args(): import argparse parser = argparse.ArgumentParser(description="Infection Monkey Island CnC Server. See https://infectionmonkey.com") parser.add_argument("-s", "--setup-only", action="store_true", - help="Pass this flag to cause the Island to setup and exit without actually starting. This is useful " - "for preparing Island to boot faster later-on, so for compiling/packaging Islands.") + help="Pass this flag to cause the Island to setup and exit without actually starting. " + "This is useful for preparing Island to boot faster later-on, so for " + "compiling/packaging Islands.") args = parser.parse_args() return args.setup_only diff --git a/monkey/monkey_island/cc/environment/set_server_config.py b/monkey/monkey_island/cc/environment/set_server_config.py index d31dd6916..8c5c12b63 100644 --- a/monkey/monkey_island/cc/environment/set_server_config.py +++ b/monkey/monkey_island/cc/environment/set_server_config.py @@ -3,6 +3,7 @@ import json import logging import sys from pathlib import Path +from shutil import move def add_monkey_dir_to_sys_path(): @@ -16,6 +17,7 @@ add_monkey_dir_to_sys_path() from monkey_island.cc.environment.environment_config import EnvironmentConfig # noqa: E402 isort:skip SERVER_CONFIG = "server_config" +BACKUP_CONFIG_FILENAME = "./server_config.backup" logger = logging.getLogger(__name__) logger.addHandler(logging.StreamHandler()) @@ -26,10 +28,19 @@ def main(): args = parse_args() file_path = EnvironmentConfig.get_config_file_path() + if args.server_config == "restore": + restore_previous_config(file_path) + quit() + # Read config with open(file_path) as config_file: config_data = json.load(config_file) + # Backup the config + with open(BACKUP_CONFIG_FILENAME, "w") as backup_file: + json.dump(config_data, backup_file, indent=4) + backup_file.write("\n") + # Edit the config config_data[SERVER_CONFIG] = args.server_config @@ -42,10 +53,14 @@ def main(): def parse_args(): parser = argparse.ArgumentParser() - parser.add_argument("server_config", choices=["standard", "testing", "password"]) + parser.add_argument("server_config", choices=["standard", "testing", "password", "restore"]) args = parser.parse_args() return args +def restore_previous_config(config_path): + move(BACKUP_CONFIG_FILENAME, config_path) + + if __name__ == '__main__': main() diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 610681034..cf58f48c5 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -6,6 +6,8 @@ from pathlib import Path from threading import Thread # Add the monkey_island directory to the path, to make sure imports that don't start with "monkey_island." work. +from gevent.pywsgi import WSGIServer + MONKEY_ISLAND_DIR_BASE_PATH = str(Path(__file__).parent.parent) if str(MONKEY_ISLAND_DIR_BASE_PATH) not in sys.path: sys.path.insert(0, MONKEY_ISLAND_DIR_BASE_PATH) @@ -46,9 +48,6 @@ def main(should_setup_only=False): def start_island_server(should_setup_only): - from tornado.httpserver import HTTPServer - from tornado.ioloop import IOLoop - from tornado.wsgi import WSGIContainer mongo_url = os.environ.get('MONGO_URL', env_singleton.env.get_mongo_url()) wait_for_mongo_db_server(mongo_url) @@ -69,12 +68,11 @@ def start_island_server(should_setup_only): if env_singleton.env.is_debug(): app.run(host='0.0.0.0', debug=True, ssl_context=(crt_path, key_path)) else: - http_server = HTTPServer(WSGIContainer(app), - ssl_options={'certfile': os.environ.get('SERVER_CRT', crt_path), - 'keyfile': os.environ.get('SERVER_KEY', key_path)}) - http_server.listen(env_singleton.env.get_island_port()) + http_server = WSGIServer(('0.0.0.0', env_singleton.env.get_island_port()), app, + certfile=os.environ.get('SERVER_CRT', crt_path), + keyfile=os.environ.get('SERVER_KEY', key_path)) log_init_info() - IOLoop.instance().start() + http_server.serve_forever() def log_init_info(): diff --git a/monkey/monkey_island/cc/resources/pba_file_download.py b/monkey/monkey_island/cc/resources/pba_file_download.py index ad0786f4f..5a616a0fc 100644 --- a/monkey/monkey_island/cc/resources/pba_file_download.py +++ b/monkey/monkey_island/cc/resources/pba_file_download.py @@ -1,7 +1,7 @@ import flask_restful from flask import send_from_directory -from monkey_island.cc.services.post_breach_files import UPLOADS_DIR +from monkey_island.cc.services.post_breach_files import UPLOADS_DIR_NAME __author__ = 'VakarisZ' @@ -13,4 +13,4 @@ class PBAFileDownload(flask_restful.Resource): # Used by monkey. can't secure. def get(self, path): - return send_from_directory(UPLOADS_DIR, path) + return send_from_directory(UPLOADS_DIR_NAME, path) diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index 0a5e671a3..25158d73a 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -12,6 +12,7 @@ EXPLOITER_CLASSES = { "SmbExploiter" ], "title": "SMB Exploiter", + "safe": True, "attack_techniques": ["T1110", "T1075", "T1035"], "info": "Brute forces using credentials provided by user and" " hashes gathered by mimikatz.", @@ -23,6 +24,7 @@ EXPLOITER_CLASSES = { "WmiExploiter" ], "title": "WMI Exploiter", + "safe": True, "attack_techniques": ["T1110", "T1106"], "info": "Brute forces WMI (Windows Management Instrumentation) " "using credentials provided by user and hashes gathered by mimikatz.", @@ -34,6 +36,7 @@ EXPLOITER_CLASSES = { "MSSQLExploiter" ], "title": "MSSQL Exploiter", + "safe": True, "attack_techniques": ["T1110"], "info": "Tries to brute force into MsSQL server and uses insecure " "configuration to execute commands on server.", @@ -44,7 +47,8 @@ EXPLOITER_CLASSES = { "enum": [ "Ms08_067_Exploiter" ], - "title": "MS08-067 Exploiter (UNSAFE)", + "title": "MS08-067 Exploiter", + "safe": False, "info": "Unsafe exploiter, that might cause system crash due to the use of buffer overflow. " "Uses MS08-067 vulnerability.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/ms08-067/" @@ -55,6 +59,7 @@ EXPLOITER_CLASSES = { "SSHExploiter" ], "title": "SSH Exploiter", + "safe": True, "attack_techniques": ["T1110", "T1145", "T1106"], "info": "Brute forces using credentials provided by user and SSH keys gathered from systems.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/sshexec/" @@ -65,6 +70,7 @@ EXPLOITER_CLASSES = { "ShellShockExploiter" ], "title": "ShellShock Exploiter", + "safe": True, "info": "CVE-2014-6271, based on logic from " "https://github.com/nccgroup/shocker/blob/master/shocker.py .", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/shellshock/" @@ -75,6 +81,7 @@ EXPLOITER_CLASSES = { "SambaCryExploiter" ], "title": "SambaCry Exploiter", + "safe": True, "info": "Bruteforces and searches for anonymous shares. Uses Impacket.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/sambacry/" }, @@ -84,6 +91,7 @@ EXPLOITER_CLASSES = { "ElasticGroovyExploiter" ], "title": "ElasticGroovy Exploiter", + "safe": True, "info": "CVE-2015-1427. Logic is based on Metasploit module.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/elasticgroovy/" }, @@ -93,6 +101,7 @@ EXPLOITER_CLASSES = { "Struts2Exploiter" ], "title": "Struts2 Exploiter", + "safe": True, "info": "Exploits struts2 java web framework. CVE-2017-5638. Logic based on " "https://www.exploit-db.com/exploits/41570 .", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/struts2/" @@ -103,6 +112,7 @@ EXPLOITER_CLASSES = { "WebLogicExploiter" ], "title": "WebLogic Exploiter", + "safe": True, "info": "Exploits CVE-2017-10271 and CVE-2019-2725 vulnerabilities on WebLogic server.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/weblogic/" }, @@ -112,6 +122,7 @@ EXPLOITER_CLASSES = { "HadoopExploiter" ], "title": "Hadoop/Yarn Exploiter", + "safe": True, "info": "Remote code execution on HADOOP server with YARN and default settings. " "Logic based on https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/hadoop/" @@ -122,6 +133,7 @@ EXPLOITER_CLASSES = { "VSFTPDExploiter" ], "title": "VSFTPD Exploiter", + "safe": True, "info": "Exploits a malicious backdoor that was added to the VSFTPD download archive. " "Logic based on Metasploit module.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/vsftpd/" @@ -132,6 +144,7 @@ EXPLOITER_CLASSES = { "DrupalExploiter" ], "title": "Drupal Exploiter", + "safe": True, "info": "Exploits a remote command execution vulnerability in a Drupal server," "for which certain modules (such as RESTful Web Services) are enabled.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/drupal/" diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py index 405983dc5..5e3f75f33 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py @@ -10,6 +10,7 @@ FINGER_CLASSES = { "SMBFinger" ], "title": "SMBFinger", + "safe": True, "info": "Figures out if SMB is running and what's the version of it.", "attack_techniques": ["T1210"] }, @@ -19,6 +20,7 @@ FINGER_CLASSES = { "SSHFinger" ], "title": "SSHFinger", + "safe": True, "info": "Figures out if SSH is running.", "attack_techniques": ["T1210"] }, @@ -28,6 +30,7 @@ FINGER_CLASSES = { "PingScanner" ], "title": "PingScanner", + "safe": True, "info": "Tries to identify if host is alive and which OS it's running by ping scan." }, { @@ -36,6 +39,7 @@ FINGER_CLASSES = { "HTTPFinger" ], "title": "HTTPFinger", + "safe": True, "info": "Checks if host has HTTP/HTTPS ports open." }, { @@ -44,6 +48,7 @@ FINGER_CLASSES = { "MySQLFinger" ], "title": "MySQLFinger", + "safe": True, "info": "Checks if MySQL server is running and tries to get it's version.", "attack_techniques": ["T1210"] }, @@ -53,6 +58,7 @@ FINGER_CLASSES = { "MSSQLFinger" ], "title": "MSSQLFinger", + "safe": True, "info": "Checks if Microsoft SQL service is running and tries to gather information about it.", "attack_techniques": ["T1210"] }, @@ -62,6 +68,7 @@ FINGER_CLASSES = { "ElasticFinger" ], "title": "ElasticFinger", + "safe": True, "info": "Checks if ElasticSearch is running and attempts to find it's version.", "attack_techniques": ["T1210"] }, @@ -71,6 +78,7 @@ FINGER_CLASSES = { "WindowsServerFinger" ], "title": "WindowsServerFinger", + "safe": True, "info": "Checks if server is a Windows Server and tests if it is vulnerable to Zerologon.", "attack_techniques": ["T1210"] } diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py index f1fe0f6f2..857e80da4 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py @@ -10,6 +10,7 @@ POST_BREACH_ACTIONS = { "BackdoorUser" ], "title": "Back door user", + "safe": True, "info": "Attempts to create a new user on the system and delete it afterwards.", "attack_techniques": ["T1136"] }, @@ -19,6 +20,7 @@ POST_BREACH_ACTIONS = { "CommunicateAsNewUser" ], "title": "Communicate as new user", + "safe": True, "info": "Attempts to create a new user, create HTTPS requests as that user and delete the user " "afterwards.", "attack_techniques": ["T1136"] @@ -29,6 +31,7 @@ POST_BREACH_ACTIONS = { "ModifyShellStartupFiles" ], "title": "Modify shell startup files", + "safe": True, "info": "Attempts to modify shell startup files, like ~/.profile, ~/.bashrc, ~/.bash_profile " "in linux, and profile.ps1 in windows. Reverts modifications done afterwards.", "attack_techniques": ["T1156", "T1504"] @@ -39,6 +42,7 @@ POST_BREACH_ACTIONS = { "HiddenFiles" ], "title": "Hidden files and directories", + "safe": True, "info": "Attempts to create a hidden file and remove it afterward.", "attack_techniques": ["T1158"] }, @@ -48,6 +52,7 @@ POST_BREACH_ACTIONS = { "TrapCommand" ], "title": "Trap", + "safe": True, "info": "On Linux systems, attempts to trap an interrupt signal in order to execute a command " "upon receiving that signal. Removes the trap afterwards.", "attack_techniques": ["T1154"] @@ -58,6 +63,7 @@ POST_BREACH_ACTIONS = { "ChangeSetuidSetgid" ], "title": "Setuid and Setgid", + "safe": True, "info": "On Linux systems, attempts to set the setuid and setgid bits of a new file. " "Removes the file afterwards.", "attack_techniques": ["T1166"] @@ -68,6 +74,7 @@ POST_BREACH_ACTIONS = { "ScheduleJobs" ], "title": "Job scheduling", + "safe": True, "info": "Attempts to create a scheduled job on the system and remove it.", "attack_techniques": ["T1168", "T1053"] }, @@ -77,6 +84,7 @@ POST_BREACH_ACTIONS = { "Timestomping" ], "title": "Timestomping", + "safe": True, "info": "Creates a temporary file and attempts to modify its time attributes. Removes the file afterwards.", "attack_techniques": ["T1099"] }, @@ -86,7 +94,8 @@ POST_BREACH_ACTIONS = { "SignedScriptProxyExecution" ], "title": "Signed script proxy execution", - "info": "On Windows systems, attemps to execute an arbitrary file " + "safe": False, + "info": "On Windows systems, attempts to execute an arbitrary file " "with the help of a pre-existing signed script.", "attack_techniques": ["T1216"] }, @@ -96,6 +105,7 @@ POST_BREACH_ACTIONS = { "AccountDiscovery" ], "title": "Account Discovery", + "safe": True, "info": "Attempts to get a listing of user accounts on the system.", "attack_techniques": ["T1087"] }, @@ -105,6 +115,7 @@ POST_BREACH_ACTIONS = { "ClearCommandHistory" ], "title": "Clear command history", + "safe": False, "info": "Attempts to clear the command history.", "attack_techniques": ["T1146"] } diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py index 5f113f4a7..174133f43 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py @@ -16,6 +16,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { ENVIRONMENT_COLLECTOR ], "title": "Environment collector", + "safe": True, "info": "Collects information about machine's environment (on premise/GCP/AWS).", "attack_techniques": ["T1082"] }, @@ -25,6 +26,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { MIMIKATZ_COLLECTOR ], "title": "Mimikatz collector", + "safe": True, "info": "Collects credentials from Windows credential manager.", "attack_techniques": ["T1003", "T1005"] }, @@ -34,6 +36,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { AWS_COLLECTOR ], "title": "AWS collector", + "safe": True, "info": "If on AWS, collects more information about the AWS instance currently running on.", "attack_techniques": ["T1082"] }, @@ -43,6 +46,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { HOSTNAME_COLLECTOR ], "title": "Hostname collector", + "safe": True, "info": "Collects machine's hostname.", "attack_techniques": ["T1082", "T1016"] }, @@ -52,6 +56,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { PROCESS_LIST_COLLECTOR ], "title": "Process list collector", + "safe": True, "info": "Collects a list of running processes on the machine.", "attack_techniques": ["T1082"] }, @@ -61,6 +66,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { AZURE_CRED_COLLECTOR ], "title": "Azure credential collector", + "safe": True, "info": "Collects password credentials from Azure VMs", "attack_techniques": ["T1003", "T1005"] } diff --git a/monkey/monkey_island/cc/services/post_breach_files.py b/monkey/monkey_island/cc/services/post_breach_files.py index b198161ad..20064f4c4 100644 --- a/monkey/monkey_island/cc/services/post_breach_files.py +++ b/monkey/monkey_island/cc/services/post_breach_files.py @@ -11,7 +11,8 @@ logger = logging.getLogger(__name__) # Where to find file names in config PBA_WINDOWS_FILENAME_PATH = ['monkey', 'post_breach', 'PBA_windows_filename'] PBA_LINUX_FILENAME_PATH = ['monkey', 'post_breach', 'PBA_linux_filename'] -PBA_UPLOAD_PATH = ['monkey_island', 'cc', 'userUploads'] +UPLOADS_DIR_NAME = 'userUploads' +PBA_UPLOAD_PATH = ['monkey_island', 'cc', UPLOADS_DIR_NAME] UPLOADS_DIR = Path(*PBA_UPLOAD_PATH) diff --git a/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py b/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py index f2fa11c89..990df24e4 100644 --- a/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py +++ b/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py @@ -1,5 +1,6 @@ import logging -import threading + +from gevent.lock import BoundedSemaphore logger = logging.getLogger(__name__) @@ -7,9 +8,9 @@ logger = logging.getLogger(__name__) # Report generation can be quite slow if there is a lot of data, and the UI queries the Root service often; without # the locks, these requests would accumulate, overload the server, eventually causing it to crash. logger.debug("Initializing report generation locks.") -__report_generating_lock = threading.Semaphore() -__attack_report_generating_lock = threading.Semaphore() -__regular_report_generating_lock = threading.Semaphore() +__report_generating_lock = BoundedSemaphore() +__attack_report_generating_lock = BoundedSemaphore() +__regular_report_generating_lock = BoundedSemaphore() def safe_generate_reports(): diff --git a/monkey/monkey_island/cc/services/telemetry/processing/scan.py b/monkey/monkey_island/cc/services/telemetry/processing/scan.py index 43446126c..70675c995 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/scan.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/scan.py @@ -1,6 +1,5 @@ from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey -from monkey_island.cc.services.edge.edge import EdgeService from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.telemetry.processing.utils import \ get_edge_by_scan_or_exploit_telemetry diff --git a/monkey/monkey_island/cc/ui/.eslintrc b/monkey/monkey_island/cc/ui/.eslintrc index 2cd52bb98..afc7f2b7a 100644 --- a/monkey/monkey_island/cc/ui/.eslintrc +++ b/monkey/monkey_island/cc/ui/.eslintrc @@ -35,7 +35,8 @@ "comma-dangle": 1, "quotes": [ 1, - "single" + "single", + {"allowTemplateLiterals": true} ], "no-undef": 1, "global-strict": 0, diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunManually/LocalManualRunOptions.js similarity index 74% rename from monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js rename to monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunManually/LocalManualRunOptions.js index b28285a18..bd396e256 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunManually/LocalManualRunOptions.js @@ -1,12 +1,12 @@ import React, {useEffect, useState} from 'react'; -import InlineSelection from '../../ui-components/inline-selection/InlineSelection'; -import DropdownSelect from '../../ui-components/DropdownSelect'; -import {OS_TYPES} from './OsTypes'; -import GenerateLocalWindowsCmd from './commands/local_windows_cmd'; -import GenerateLocalWindowsPowershell from './commands/local_windows_powershell'; -import GenerateLocalLinuxWget from './commands/local_linux_wget'; -import GenerateLocalLinuxCurl from './commands/local_linux_curl'; -import CommandDisplay from './CommandDisplay'; +import InlineSelection from '../../../ui-components/inline-selection/InlineSelection'; +import DropdownSelect from '../../../ui-components/DropdownSelect'; +import {OS_TYPES} from '../utils/OsTypes'; +import GenerateLocalWindowsCmd from '../commands/local_windows_cmd'; +import GenerateLocalWindowsPowershell from '../commands/local_windows_powershell'; +import GenerateLocalLinuxWget from '../commands/local_linux_wget'; +import GenerateLocalLinuxCurl from '../commands/local_linux_curl'; +import CommandDisplay from '../utils/CommandDisplay'; const LocalManualRunOptions = (props) => { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnAWS/AWSInstanceTable.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnAWS/AWSInstanceTable.js new file mode 100644 index 000000000..cf792f7b8 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnAWS/AWSInstanceTable.js @@ -0,0 +1,119 @@ +import React, {useState} from 'react'; +import ReactTable from 'react-table' +import checkboxHOC from 'react-table/lib/hoc/selectTable'; +import PropTypes from 'prop-types'; + + +const CheckboxTable = checkboxHOC(ReactTable); + +const columns = [ + { + Header: 'Machines', + columns: [ + {Header: 'Machine', accessor: 'name'}, + {Header: 'Instance ID', accessor: 'instance_id'}, + {Header: 'IP Address', accessor: 'ip_address'}, + {Header: 'OS', accessor: 'os'} + ] + } +]; + +const pageSize = 10; + +function AWSInstanceTable(props) { + + const [allToggled, setAllToggled] = useState(false); + let checkboxTable = null; + + function toggleSelection(key) { + key = key.replace('select-', ''); + // start off with the existing state + let modifiedSelection = [...props.selection]; + const keyIndex = modifiedSelection.indexOf(key); + // check to see if the key exists + if (keyIndex >= 0) { + // it does exist so we will remove it using destructing + modifiedSelection = [ + ...modifiedSelection.slice(0, keyIndex), + ...modifiedSelection.slice(keyIndex + 1) + ]; + } else { + // it does not exist so add it + modifiedSelection.push(key); + } + // update the state + props.setSelection(modifiedSelection); + } + + function isSelected(key) { + return props.selection.includes(key); + } + + function toggleAll() { + const selectAll = !allToggled; + const selection = []; + if (selectAll) { + // we need to get at the internals of ReactTable + const wrappedInstance = checkboxTable.getWrappedInstance(); + // the 'sortedData' property contains the currently accessible records based on the filter and sort + const currentRecords = wrappedInstance.getResolvedState().sortedData; + // we just push all the IDs onto the selection array + currentRecords.forEach(item => { + selection.push(item._original.instance_id); + }); + } + setAllToggled(selectAll); + props.setSelection(selection); + } + + function getTrProps(_, r) { + let color = 'inherit'; + if (r) { + let instId = r.original.instance_id; + if (isSelected(instId)) { + color = '#ffed9f'; + } else if (Object.prototype.hasOwnProperty.call(props.results, instId)) { + color = props.results[instId] ? '#00f01b' : '#f00000' + } + } + + return { + style: {backgroundColor: color} + }; + } + + return ( +
+