Merge remote-tracking branch 'upstream/develop' into old_machine_bootloader
# Conflicts: # monkey/monkey_island/cc/main.py
This commit is contained in:
commit
a22cd893d8
27
.travis.yml
27
.travis.yml
|
@ -15,6 +15,7 @@ install:
|
|||
# Python
|
||||
- pip install -r monkey/monkey_island/requirements.txt # for unit tests
|
||||
- pip install flake8 pytest dlint # for next stages
|
||||
- pip install coverage # for code coverage
|
||||
- pip install -r monkey/infection_monkey/requirements.txt # for unit tests
|
||||
|
||||
before_script:
|
||||
|
@ -23,24 +24,28 @@ before_script:
|
|||
|
||||
script:
|
||||
# Check Python code
|
||||
# Check syntax errors and fail the build if any are found.
|
||||
## Check syntax errors and fail the build if any are found.
|
||||
- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
||||
|
||||
# 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.
|
||||
## 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
|
||||
# Display the linter issues
|
||||
## Display the linter issues
|
||||
- cat flake8_warnings.txt
|
||||
# Make sure that we haven't increased the amount of warnings.
|
||||
## Make sure that we haven't increased the amount of warnings.
|
||||
- PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT=190
|
||||
- if [ $(tail -n 1 flake8_warnings.txt) -gt $PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT ]; then echo "Too many warnings! Failing this build. Lower the amount of linter errors in this and try again. " && exit 1; fi
|
||||
- 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
|
||||
|
||||
## Run unit tests
|
||||
- cd monkey # This is our source dir
|
||||
- python -m pytest # Have to use `python -m pytest` instead of `pytest` to add "{$builddir}/monkey/monkey" to sys.path.
|
||||
|
||||
## Calculate Code Coverage
|
||||
- coverage run -m pytest
|
||||
|
||||
# Check JS code. The npm install must happen AFTER the flake8 because the node_modules folder will cause a lot of errors.
|
||||
- cd monkey_island/cc/ui
|
||||
- npm i
|
||||
|
@ -51,6 +56,10 @@ script:
|
|||
- JS_WARNINGS_AMOUNT_UPPER_LIMIT=37
|
||||
- eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT
|
||||
|
||||
after_success:
|
||||
# Upload code coverage results to codecov.io, see https://github.com/codecov/codecov-bash for more information
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
notifications:
|
||||
slack: # Notify to slack
|
||||
rooms:
|
||||
|
|
26
README.md
26
README.md
|
@ -1,6 +1,9 @@
|
|||
# Infection Monkey
|
||||
[![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=develop)](https://travis-ci.com/guardicore/monkey)
|
||||
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/guardicore/monkey)](https://github.com/guardicore/monkey/releases)
|
||||
|
||||
[![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=develop)](https://travis-ci.com/guardicore/monkey)
|
||||
[![codecov](https://codecov.io/gh/guardicore/monkey/branch/develop/graph/badge.svg)](https://codecov.io/gh/guardicore/monkey)
|
||||
|
||||
![GitHub stars](https://img.shields.io/github/stars/guardicore/monkey)
|
||||
![GitHub commit activity](https://img.shields.io/github/commit-activity/m/guardicore/monkey)
|
||||
|
||||
|
@ -14,14 +17,15 @@ The Infection Monkey is an open source security tool for testing a data center's
|
|||
|
||||
<img src=".github/Security-overview.png" width="800" height="500">
|
||||
|
||||
|
||||
The Infection Monkey is comprised of two parts:
|
||||
* Monkey - A tool which infects other machines and propagates to them
|
||||
* Monkey Island - A dedicated server to control and visualize the Infection Monkey's progress inside the data center
|
||||
|
||||
To read more about the Monkey, visit http://infectionmonkey.com
|
||||
* **Monkey** - A tool which infects other machines and propagates to them.
|
||||
* **Monkey Island** - A dedicated server to control and visualize the Infection Monkey's progress inside the data center.
|
||||
|
||||
To read more about the Monkey, visit [infectionmonkey.com](https://infectionmonkey.com).
|
||||
|
||||
## Main Features
|
||||
|
||||
The Infection Monkey uses the following techniques and exploits to propagate to other machines.
|
||||
|
||||
* Multiple propagation techniques:
|
||||
|
@ -42,12 +46,10 @@ Check out the [Setup](https://github.com/guardicore/monkey/wiki/setup) page in t
|
|||
|
||||
The Infection Monkey supports a variety of platforms, documented [in the wiki](https://github.com/guardicore/monkey/wiki/OS-compatibility).
|
||||
|
||||
|
||||
## Building the Monkey from source
|
||||
To deploy development version of monkey you should refer to readme in the [deployment scripts](deployment_scripts) folder.
|
||||
If you only want to build the monkey from source, see [Setup](https://github.com/guardicore/monkey/wiki/Setup#compile-it-yourself)
|
||||
and follow the instructions at the readme files under [infection_monkey](infection_monkey) and [monkey_island](monkey_island).
|
||||
|
||||
and follow the instructions at the readme files under [infection_monkey](monkey/infection_monkey) and [monkey_island](monkey/monkey_island).
|
||||
|
||||
### Build status
|
||||
| Branch | Status |
|
||||
|
@ -56,13 +58,21 @@ and follow the instructions at the readme files under [infection_monkey](infecti
|
|||
| Master | [![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=master)](https://travis-ci.com/guardicore/monkey) |
|
||||
|
||||
## Tests
|
||||
|
||||
### Unit Tests
|
||||
|
||||
In order to run all of the Unit Tests, run the command `python -m pytest` in the `monkey` directory.
|
||||
|
||||
To get a coverage report, first make sure the `coverage` package is installed using `pip install coverage`. Run the command
|
||||
`coverage run -m unittest` in the `monkey` directory and then `coverage html`. The coverage report can be found in
|
||||
`htmlcov.index`.
|
||||
|
||||
### Blackbox tests
|
||||
|
||||
In order to run the Blackbox tests, refer to `envs/monkey_zoo/blackbox/README.md`.
|
||||
|
||||
# License
|
||||
|
||||
Copyright (c) Guardicore Ltd
|
||||
|
||||
See the [LICENSE](LICENSE) file for license rights and limitations (GPLv3).
|
||||
|
|
|
@ -1,25 +1,55 @@
|
|||
# Files used to deploy development version of infection monkey
|
||||
## Windows
|
||||
# Deployment guide for a development environemnt
|
||||
|
||||
Before running the script you must have git installed.<br>
|
||||
Cd to scripts directory and use the scripts.<br>
|
||||
First argument is an empty directory (script can create one) and second is branch you want to clone.
|
||||
Example usages:<br>
|
||||
./run_script.bat (Sets up monkey in current directory under .\infection_monkey)<br>
|
||||
./run_script.bat "C:\test" (Sets up monkey in C:\test)<br>
|
||||
powershell -ExecutionPolicy ByPass -Command ". .\deploy_windows.ps1; Deploy-Windows -monkey_home C:\test" (Same as above)<br>
|
||||
./run_script.bat "" "master"(Sets up master branch instead of develop in current dir)
|
||||
Don't forget to add python to PATH or do so while installing it via this script.<br>
|
||||
This guide is for you if you wish to develop for Infection Monkey. If you only want to use it, please download the relevant version from [our website](https://infectionmonkey.com).
|
||||
|
||||
## Linux
|
||||
## Prerequisites
|
||||
|
||||
Linux deployment script is meant for Ubuntu 16.x machines.
|
||||
You must have root permissions, but don't run the script as root.<br>
|
||||
Launch deploy_linux.sh from scripts directory.<br>
|
||||
First argument should be an absolute path of an empty directory (script will create one if doesn't exist, default is ./infection_monkey).
|
||||
Second parameter is the branch you want to clone (develop by default).
|
||||
Example usages:<br>
|
||||
./deploy_linux.sh (deploys under ./infection_monkey)<br>
|
||||
./deploy_linux.sh "/home/test/monkey" (deploys under /home/test/monkey)<br>
|
||||
./deploy_linux.sh "" "master" (deploys master branch in script directory)<br>
|
||||
./deploy_linux.sh "/home/user/new" "master" (if directory "new" is not found creates it and clones master branch into it)<br>
|
||||
Before running the script you must have `git` installed. If you don't have `git` installed, please follow [this guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).
|
||||
|
||||
## Deploy on Windows
|
||||
|
||||
Run the following command in powershell:
|
||||
|
||||
```powershell
|
||||
Invoke-WebRequest https://raw.githubusercontent.com/guardicore/monkey/develop/deployment_scripts/deploy_windows.ps1 -OutFile deploy_windows.ps1
|
||||
```
|
||||
|
||||
This will download our deploy script. It's a good idea to read it quickly before executing it!
|
||||
|
||||
After downloading that script, execute it in `powershell`.
|
||||
|
||||
The first argument is an empty directory (script can create one). The second argument is which branch you want to clone - by default, the script will check out the `develop` branch. Some example usages:
|
||||
|
||||
- `.\deploy_windows.ps1` (Sets up monkey in current directory under .\infection_monkey)
|
||||
- `.\deploy_windows.ps1 -monkey_home "C:\test"` (Sets up monkey in C:\test)
|
||||
- `.\deploy_windows.ps1 -branch "master"` (Sets up master branch instead of develop in current dir)
|
||||
|
||||
You may also pass in an optional `agents=$false` parameter to disable downloading the latest agent binaries.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
- If you run into Execution Policy warnings, you can disable them by prefixing the following snippet: `powershell -ExecutionPolicy ByPass -Command "[original command here]"`
|
||||
- Don't forget to add python to PATH or do so while installing it via this script.
|
||||
|
||||
## Deploy on Linux
|
||||
|
||||
Linux deployment script is meant for Ubuntu 16 and Ubuntu 18 machines.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
This will download our deploy script. It's a good idea to read it quickly before executing it!
|
||||
|
||||
Then execute the resulting script with your shell.
|
||||
|
||||
After downloading that script, execute it in a shell. The first argument should be an absolute path of an empty directory (the script will create one if doesn't exist, default is ./infection_monkey). The second parameter is the branch you want to clone (develop by default). Some example usages:
|
||||
|
||||
- `./deploy_linux.sh` (deploys under ./infection_monkey)
|
||||
- `./deploy_linux.sh "/home/test/monkey"` (deploys under /home/test/monkey)
|
||||
- `./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.
|
|
@ -5,21 +5,17 @@ MONKEY_FOLDER_NAME="infection_monkey"
|
|||
MONKEY_GIT_URL="https://github.com/guardicore/monkey"
|
||||
|
||||
# Monkey binaries
|
||||
LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-32"
|
||||
LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/monkey-linux-32"
|
||||
LINUX_32_BINARY_NAME="monkey-linux-32"
|
||||
LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-64"
|
||||
LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/monkey-linux-64"
|
||||
LINUX_64_BINARY_NAME="monkey-linux-64"
|
||||
WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/monkey-windows-32.exe"
|
||||
WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/monkey-windows-32.exe"
|
||||
WINDOWS_32_BINARY_NAME="monkey-windows-32.exe"
|
||||
WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/monkey-windows-64.exe"
|
||||
WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/monkey-windows-64.exe"
|
||||
WINDOWS_64_BINARY_NAME="monkey-windows-64.exe"
|
||||
|
||||
# Other binaries for monkey
|
||||
TRACEROUTE_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/traceroute64"
|
||||
TRACEROUTE_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/traceroute32"
|
||||
SAMBACRY_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner64.so"
|
||||
SAMBACRY_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner32.so"
|
||||
|
||||
# Mongo url's
|
||||
MONGO_DEBIAN_URL="https://downloads.mongodb.org/linux/mongodb-linux-x86_64-debian81-latest.tgz"
|
||||
MONGO_UBUNTU_URL="https://downloads.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-latest.tgz"
|
||||
TRACEROUTE_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/traceroute64"
|
||||
TRACEROUTE_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/traceroute32"
|
||||
SAMBACRY_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/sc_monkey_runner64.so"
|
||||
SAMBACRY_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/sc_monkey_runner32.so"
|
|
@ -2,28 +2,33 @@
|
|||
$MONKEY_FOLDER_NAME = "infection_monkey"
|
||||
# Url of public git repository that contains monkey's source code
|
||||
$MONKEY_GIT_URL = "https://github.com/guardicore/monkey"
|
||||
$MONKEY_RELEASES_URL = $MONKEY_GIT_URL + "/releases"
|
||||
$MONKEY_LATEST_VERSION = "v1.7.0"
|
||||
$MONKEY_DOWNLOAD_URL = $MONKEY_RELEASES_URL + "/download/" + $MONKEY_LATEST_VERSION + "/"
|
||||
# Link to the latest python download or install it manually
|
||||
$PYTHON_URL = "https://www.python.org/ftp/python/3.7.4/python-3.7.4-amd64.exe"
|
||||
$PYTHON_URL = "https://www.python.org/ftp/python/3.7.6/python-3.7.6-amd64.exe"
|
||||
|
||||
|
||||
# Monkey binaries
|
||||
$LINUX_32_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-32"
|
||||
$LINUX_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-linux-32"
|
||||
$LINUX_32_BINARY_PATH = "monkey-linux-32"
|
||||
$LINUX_64_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-64"
|
||||
$LINUX_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-linux-64"
|
||||
$LINUX_64_BINARY_PATH = "monkey-linux-64"
|
||||
$WINDOWS_32_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-windows-32.exe"
|
||||
$WINDOWS_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-windows-32.exe"
|
||||
$WINDOWS_32_BINARY_PATH = "monkey-windows-32.exe"
|
||||
$WINDOWS_64_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-windows-64.exe"
|
||||
$WINDOWS_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-windows-64.exe"
|
||||
$WINDOWS_64_BINARY_PATH = "monkey-windows-64.exe"
|
||||
$SAMBA_32_BINARY_URL = "https://github.com/VakarisZ/tempBinaries/raw/master/sc_monkey_runner32.so"
|
||||
$SAMBA_32_BINARY_NAME= "sc_monkey_runner32.so"
|
||||
$SAMBA_64_BINARY_URL = "https://github.com/VakarisZ/tempBinaries/raw/master/sc_monkey_runner64.so"
|
||||
$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 = "\monkey\monkey_island"
|
||||
$MONKEY_DIR = "\monkey\infection_monkey"
|
||||
$MONKEY_ISLAND_DIR = Join-Path "\monkey" -ChildPath "monkey_island"
|
||||
$MONKEY_DIR = Join-Path "\monkey" -ChildPath "infection_monkey"
|
||||
$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\bin"
|
||||
$PYTHON_DLL = "C:\Windows\System32\python27.dll"
|
||||
$MK32_DLL = "mk32.zip"
|
||||
$MK64_DLL = "mk64.zip"
|
||||
$TEMP_PYTHON_INSTALLER = ".\python.exe"
|
||||
|
@ -31,16 +36,14 @@ $TEMP_MONGODB_ZIP = ".\mongodb.zip"
|
|||
$TEMP_OPEN_SSL_ZIP = ".\openssl.zip"
|
||||
$TEMP_CPP_INSTALLER = "cpp.exe"
|
||||
$TEMP_NPM_INSTALLER = "node.msi"
|
||||
$TEMP_PYWIN32_INSTALLER = "pywin32.exe"
|
||||
$TEMP_UPX_ZIP = "upx.zip"
|
||||
$UPX_FOLDER = "upx394w"
|
||||
$UPX_FOLDER = "upx-3.96-win64"
|
||||
|
||||
# Other url's
|
||||
$MONGODB_URL = "https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip"
|
||||
$OPEN_SSL_URL = "https://indy.fulgan.com/SSL/Archive/openssl-1.0.2l-i386-win32.zip"
|
||||
$MONGODB_URL = "https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2012plus-v4.2-latest.zip"
|
||||
$OPEN_SSL_URL = "https://indy.fulgan.com/SSL/openssl-1.0.2u-x64_86-win64.zip"
|
||||
$CPP_URL = "https://go.microsoft.com/fwlink/?LinkId=746572"
|
||||
$NPM_URL = "https://nodejs.org/dist/v10.13.0/node-v10.13.0-x64.msi"
|
||||
$PYWIN32_URL = "https://github.com/mhammond/pywin32/releases/download/b225/pywin32-225.win-amd64-py3.7.exe"
|
||||
$NPM_URL = "https://nodejs.org/dist/v12.14.1/node-v12.14.1-x64.msi"
|
||||
$MK32_DLL_URL = "https://github.com/guardicore/mimikatz/releases/download/1.1.0/mk32.zip"
|
||||
$MK64_DLL_URL = "https://github.com/guardicore/mimikatz/releases/download/1.1.0/mk64.zip"
|
||||
$UPX_URL = "https://github.com/upx/upx/releases/download/v3.94/upx394w.zip"
|
||||
$UPX_URL = "https://github.com/upx/upx/releases/download/v3.96/upx-3.96-win64.zip"
|
||||
|
|
|
@ -1,10 +1,54 @@
|
|||
#!/bin/bash
|
||||
source config
|
||||
|
||||
exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
is_root() {
|
||||
return $(id -u)
|
||||
}
|
||||
|
||||
has_sudo() {
|
||||
# 0 true, 1 false
|
||||
timeout 1 sudo id && return 0 || return 1
|
||||
}
|
||||
|
||||
handle_error() {
|
||||
echo "Fix the errors above and rerun the script"
|
||||
exit 1
|
||||
}
|
||||
|
||||
log_message() {
|
||||
echo -e "\n\n"
|
||||
echo -e "DEPLOYMENT SCRIPT: $1"
|
||||
}
|
||||
|
||||
config_branch=${2:-"develop"}
|
||||
config_url="https://raw.githubusercontent.com/guardicore/monkey/${config_branch}/deployment_scripts/config"
|
||||
|
||||
if (! exists curl) && (! exists wget); then
|
||||
log_message 'Your system does not have curl or wget, exiting'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
file=$(mktemp)
|
||||
# shellcheck disable=SC2086
|
||||
if exists wget; then
|
||||
# shellcheck disable=SC2086
|
||||
wget --output-document=$file "$config_url"
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
curl -s -o $file "$config_url"
|
||||
fi
|
||||
|
||||
log_message "downloaded configuration"
|
||||
# shellcheck source=deployment_scripts/config
|
||||
# shellcheck disable=SC2086
|
||||
source $file
|
||||
log_message "loaded configuration"
|
||||
# shellcheck disable=SC2086
|
||||
# rm $file
|
||||
|
||||
# Setup monkey either in dir required or current dir
|
||||
monkey_home=${1:-$(pwd)}
|
||||
if [[ $monkey_home == $(pwd) ]]; then
|
||||
|
@ -13,26 +57,19 @@ fi
|
|||
|
||||
# We can set main paths after we know the home dir
|
||||
ISLAND_PATH="$monkey_home/monkey/monkey_island"
|
||||
MONKEY_COMMON_PATH="$monkey_home/monkey/common/"
|
||||
MONGO_PATH="$ISLAND_PATH/bin/mongodb"
|
||||
ISLAND_BINARIES_PATH="$ISLAND_PATH/cc/binaries"
|
||||
INFECTION_MONKEY_DIR="$monkey_home/monkey/infection_monkey"
|
||||
MONKEY_BIN_DIR="$INFECTION_MONKEY_DIR/bin"
|
||||
|
||||
handle_error() {
|
||||
echo "Fix the errors above and rerun the script"
|
||||
if is_root; then
|
||||
log_message "Please don't run this script as root"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
log_message() {
|
||||
echo -e "\n\n-------------------------------------------"
|
||||
echo -e "DEPLOYMENT SCRIPT: $1"
|
||||
echo -e "-------------------------------------------\n"
|
||||
}
|
||||
|
||||
sudo -v
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "You need root permissions for some of this script operations. Quiting."
|
||||
HAS_SUDO=$(has_sudo)
|
||||
if [[ ! $HAS_SUDO ]]; then
|
||||
log_message "You need root permissions for some of this script operations. Quiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -41,15 +78,10 @@ if [[ ! -d ${monkey_home} ]]; then
|
|||
fi
|
||||
|
||||
if ! exists git; then
|
||||
echo "Please install git and re-run this script"
|
||||
log_message "Please install git and re-run this script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! exists wget; then
|
||||
echo 'Your system does have wget, please install and re-run this script'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_message "Cloning files from git"
|
||||
branch=${2:-"develop"}
|
||||
if [[ ! -d "$monkey_home/monkey" ]]; then # If not already cloned
|
||||
|
@ -59,7 +91,7 @@ fi
|
|||
|
||||
# Create folders
|
||||
log_message "Creating island dirs under $ISLAND_PATH"
|
||||
mkdir -p "${MONGO_PATH}"
|
||||
mkdir -p "${MONGO_PATH}" || handle_error
|
||||
mkdir -p "${ISLAND_BINARIES_PATH}" || handle_error
|
||||
|
||||
# Detecting command that calls python 3.7
|
||||
|
@ -78,87 +110,114 @@ 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
|
||||
sudo apt install python3.7 python3.7-dev
|
||||
log_message "Python 3.7 is now available with command 'python3.7'."
|
||||
python_cmd="python3.7"
|
||||
fi
|
||||
|
||||
log_message "Updating package list"
|
||||
sudo apt-get update
|
||||
log_message "Installing build-essential"
|
||||
sudo apt install build-essential
|
||||
|
||||
log_message "Installing pip"
|
||||
sudo apt install python3-pip
|
||||
${python_cmd} -m pip install pip
|
||||
|
||||
log_message "Install python3.7-dev"
|
||||
sudo apt-get install python3.7-dev
|
||||
log_message "Installing or updating pip"
|
||||
# shellcheck disable=SC2086
|
||||
pip_url=https://bootstrap.pypa.io/get-pip.py
|
||||
if exists wget; then
|
||||
wget --output-document=get-pip.py $pip_url
|
||||
else
|
||||
curl $pip_url -o get-pip.py
|
||||
fi
|
||||
${python_cmd} get-pip.py
|
||||
rm get-pip.py
|
||||
|
||||
log_message "Installing island requirements"
|
||||
requirements="$ISLAND_PATH/requirements.txt"
|
||||
${python_cmd} -m pip install --user --upgrade -r ${requirements} || handle_error
|
||||
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
|
||||
cd "${monkey_home}"/monkey/infection_monkey || handle_error
|
||||
${python_cmd} -m pip install -r requirements.txt --user --upgrade || handle_error
|
||||
requirements_monkey="$INFECTION_MONKEY_DIR/requirements.txt"
|
||||
${python_cmd} -m pip install -r "${requirements_monkey}" --user --upgrade || handle_error
|
||||
|
||||
|
||||
agents=${3:-true}
|
||||
# Download binaries
|
||||
log_message "Downloading binaries"
|
||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_32_BINARY_URL}
|
||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_64_BINARY_URL}
|
||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_32_BINARY_URL}
|
||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_64_BINARY_URL}
|
||||
if [ "$agents" = true ] ; then
|
||||
log_message "Downloading binaries"
|
||||
if exists wget; then
|
||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_32_BINARY_URL}
|
||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_64_BINARY_URL}
|
||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_32_BINARY_URL}
|
||||
wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_64_BINARY_URL}
|
||||
else
|
||||
curl -o ${ISLAND_BINARIES_PATH}\monkey-linux-32 ${LINUX_32_BINARY_URL}
|
||||
curl -o ${ISLAND_BINARIES_PATH}\monkey-linux-64 ${LINUX_64_BINARY_URL}
|
||||
curl -o ${ISLAND_BINARIES_PATH}\monkey-windows-32.exe ${WINDOWS_32_BINARY_URL}
|
||||
curl -o ${ISLAND_BINARIES_PATH}\monkey-windows-64.exe ${WINDOWS_64_BINARY_URL}
|
||||
fi
|
||||
fi
|
||||
|
||||
# Allow them to be executed
|
||||
chmod a+x "$ISLAND_BINARIES_PATH/$LINUX_32_BINARY_NAME"
|
||||
chmod a+x "$ISLAND_BINARIES_PATH/$LINUX_64_BINARY_NAME"
|
||||
|
||||
# Get machine type/kernel version
|
||||
kernel=$(uname -m)
|
||||
linux_dist=$(lsb_release -a 2>/dev/null)
|
||||
|
||||
# If a user haven't installed mongo manually check if we can install it with our script
|
||||
log_message "Installing MongoDB"
|
||||
"${ISLAND_PATH}"/linux/install_mongo.sh ${MONGO_PATH} || handle_error
|
||||
|
||||
if ! exists mongod; then
|
||||
log_message "Installing MongoDB"
|
||||
"${ISLAND_PATH}"/linux/install_mongo.sh ${MONGO_PATH} || handle_error
|
||||
fi
|
||||
log_message "Installing openssl"
|
||||
sudo apt-get install openssl
|
||||
|
||||
# Generate SSL certificate
|
||||
log_message "Generating certificate"
|
||||
cd "${ISLAND_PATH}" || {
|
||||
echo "cd failed"
|
||||
exit 1
|
||||
}
|
||||
openssl genrsa -out cc/server.key 2048
|
||||
openssl req -new -key cc/server.key -out cc/server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com"
|
||||
openssl x509 -req -days 366 -in cc/server.csr -signkey cc/server.key -out cc/server.crt
|
||||
|
||||
"${ISLAND_PATH}"/linux/create_certificate.sh ${ISLAND_PATH}/cc
|
||||
|
||||
# Update node
|
||||
log_message "Installing nodejs"
|
||||
cd "$ISLAND_PATH/cc/ui" || handle_error
|
||||
sudo apt-get install curl
|
||||
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
if ! exists npm; then
|
||||
log_message "Installing nodejs"
|
||||
node_src=https://deb.nodesource.com/setup_12.x
|
||||
if exists curl; then
|
||||
curl -sL $node_src | sudo -E bash -
|
||||
else
|
||||
wget -q -O - $node_src | sudo -E bash -
|
||||
fi
|
||||
sudo apt-get install -y nodejs
|
||||
fi
|
||||
|
||||
pushd "$ISLAND_PATH/cc/ui" || handle_error
|
||||
npm install sass-loader node-sass webpack --save-dev
|
||||
npm update
|
||||
|
||||
log_message "Generating front end"
|
||||
npm run dist
|
||||
popd || handle_error
|
||||
|
||||
# Making dir for binaries
|
||||
mkdir "${MONKEY_BIN_DIR}"
|
||||
|
||||
# Download sambacry binaries
|
||||
log_message "Downloading sambacry binaries"
|
||||
wget -c -N -P "${MONKEY_BIN_DIR}" "${SAMBACRY_64_BINARY_URL}"
|
||||
wget -c -N -P "${MONKEY_BIN_DIR}" "${SAMBACRY_32_BINARY_URL}"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if exists wget; then
|
||||
wget -c -N -P "${MONKEY_BIN_DIR}" ${SAMBACRY_64_BINARY_URL}
|
||||
wget -c -N -P "${MONKEY_BIN_DIR}" ${SAMBACRY_32_BINARY_URL}
|
||||
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"
|
||||
wget -c -N -P "${MONKEY_BIN_DIR}" "${TRACEROUTE_64_BINARY_URL}"
|
||||
wget -c -N -P "${MONKEY_BIN_DIR}" "${TRACEROUTE_32_BINARY_URL}"
|
||||
# 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
|
||||
|
||||
sudo chmod +x "${monkey_home}"/monkey/infection_monkey/build_linux.sh
|
||||
sudo chmod +x "${INFECTION_MONKEY_DIR}/build_linux.sh"
|
||||
|
||||
log_message "Deployment script finished."
|
||||
exit 0
|
||||
|
|
|
@ -1,17 +1,40 @@
|
|||
function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, [String] $branch = "develop"){
|
||||
# Import the config variables
|
||||
. ./config.ps1
|
||||
"Config variables from config.ps1 imported"
|
||||
|
||||
# If we want monkey in current dir we need to create an empty folder for source files
|
||||
if ( (Join-Path $monkey_home '') -eq (Join-Path (Get-Item -Path ".\").FullName '') ){
|
||||
$monkey_home = Join-Path -Path $monkey_home -ChildPath $MONKEY_FOLDER_NAME
|
||||
}
|
||||
param(
|
||||
[Parameter(Mandatory = $false, Position = 0)]
|
||||
[String] $monkey_home = (Get-Item -Path ".\").FullName,
|
||||
|
||||
[Parameter(Mandatory = $false, Position = 1)]
|
||||
[System.String]
|
||||
$branch = "develop",
|
||||
[Parameter(Mandatory = $false, Position = 2)]
|
||||
[Bool]
|
||||
$agents = $true
|
||||
)
|
||||
function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, [String] $branch = "develop")
|
||||
{
|
||||
Write-Output "Downloading to $monkey_home"
|
||||
Write-Output "Branch $branch"
|
||||
# Set variables for script execution
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
|
||||
|
||||
# Import the config variables
|
||||
$config_filename = New-TemporaryFile
|
||||
$config_filename = "config.ps1"
|
||||
$config_url = "https://raw.githubusercontent.com/guardicore/monkey/" + $branch + "/deployment_scripts/config.ps1"
|
||||
$webClient.DownloadFile($config_url, $config_filename)
|
||||
. ./config.ps1
|
||||
"Config variables from config.ps1 imported"
|
||||
Remove-Item $config_filename
|
||||
|
||||
|
||||
# If we want monkey in current dir we need to create an empty folder for source files
|
||||
if ((Join-Path $monkey_home '') -eq (Join-Path (Get-Item -Path ".\").FullName ''))
|
||||
{
|
||||
$monkey_home = Join-Path -Path $monkey_home -ChildPath $MONKEY_FOLDER_NAME
|
||||
}
|
||||
|
||||
|
||||
# We check if git is installed
|
||||
try
|
||||
{
|
||||
|
@ -25,15 +48,22 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
}
|
||||
|
||||
# Download the monkey
|
||||
$output = cmd.exe /c "git clone --single-branch -b $branch $MONKEY_GIT_URL $monkey_home 2>&1"
|
||||
$command = "git clone --single-branch -b $branch $MONKEY_GIT_URL $monkey_home 2>&1"
|
||||
Write-Output $command
|
||||
$output = cmd.exe /c $command
|
||||
$binDir = (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\bin")
|
||||
if ( $output -like "*already exists and is not an empty directory.*"){
|
||||
if ($output -like "*already exists and is not an empty directory.*")
|
||||
{
|
||||
"Assuming you already have the source directory. If not, make sure to set an empty directory as monkey's home directory."
|
||||
} elseif ($output -like "fatal:*"){
|
||||
}
|
||||
elseif ($output -like "fatal:*")
|
||||
{
|
||||
"Error while cloning monkey from the repository:"
|
||||
$output
|
||||
return
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
"Monkey cloned from the repository"
|
||||
# Create bin directory
|
||||
New-Item -ItemType directory -path $binDir
|
||||
|
@ -44,9 +74,12 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
try
|
||||
{
|
||||
$version = cmd.exe /c '"python" --version 2>&1'
|
||||
if ( $version -like 'Python 3.*' ) {
|
||||
if ($version -like 'Python 3.*')
|
||||
{
|
||||
"Python 3.* was found, installing dependencies"
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw System.Management.Automation.CommandNotFoundException
|
||||
}
|
||||
}
|
||||
|
@ -56,11 +89,12 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
"Select 'add to PATH' when installing"
|
||||
$webClient.DownloadFile($PYTHON_URL, $TEMP_PYTHON_INSTALLER)
|
||||
Start-Process -Wait $TEMP_PYTHON_INSTALLER -ErrorAction Stop
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||
Remove-Item $TEMP_PYTHON_INSTALLER
|
||||
# Check if installed correctly
|
||||
$version = cmd.exe /c '"python" --version 2>&1'
|
||||
if ( $version -like '* is not recognized*' ) {
|
||||
if ($version -like '* is not recognized*')
|
||||
{
|
||||
"Python is not found in PATH. Add it to PATH and relaunch the script."
|
||||
return
|
||||
}
|
||||
|
@ -69,7 +103,8 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
"Upgrading pip..."
|
||||
$output = cmd.exe /c 'python -m pip install --user --upgrade pip 2>&1'
|
||||
$output
|
||||
if ( $output -like '*No module named pip*' ) {
|
||||
if ($output -like '*No module named pip*')
|
||||
{
|
||||
"Make sure pip module is installed and re-run this script."
|
||||
return
|
||||
}
|
||||
|
@ -83,20 +118,24 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
|
||||
$user_python_dir = cmd.exe /c 'py -m site --user-site'
|
||||
$user_python_dir = Join-Path (Split-Path $user_python_dir) -ChildPath "\Scripts"
|
||||
if(!($ENV:PATH | Select-String -SimpleMatch $user_python_dir)){
|
||||
if (!($ENV:Path | Select-String -SimpleMatch $user_python_dir))
|
||||
{
|
||||
"Adding python scripts path to user's env"
|
||||
$env:Path += ";"+$user_python_dir
|
||||
[Environment]::SetEnvironmentVariable("Path",$env:Path,"User")
|
||||
$env:Path += ";" + $user_python_dir
|
||||
[Environment]::SetEnvironmentVariable("Path", $env:Path, "User")
|
||||
}
|
||||
|
||||
# Download mongodb
|
||||
if(!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "mongodb") )){
|
||||
if (!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "mongodb")))
|
||||
{
|
||||
"Downloading mongodb ..."
|
||||
$webClient.DownloadFile($MONGODB_URL, $TEMP_MONGODB_ZIP)
|
||||
"Unzipping mongodb"
|
||||
Expand-Archive $TEMP_MONGODB_ZIP -DestinationPath $binDir
|
||||
# Get unzipped folder's name
|
||||
$mongodb_folder = Get-ChildItem -Path $binDir | Where-Object -FilterScript {($_.Name -like "mongodb*")} | Select-Object -ExpandProperty Name
|
||||
$mongodb_folder = Get-ChildItem -Path $binDir | Where-Object -FilterScript {
|
||||
($_.Name -like "mongodb*")
|
||||
} | Select-Object -ExpandProperty Name
|
||||
# Move all files from extracted folder to mongodb folder
|
||||
New-Item -ItemType directory -Path (Join-Path -Path $binDir -ChildPath "mongodb")
|
||||
New-Item -ItemType directory -Path (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "db")
|
||||
|
@ -127,23 +166,30 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
. .\windows\create_certificate.bat
|
||||
Pop-Location
|
||||
|
||||
# Adding binaries
|
||||
"Adding binaries"
|
||||
$binaries = (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\cc\binaries")
|
||||
New-Item -ItemType directory -path $binaries -ErrorAction SilentlyContinue
|
||||
$webClient.DownloadFile($LINUX_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_32_BINARY_PATH))
|
||||
$webClient.DownloadFile($LINUX_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_64_BINARY_PATH))
|
||||
$webClient.DownloadFile($WINDOWS_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_32_BINARY_PATH))
|
||||
$webClient.DownloadFile($WINDOWS_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_64_BINARY_PATH))
|
||||
if ($agents)
|
||||
{
|
||||
# Adding binaries
|
||||
"Adding binaries"
|
||||
$binaries = (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\cc\binaries")
|
||||
New-Item -ItemType directory -path $binaries -ErrorAction SilentlyContinue
|
||||
$webClient.DownloadFile($LINUX_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_32_BINARY_PATH))
|
||||
$webClient.DownloadFile($LINUX_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_64_BINARY_PATH))
|
||||
$webClient.DownloadFile($WINDOWS_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_32_BINARY_PATH))
|
||||
$webClient.DownloadFile($WINDOWS_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_64_BINARY_PATH))
|
||||
}
|
||||
|
||||
|
||||
# Check if NPM installed
|
||||
"Installing npm"
|
||||
try
|
||||
{
|
||||
$version = cmd.exe /c '"npm" --version 2>&1'
|
||||
if ( $version -like "*is not recognized*"){
|
||||
if ($version -like "*is not recognized*")
|
||||
{
|
||||
throw System.Management.Automation.CommandNotFoundException
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
"Npm already installed"
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +198,7 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
"Downloading npm ..."
|
||||
$webClient.DownloadFile($NPM_URL, $TEMP_NPM_INSTALLER)
|
||||
Start-Process -Wait $TEMP_NPM_INSTALLER
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine")
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
|
||||
Remove-Item $TEMP_NPM_INSTALLER
|
||||
}
|
||||
|
||||
|
@ -162,18 +208,13 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
& npm run dist
|
||||
Pop-Location
|
||||
|
||||
# Install pywin32
|
||||
"Downloading pywin32"
|
||||
$webClient.DownloadFile($PYWIN32_URL, $TEMP_PYWIN32_INSTALLER)
|
||||
Start-Process -Wait $TEMP_PYWIN32_INSTALLER -ErrorAction Stop
|
||||
Remove-Item $TEMP_PYWIN32_INSTALLER
|
||||
|
||||
# Create infection_monkey/bin directory if not already present
|
||||
$binDir = (Join-Path -Path $monkey_home -ChildPath $MONKEY_DIR | Join-Path -ChildPath "\bin")
|
||||
New-Item -ItemType directory -path $binaries -ErrorAction SilentlyContinue
|
||||
|
||||
# Download upx
|
||||
if(!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "upx.exe") )){
|
||||
if (!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "upx.exe")))
|
||||
{
|
||||
"Downloading upx ..."
|
||||
$webClient.DownloadFile($UPX_URL, $TEMP_UPX_ZIP)
|
||||
"Unzipping upx"
|
||||
|
@ -187,12 +228,14 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
|
||||
# Download mimikatz binaries
|
||||
$mk32_path = Join-Path -Path $binDir -ChildPath $MK32_DLL
|
||||
if(!(Test-Path -Path $mk32_path )){
|
||||
if (!(Test-Path -Path $mk32_path))
|
||||
{
|
||||
"Downloading mimikatz 32 binary"
|
||||
$webClient.DownloadFile($MK32_DLL_URL, $mk32_path)
|
||||
}
|
||||
$mk64_path = Join-Path -Path $binDir -ChildPath $MK64_DLL
|
||||
if(!(Test-Path -Path $mk64_path )){
|
||||
if (!(Test-Path -Path $mk64_path))
|
||||
{
|
||||
"Downloading mimikatz 64 binary"
|
||||
$webClient.DownloadFile($MK64_DLL_URL, $mk64_path)
|
||||
}
|
||||
|
@ -200,12 +243,14 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
# Download sambacry binaries
|
||||
$samba_path = Join-Path -Path $monkey_home -ChildPath $SAMBA_BINARIES_DIR
|
||||
$samba32_path = Join-Path -Path $samba_path -ChildPath $SAMBA_32_BINARY_NAME
|
||||
if(!(Test-Path -Path $samba32_path )){
|
||||
if (!(Test-Path -Path $samba32_path))
|
||||
{
|
||||
"Downloading sambacry 32 binary"
|
||||
$webClient.DownloadFile($SAMBA_32_BINARY_URL, $samba32_path)
|
||||
}
|
||||
$samba64_path = Join-Path -Path $samba_path -ChildPath $SAMBA_64_BINARY_NAME
|
||||
if(!(Test-Path -Path $samba64_path )){
|
||||
if (!(Test-Path -Path $samba64_path))
|
||||
{
|
||||
"Downloading sambacry 64 binary"
|
||||
$webClient.DownloadFile($SAMBA_64_BINARY_URL, $samba64_path)
|
||||
}
|
||||
|
@ -213,3 +258,4 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
"Script finished"
|
||||
|
||||
}
|
||||
Deploy-Windows -monkey_home $monkey_home -branch $branch
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
SET command=. .\deploy_windows.ps1; Deploy-Windows
|
||||
if NOT "%~1" == "" (
|
||||
SET "command=%command% -monkey_home %~1"
|
||||
)
|
||||
if NOT "%~2" == "" (
|
||||
SET "command=%command% -branch %~2"
|
||||
)
|
||||
powershell -ExecutionPolicy ByPass -Command %command%
|
|
@ -0,0 +1,20 @@
|
|||
codecov:
|
||||
require_ci_to_pass: yes
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "50...90"
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
conditional: yes
|
||||
loop: yes
|
||||
method: no
|
||||
macro: no
|
||||
|
||||
comment:
|
||||
layout: "reach,diff,flags,tree"
|
||||
behavior: default
|
||||
require_changes: no
|
|
@ -0,0 +1 @@
|
|||
dev
|
|
@ -0,0 +1,12 @@
|
|||
from typing import List
|
||||
|
||||
from common.cloud.aws.aws_instance import AwsInstance
|
||||
from common.cloud.azure.azure_instance import AzureInstance
|
||||
from common.cloud.gcp.gcp_instance import GcpInstance
|
||||
from common.cloud.instance import CloudInstance
|
||||
|
||||
all_cloud_instances = [AwsInstance(), AzureInstance(), GcpInstance()]
|
||||
|
||||
|
||||
def get_all_cloud_instances() -> List[CloudInstance]:
|
||||
return all_cloud_instances
|
|
@ -6,6 +6,9 @@ import logging
|
|||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
from common.cloud.environment_names import Environment
|
||||
from common.cloud.instance import CloudInstance
|
||||
|
||||
AWS_INSTANCE_METADATA_LOCAL_IP_ADDRESS = "169.254.169.254"
|
||||
AWS_LATEST_METADATA_URI_PREFIX = 'http://{0}/latest/'.format(AWS_INSTANCE_METADATA_LOCAL_IP_ADDRESS)
|
||||
ACCOUNT_ID_KEY = "accountId"
|
||||
|
@ -13,10 +16,15 @@ ACCOUNT_ID_KEY = "accountId"
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AwsInstance(object):
|
||||
class AwsInstance(CloudInstance):
|
||||
"""
|
||||
Class which gives useful information about the current instance you're on.
|
||||
"""
|
||||
def is_instance(self):
|
||||
return self.instance_id is not None
|
||||
|
||||
def get_cloud_provider_name(self) -> Environment:
|
||||
return Environment.AWS
|
||||
|
||||
def __init__(self):
|
||||
self.instance_id = None
|
||||
|
@ -57,9 +65,6 @@ class AwsInstance(object):
|
|||
def get_region(self):
|
||||
return self.region
|
||||
|
||||
def is_aws_instance(self):
|
||||
return self.instance_id is not None
|
||||
|
||||
@staticmethod
|
||||
def _extract_account_id(instance_identity_document_response):
|
||||
"""
|
|
@ -4,7 +4,7 @@ import boto3
|
|||
import botocore
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
from common.cloud.aws_instance import AwsInstance
|
||||
from common.cloud.aws.aws_instance import AwsInstance
|
||||
|
||||
__author__ = ['itay.mizeretz', 'shay.nehmad']
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import logging
|
||||
import requests
|
||||
|
||||
from common.cloud.environment_names import Environment
|
||||
from common.cloud.instance import CloudInstance
|
||||
|
||||
LATEST_AZURE_METADATA_API_VERSION = "2019-04-30"
|
||||
AZURE_METADATA_SERVICE_URL = "http://169.254.169.254/metadata/instance?api-version=%s" % LATEST_AZURE_METADATA_API_VERSION
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AzureInstance(CloudInstance):
|
||||
"""
|
||||
Access to useful information about the current machine if it's an Azure VM.
|
||||
Based on Azure metadata service: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service
|
||||
"""
|
||||
def is_instance(self):
|
||||
return self.on_azure
|
||||
|
||||
def get_cloud_provider_name(self) -> Environment:
|
||||
return Environment.AZURE
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Determines if on Azure and if so, gets some basic metadata on this instance.
|
||||
"""
|
||||
self.instance_name = None
|
||||
self.instance_id = None
|
||||
self.location = None
|
||||
self.on_azure = False
|
||||
|
||||
try:
|
||||
response = requests.get(AZURE_METADATA_SERVICE_URL, headers={"Metadata": "true"})
|
||||
self.on_azure = True
|
||||
|
||||
# If not on cloud, the metadata URL is non-routable and the connection will fail.
|
||||
# If on AWS, should get 404 since the metadata service URL is different, so bool(response) will be false.
|
||||
if response:
|
||||
logger.debug("On Azure. Trying to parse metadata.")
|
||||
self.try_parse_response(response)
|
||||
else:
|
||||
logger.warning("On Azure, but metadata response not ok: {}".format(response.status_code))
|
||||
except requests.RequestException:
|
||||
logger.debug("Failed to get response from Azure metadata service: This instance is not on Azure.")
|
||||
self.on_azure = False
|
||||
|
||||
def try_parse_response(self, response):
|
||||
try:
|
||||
response_data = response.json()
|
||||
self.instance_name = response_data["compute"]["name"]
|
||||
self.instance_id = response_data["compute"]["vmId"]
|
||||
self.location = response_data["compute"]["location"]
|
||||
except KeyError:
|
||||
logger.exception("Error while parsing response from Azure metadata service.")
|
|
@ -0,0 +1,15 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class Environment(Enum):
|
||||
UNKNOWN = "Unknown"
|
||||
ON_PREMISE = "On Premise"
|
||||
AZURE = "Azure"
|
||||
AWS = "AWS"
|
||||
GCP = "GCP"
|
||||
ALIBABA = "Alibaba Cloud"
|
||||
IBM = "IBM Cloud"
|
||||
DigitalOcean = "Digital Ocean"
|
||||
|
||||
|
||||
ALL_ENVIRONMENTS_NAMES = [x.value for x in Environment]
|
|
@ -0,0 +1,43 @@
|
|||
import logging
|
||||
import requests
|
||||
|
||||
from common.cloud.environment_names import Environment
|
||||
from common.cloud.instance import CloudInstance
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
GCP_METADATA_SERVICE_URL = "http://metadata.google.internal/"
|
||||
|
||||
|
||||
class GcpInstance(CloudInstance):
|
||||
"""
|
||||
Used to determine if on GCP. See https://cloud.google.com/compute/docs/storing-retrieving-metadata#runninggce
|
||||
"""
|
||||
def is_instance(self):
|
||||
return self.on_gcp
|
||||
|
||||
def get_cloud_provider_name(self) -> Environment:
|
||||
return Environment.GCP
|
||||
|
||||
def __init__(self):
|
||||
self.on_gcp = False
|
||||
|
||||
try:
|
||||
# If not on GCP, this domain shouldn't resolve.
|
||||
response = requests.get(GCP_METADATA_SERVICE_URL)
|
||||
|
||||
if response:
|
||||
logger.debug("Got ok metadata response: on GCP")
|
||||
self.on_gcp = True
|
||||
|
||||
if "Metadata-Flavor" not in response.headers:
|
||||
logger.warning("Got unexpected GCP Metadata format")
|
||||
else:
|
||||
if not response.headers["Metadata-Flavor"] == "Google":
|
||||
logger.warning("Got unexpected Metadata flavor: {}".format(response.headers["Metadata-Flavor"]))
|
||||
else:
|
||||
logger.warning("On GCP, but metadata response not ok: {}".format(response.status_code))
|
||||
except requests.RequestException:
|
||||
logger.debug("Failed to get response from GCP metadata service: This instance is not on GCP")
|
||||
self.on_gcp = False
|
|
@ -0,0 +1,14 @@
|
|||
from common.cloud.environment_names import Environment
|
||||
|
||||
|
||||
class CloudInstance(object):
|
||||
"""
|
||||
This is an abstract class which represents a cloud instance.
|
||||
|
||||
The current machine can be a cloud instance (for example EC2 instance or Azure VM).
|
||||
"""
|
||||
def is_instance(self) -> bool:
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_cloud_provider_name(self) -> Environment:
|
||||
raise NotImplementedError()
|
|
@ -1,6 +1,6 @@
|
|||
import logging
|
||||
|
||||
from common.cloud.aws_service import AwsService
|
||||
from common.cloud.aws.aws_service import AwsService
|
||||
from common.cmd.aws.aws_cmd_result import AwsCmdResult
|
||||
from common.cmd.cmd_runner import CmdRunner
|
||||
from common.cmd.cmd_status import CmdStatus
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
AWS_COLLECTOR = "AwsCollector"
|
||||
HOSTNAME_COLLECTOR = "HostnameCollector"
|
||||
ENVIRONMENT_COLLECTOR = "EnvironmentCollector"
|
||||
PROCESS_LIST_COLLECTOR = "ProcessListCollector"
|
|
@ -46,6 +46,7 @@ class NetworkRange(object, metaclass=ABCMeta):
|
|||
def get_range_obj(address_str):
|
||||
if not address_str: # Empty string
|
||||
return None
|
||||
address_str = address_str.strip()
|
||||
if NetworkRange.check_if_range(address_str):
|
||||
return IpRange(ip_range=address_str)
|
||||
if -1 != address_str.find('/'):
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# To get the version from shell, run `python ./version.py` (see `python ./version.py -h` for details).
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
MAJOR = "1"
|
||||
MINOR = "8"
|
||||
PATCH = "0"
|
||||
build_file_path = Path(__file__).parent.joinpath("BUILD")
|
||||
with open(build_file_path, "r") as build_file:
|
||||
BUILD = build_file.read()
|
||||
|
||||
|
||||
def get_version(build=BUILD):
|
||||
return f"{MAJOR}.{MINOR}.{PATCH}+{build}"
|
||||
|
||||
|
||||
def print_version():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-b", "--build", default=BUILD, help="Choose the build string for this version.", type=str)
|
||||
args = parser.parse_args()
|
||||
print(get_version(args.build))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print_version()
|
|
@ -1,2 +1,17 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Allow custom build ID
|
||||
# If the first argument is not empty...
|
||||
if [[ -n "$1" ]]
|
||||
then
|
||||
# Validate argument is a valid build string
|
||||
if [[ "$1" =~ ^[\da-zA-Z]*$ ]]
|
||||
then
|
||||
# And put it in the BUILD file
|
||||
echo "$1" > ../common/BUILD
|
||||
else
|
||||
echo "Build ID $1 invalid!"
|
||||
fi
|
||||
fi
|
||||
|
||||
pyinstaller -F --log-level=DEBUG --clean monkey.spec
|
||||
|
|
|
@ -1 +1,12 @@
|
|||
REM Check if build ID was passed to the build script.
|
||||
if "%1"=="" GOTO START_BUILD
|
||||
|
||||
REM Validate build ID
|
||||
echo %1|findstr /r "^[0-9a-zA-Z]*$"
|
||||
if %errorlevel% neq 0 (exit /b %errorlevel%)
|
||||
|
||||
REM replace build ID
|
||||
echo %1> ../common/BUILD
|
||||
|
||||
:START_BUILD
|
||||
pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import hashlib
|
||||
import os
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from abc import ABCMeta
|
||||
|
@ -125,6 +125,7 @@ class Configuration(object):
|
|||
|
||||
finger_classes = []
|
||||
exploiter_classes = []
|
||||
system_info_collectors_classes = []
|
||||
|
||||
# how many victims to look for in a single scan iteration
|
||||
victims_max_find = 100
|
||||
|
|
|
@ -12,6 +12,7 @@ from infection_monkey.config import WormConfiguration, EXTERNAL_CONFIG_FILE
|
|||
from infection_monkey.dropper import MonkeyDrops
|
||||
from infection_monkey.model import MONKEY_ARG, DROPPER_ARG
|
||||
from infection_monkey.monkey import InfectionMonkey
|
||||
from common.version import get_version
|
||||
# noinspection PyUnresolvedReferences
|
||||
import infection_monkey.post_breach # dummy import for pyinstaller
|
||||
|
||||
|
@ -117,6 +118,8 @@ def main():
|
|||
LOG.info(">>>>>>>>>> Initializing monkey (%s): PID %s <<<<<<<<<<",
|
||||
monkey_cls.__name__, os.getpid())
|
||||
|
||||
LOG.info(f"version: {get_version()}")
|
||||
|
||||
monkey = monkey_cls(monkey_args)
|
||||
monkey.initialize()
|
||||
|
||||
|
|
|
@ -30,13 +30,20 @@ from infection_monkey.network.tools import get_interface_to_target
|
|||
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError
|
||||
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
||||
from common.utils.attack_utils import ScanStatus, UsageEnum
|
||||
from common.version import get_version
|
||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||
|
||||
MAX_DEPTH_REACHED_MESSAGE = "Reached max depth, shutting down"
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PlannedShutdownException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InfectionMonkey(object):
|
||||
def __init__(self, args):
|
||||
self._keep_running = False
|
||||
|
@ -87,143 +94,158 @@ class InfectionMonkey(object):
|
|||
LOG.debug("Default server: %s is already in command servers list" % self._default_server)
|
||||
|
||||
def start(self):
|
||||
LOG.info("Monkey is running...")
|
||||
try:
|
||||
LOG.info("Monkey is starting...")
|
||||
|
||||
# Sets island's IP and port for monkey to communicate to
|
||||
if not self.set_default_server():
|
||||
return
|
||||
self.set_default_port()
|
||||
LOG.debug("Starting the setup phase.")
|
||||
# Sets island's IP and port for monkey to communicate to
|
||||
self.set_default_server()
|
||||
self.set_default_port()
|
||||
|
||||
# Create a dir for monkey files if there isn't one
|
||||
create_monkey_dir()
|
||||
# Create a dir for monkey files if there isn't one
|
||||
create_monkey_dir()
|
||||
|
||||
if WindowsUpgrader.should_upgrade():
|
||||
self._upgrading_to_64 = True
|
||||
self._singleton.unlock()
|
||||
LOG.info("32bit monkey running on 64bit Windows. Upgrading.")
|
||||
WindowsUpgrader.upgrade(self._opts)
|
||||
return
|
||||
self.upgrade_to_64_if_needed()
|
||||
|
||||
ControlClient.wakeup(parent=self._parent)
|
||||
ControlClient.load_control_config()
|
||||
ControlClient.wakeup(parent=self._parent)
|
||||
ControlClient.load_control_config()
|
||||
|
||||
if is_windows_os():
|
||||
T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send()
|
||||
if is_windows_os():
|
||||
T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send()
|
||||
|
||||
if not WormConfiguration.alive:
|
||||
LOG.info("Marked not alive from configuration")
|
||||
return
|
||||
self.shutdown_by_not_alive_config()
|
||||
|
||||
if firewall.is_enabled():
|
||||
firewall.add_firewall_rule()
|
||||
if firewall.is_enabled():
|
||||
firewall.add_firewall_rule()
|
||||
|
||||
monkey_tunnel = ControlClient.create_control_tunnel()
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.start()
|
||||
monkey_tunnel = ControlClient.create_control_tunnel()
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.start()
|
||||
|
||||
StateTelem(is_done=False).send()
|
||||
TunnelTelem().send()
|
||||
StateTelem(is_done=False, version=get_version()).send()
|
||||
TunnelTelem().send()
|
||||
|
||||
LOG.debug("Starting the post-breach phase.")
|
||||
self.collect_system_info_if_configured()
|
||||
PostBreach().execute_all_configured()
|
||||
|
||||
LOG.debug("Starting the propagation phase.")
|
||||
self.shutdown_by_max_depth_reached()
|
||||
|
||||
for iteration_index in range(WormConfiguration.max_iterations):
|
||||
ControlClient.keepalive()
|
||||
ControlClient.load_control_config()
|
||||
|
||||
self._network.initialize()
|
||||
|
||||
self._fingerprint = HostFinger.get_instances()
|
||||
|
||||
self._exploiters = HostExploiter.get_classes()
|
||||
|
||||
if not self._keep_running or not WormConfiguration.alive:
|
||||
break
|
||||
|
||||
machines = self._network.get_victim_machines(max_find=WormConfiguration.victims_max_find,
|
||||
stop_callback=ControlClient.check_for_stop)
|
||||
is_empty = True
|
||||
for machine in machines:
|
||||
if ControlClient.check_for_stop():
|
||||
break
|
||||
|
||||
is_empty = False
|
||||
for finger in self._fingerprint:
|
||||
LOG.info("Trying to get OS fingerprint from %r with module %s",
|
||||
machine, finger.__class__.__name__)
|
||||
finger.get_host_fingerprint(machine)
|
||||
|
||||
ScanTelem(machine).send()
|
||||
|
||||
# skip machines that we've already exploited
|
||||
if machine in self._exploited_machines:
|
||||
LOG.debug("Skipping %r - already exploited",
|
||||
machine)
|
||||
continue
|
||||
elif machine in self._fail_exploitation_machines:
|
||||
if WormConfiguration.retry_failed_explotation:
|
||||
LOG.debug("%r - exploitation failed before, trying again", machine)
|
||||
else:
|
||||
LOG.debug("Skipping %r - exploitation failed before", machine)
|
||||
continue
|
||||
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.set_tunnel_for_host(machine)
|
||||
if self._default_server:
|
||||
if self._network.on_island(self._default_server):
|
||||
machine.set_default_server(get_interface_to_target(machine.ip_addr) +
|
||||
(':' + self._default_server_port if self._default_server_port else ''))
|
||||
else:
|
||||
machine.set_default_server(self._default_server)
|
||||
LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server))
|
||||
|
||||
# Order exploits according to their type
|
||||
if WormConfiguration.should_exploit:
|
||||
self._exploiters = sorted(self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value)
|
||||
host_exploited = False
|
||||
for exploiter in [exploiter(machine) for exploiter in self._exploiters]:
|
||||
if self.try_exploiting(machine, exploiter):
|
||||
host_exploited = True
|
||||
VictimHostTelem('T1210', ScanStatus.USED, machine=machine).send()
|
||||
break
|
||||
if not host_exploited:
|
||||
self._fail_exploitation_machines.add(machine)
|
||||
VictimHostTelem('T1210', ScanStatus.SCANNED, machine=machine).send()
|
||||
if not self._keep_running:
|
||||
break
|
||||
|
||||
if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1):
|
||||
time_to_sleep = WormConfiguration.timeout_between_iterations
|
||||
LOG.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep)
|
||||
time.sleep(time_to_sleep)
|
||||
|
||||
if self._keep_running and WormConfiguration.alive:
|
||||
LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations)
|
||||
elif not WormConfiguration.alive:
|
||||
LOG.info("Marked not alive from configuration")
|
||||
|
||||
# if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to
|
||||
# connect to the tunnel
|
||||
if len(self._exploited_machines) > 0:
|
||||
time_to_sleep = WormConfiguration.keep_tunnel_open_time
|
||||
LOG.info("Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep)
|
||||
time.sleep(time_to_sleep)
|
||||
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.stop()
|
||||
monkey_tunnel.join()
|
||||
except PlannedShutdownException:
|
||||
LOG.info("A planned shutdown of the Monkey occurred. Logging the reason and finishing execution.")
|
||||
LOG.exception("Planned shutdown, reason:")
|
||||
|
||||
def shutdown_by_max_depth_reached(self):
|
||||
if 0 == WormConfiguration.depth:
|
||||
TraceTelem(MAX_DEPTH_REACHED_MESSAGE).send()
|
||||
raise PlannedShutdownException(MAX_DEPTH_REACHED_MESSAGE)
|
||||
else:
|
||||
LOG.debug("Running with depth: %d" % WormConfiguration.depth)
|
||||
|
||||
def collect_system_info_if_configured(self):
|
||||
if WormConfiguration.collect_system_info:
|
||||
LOG.debug("Calling system info collection")
|
||||
system_info_collector = SystemInfoCollector()
|
||||
system_info = system_info_collector.get_info()
|
||||
SystemInfoTelem(system_info).send()
|
||||
|
||||
# Executes post breach actions
|
||||
PostBreach().execute()
|
||||
def shutdown_by_not_alive_config(self):
|
||||
if not WormConfiguration.alive:
|
||||
raise PlannedShutdownException("Marked 'not alive' from configuration.")
|
||||
|
||||
if 0 == WormConfiguration.depth:
|
||||
TraceTelem("Reached max depth, shutting down").send()
|
||||
return
|
||||
else:
|
||||
LOG.debug("Running with depth: %d" % WormConfiguration.depth)
|
||||
|
||||
for iteration_index in range(WormConfiguration.max_iterations):
|
||||
ControlClient.keepalive()
|
||||
ControlClient.load_control_config()
|
||||
|
||||
self._network.initialize()
|
||||
|
||||
self._fingerprint = HostFinger.get_instances()
|
||||
|
||||
self._exploiters = HostExploiter.get_classes()
|
||||
|
||||
if not self._keep_running or not WormConfiguration.alive:
|
||||
break
|
||||
|
||||
machines = self._network.get_victim_machines(max_find=WormConfiguration.victims_max_find,
|
||||
stop_callback=ControlClient.check_for_stop)
|
||||
is_empty = True
|
||||
for machine in machines:
|
||||
if ControlClient.check_for_stop():
|
||||
break
|
||||
|
||||
is_empty = False
|
||||
for finger in self._fingerprint:
|
||||
LOG.info("Trying to get OS fingerprint from %r with module %s",
|
||||
machine, finger.__class__.__name__)
|
||||
finger.get_host_fingerprint(machine)
|
||||
|
||||
ScanTelem(machine).send()
|
||||
|
||||
# skip machines that we've already exploited
|
||||
if machine in self._exploited_machines:
|
||||
LOG.debug("Skipping %r - already exploited",
|
||||
machine)
|
||||
continue
|
||||
elif machine in self._fail_exploitation_machines:
|
||||
if WormConfiguration.retry_failed_explotation:
|
||||
LOG.debug("%r - exploitation failed before, trying again", machine)
|
||||
else:
|
||||
LOG.debug("Skipping %r - exploitation failed before", machine)
|
||||
continue
|
||||
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.set_tunnel_for_host(machine)
|
||||
if self._default_server:
|
||||
if self._network.on_island(self._default_server):
|
||||
machine.set_default_server(get_interface_to_target(machine.ip_addr) +
|
||||
(':' + self._default_server_port if self._default_server_port else ''))
|
||||
else:
|
||||
machine.set_default_server(self._default_server)
|
||||
LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server))
|
||||
|
||||
# Order exploits according to their type
|
||||
if WormConfiguration.should_exploit:
|
||||
self._exploiters = sorted(self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value)
|
||||
host_exploited = False
|
||||
for exploiter in [exploiter(machine) for exploiter in self._exploiters]:
|
||||
if self.try_exploiting(machine, exploiter):
|
||||
host_exploited = True
|
||||
VictimHostTelem('T1210', ScanStatus.USED, machine=machine).send()
|
||||
break
|
||||
if not host_exploited:
|
||||
self._fail_exploitation_machines.add(machine)
|
||||
VictimHostTelem('T1210', ScanStatus.SCANNED, machine=machine).send()
|
||||
if not self._keep_running:
|
||||
break
|
||||
|
||||
if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1):
|
||||
time_to_sleep = WormConfiguration.timeout_between_iterations
|
||||
LOG.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep)
|
||||
time.sleep(time_to_sleep)
|
||||
|
||||
if self._keep_running and WormConfiguration.alive:
|
||||
LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations)
|
||||
elif not WormConfiguration.alive:
|
||||
LOG.info("Marked not alive from configuration")
|
||||
|
||||
# if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to
|
||||
# connect to the tunnel
|
||||
if len(self._exploited_machines) > 0:
|
||||
time_to_sleep = WormConfiguration.keep_tunnel_open_time
|
||||
LOG.info("Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep)
|
||||
time.sleep(time_to_sleep)
|
||||
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.stop()
|
||||
monkey_tunnel.join()
|
||||
def upgrade_to_64_if_needed(self):
|
||||
if WindowsUpgrader.should_upgrade():
|
||||
self._upgrading_to_64 = True
|
||||
self._singleton.unlock()
|
||||
LOG.info("32bit monkey running on 64bit Windows. Upgrading.")
|
||||
WindowsUpgrader.upgrade(self._opts)
|
||||
raise PlannedShutdownException("Finished upgrading from 32bit to 64bit.")
|
||||
|
||||
def cleanup(self):
|
||||
LOG.info("Monkey cleanup started")
|
||||
|
@ -233,7 +255,7 @@ class InfectionMonkey(object):
|
|||
InfectionMonkey.close_tunnel()
|
||||
firewall.close()
|
||||
else:
|
||||
StateTelem(is_done=True).send() # Signal the server (before closing the tunnel)
|
||||
StateTelem(is_done=True, version=get_version()).send() # Signal the server (before closing the tunnel)
|
||||
InfectionMonkey.close_tunnel()
|
||||
firewall.close()
|
||||
if WormConfiguration.send_log_to_server:
|
||||
|
@ -346,9 +368,11 @@ class InfectionMonkey(object):
|
|||
self._default_server_port = ''
|
||||
|
||||
def set_default_server(self):
|
||||
"""
|
||||
Sets the default server for the Monkey to communicate back to.
|
||||
:raises PlannedShutdownException if couldn't find the server.
|
||||
"""
|
||||
if not ControlClient.find_server(default_tunnel=self._default_tunnel):
|
||||
LOG.info("Monkey couldn't find server. Going down.")
|
||||
return False
|
||||
raise PlannedShutdownException("Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel))
|
||||
self._default_server = WormConfiguration.current_server
|
||||
LOG.debug("default server set to: %s" % self._default_server)
|
||||
return True
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# -*- mode: python -*-
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
|
||||
|
||||
|
@ -18,7 +19,9 @@ def main():
|
|||
hookspath=['./pyinstaller_hooks'],
|
||||
runtime_hooks=None,
|
||||
binaries=None,
|
||||
datas=None,
|
||||
datas=[
|
||||
("../common/BUILD", "/common")
|
||||
],
|
||||
excludes=None,
|
||||
win_no_prefer_redirects=None,
|
||||
win_private_assemblies=None,
|
||||
|
@ -48,7 +51,7 @@ def is_windows():
|
|||
|
||||
|
||||
def is_32_bit():
|
||||
return platform.architecture()[0] == "32bit"
|
||||
return sys.maxsize <= 2**32
|
||||
|
||||
|
||||
def get_bin_folder():
|
||||
|
@ -93,7 +96,18 @@ def get_traceroute_binaries():
|
|||
|
||||
|
||||
def get_monkey_filename():
|
||||
return 'monkey.exe' if is_windows() else 'monkey'
|
||||
name = 'monkey-'
|
||||
if is_windows():
|
||||
name = name+"windows-"
|
||||
else:
|
||||
name = name+"linux-"
|
||||
if is_32_bit():
|
||||
name = name+"32"
|
||||
else:
|
||||
name = name+"64"
|
||||
if is_windows():
|
||||
name = name+".exe"
|
||||
return name
|
||||
|
||||
|
||||
def get_exe_strip():
|
||||
|
|
|
@ -20,7 +20,7 @@ class PostBreach(object):
|
|||
self.os_is_linux = not is_windows_os()
|
||||
self.pba_list = self.config_to_pba_list()
|
||||
|
||||
def execute(self):
|
||||
def execute_all_configured(self):
|
||||
"""
|
||||
Executes all post breach actions.
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
from PyInstaller.utils.hooks import collect_submodules, collect_data_files
|
||||
|
||||
# Import all actions as modules
|
||||
hiddenimports = collect_submodules('infection_monkey.system_info.collectors')
|
||||
# Add action files that we enumerate
|
||||
datas = (collect_data_files('infection_monkey.system_info.collectors', include_py_files=True))
|
|
@ -34,7 +34,7 @@ The monkey is composed of three separate parts.
|
|||
6. To build the final exe:
|
||||
- `cd monkey\infection_monkey`
|
||||
- `build_windows.bat`
|
||||
- `output is placed under dist\monkey.exe`
|
||||
- output is placed under `dist\monkey32.exe` or `dist\monkey64.exe` depending on your version of Python
|
||||
|
||||
## Linux
|
||||
|
||||
|
@ -55,18 +55,18 @@ Tested on Ubuntu 16.04.
|
|||
|
||||
3. Build Sambacry binaries
|
||||
- Build/Download according to sections at the end of this readme.
|
||||
- Place the binaries under [code location]\infection_monkey\bin, under the names 'sc_monkey_runner32.so', 'sc_monkey_runner64.so'
|
||||
- Place the binaries under [code location]/infection_monkey/bin, under the names 'sc_monkey_runner32.so', 'sc_monkey_runner64.so'
|
||||
|
||||
4. Build Traceroute binaries
|
||||
- Build/Download according to sections at the end of this readme.
|
||||
- Place the binaries under [code location]\infection_monkey\bin, under the names 'traceroute32', 'traceroute64'
|
||||
- Place the binaries under [code location]/infection_monkey/bin, under the names 'traceroute32', 'traceroute64'
|
||||
|
||||
5. To build, run in terminal:
|
||||
- `cd [code location]/infection_monkey`
|
||||
- `chmod +x build_linux.sh`
|
||||
- `./build_linux.sh`
|
||||
|
||||
output is placed under dist/monkey
|
||||
output is placed under `dist/monkey32` or `dist/monkey64` depending on your version of python
|
||||
|
||||
### Sambacry
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ import psutil
|
|||
from enum import IntEnum
|
||||
|
||||
from infection_monkey.network.info import get_host_subnets
|
||||
from infection_monkey.system_info.aws_collector import AwsCollector
|
||||
from infection_monkey.system_info.azure_cred_collector import AzureCollector
|
||||
from infection_monkey.system_info.netstat_collector import NetstatCollector
|
||||
from infection_monkey.system_info.system_info_collectors_handler import SystemInfoCollectorsHandler
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -61,50 +61,12 @@ class InfoCollector(object):
|
|||
self.info = {}
|
||||
|
||||
def get_info(self):
|
||||
self.get_hostname()
|
||||
self.get_process_list()
|
||||
# Collect all hardcoded
|
||||
self.get_network_info()
|
||||
self.get_azure_info()
|
||||
self.get_aws_info()
|
||||
|
||||
def get_hostname(self):
|
||||
"""
|
||||
Adds the fully qualified computer hostname to the system information.
|
||||
:return: None. Updates class information
|
||||
"""
|
||||
LOG.debug("Reading hostname")
|
||||
self.info['hostname'] = socket.getfqdn()
|
||||
|
||||
def get_process_list(self):
|
||||
"""
|
||||
Adds process information from the host to the system information.
|
||||
Currently lists process name, ID, parent ID, command line
|
||||
and the full image path of each process.
|
||||
:return: None. Updates class information
|
||||
"""
|
||||
LOG.debug("Reading process list")
|
||||
processes = {}
|
||||
for process in psutil.process_iter():
|
||||
try:
|
||||
processes[process.pid] = {"name": process.name(),
|
||||
"pid": process.pid,
|
||||
"ppid": process.ppid(),
|
||||
"cmdline": " ".join(process.cmdline()),
|
||||
"full_image_path": process.exe(),
|
||||
}
|
||||
except (psutil.AccessDenied, WindowsError):
|
||||
# we may be running as non root
|
||||
# and some processes are impossible to acquire in Windows/Linux
|
||||
# in this case we'll just add what we can
|
||||
processes[process.pid] = {"name": "null",
|
||||
"pid": process.pid,
|
||||
"ppid": process.ppid(),
|
||||
"cmdline": "ACCESS DENIED",
|
||||
"full_image_path": "null",
|
||||
}
|
||||
continue
|
||||
|
||||
self.info['process_list'] = processes
|
||||
# Collect all plugins
|
||||
SystemInfoCollectorsHandler().execute_all_configured()
|
||||
|
||||
def get_network_info(self):
|
||||
"""
|
||||
|
@ -150,11 +112,3 @@ class InfoCollector(object):
|
|||
except Exception:
|
||||
# If we failed to collect azure info, no reason to fail all the collection. Log and continue.
|
||||
LOG.error("Failed collecting Azure info.", exc_info=True)
|
||||
|
||||
def get_aws_info(self):
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
self.info['aws'] = AwsCollector().get_aws_info()
|
||||
except Exception:
|
||||
# If we failed to collect aws info, no reason to fail all the collection. Log and continue.
|
||||
LOG.error("Failed collecting AWS info.", exc_info=True)
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import logging
|
||||
|
||||
from common.cloud.aws_instance import AwsInstance
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AwsCollector(object):
|
||||
"""
|
||||
Extract info from AWS machines
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_aws_info():
|
||||
LOG.info("Collecting AWS info")
|
||||
aws = AwsInstance()
|
||||
info = {}
|
||||
if aws.is_aws_instance():
|
||||
LOG.info("Machine is an AWS instance")
|
||||
info = \
|
||||
{
|
||||
'instance_id': aws.get_instance_id()
|
||||
}
|
||||
else:
|
||||
LOG.info("Machine is NOT an AWS instance")
|
||||
|
||||
return info
|
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
This package holds all the dynamic (plugin) collectors
|
||||
"""
|
|
@ -0,0 +1,31 @@
|
|||
import logging
|
||||
|
||||
from common.cloud.aws.aws_instance import AwsInstance
|
||||
from common.data.system_info_collectors_names import AWS_COLLECTOR
|
||||
from infection_monkey.system_info.system_info_collector import SystemInfoCollector
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AwsCollector(SystemInfoCollector):
|
||||
"""
|
||||
Extract info from AWS machines.
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__(name=AWS_COLLECTOR)
|
||||
|
||||
def collect(self) -> dict:
|
||||
logger.info("Collecting AWS info")
|
||||
aws = AwsInstance()
|
||||
info = {}
|
||||
if aws.is_instance():
|
||||
logger.info("Machine is an AWS instance")
|
||||
info = \
|
||||
{
|
||||
'instance_id': aws.get_instance_id()
|
||||
}
|
||||
else:
|
||||
logger.info("Machine is NOT an AWS instance")
|
||||
|
||||
return info
|
|
@ -0,0 +1,24 @@
|
|||
from common.cloud.all_instances import get_all_cloud_instances
|
||||
from common.cloud.environment_names import Environment
|
||||
from common.data.system_info_collectors_names import ENVIRONMENT_COLLECTOR
|
||||
from infection_monkey.system_info.system_info_collector import SystemInfoCollector
|
||||
|
||||
|
||||
def get_monkey_environment() -> str:
|
||||
"""
|
||||
Get the Monkey's running environment.
|
||||
:return: One of the cloud providers if on cloud; otherwise, assumes "on premise".
|
||||
"""
|
||||
for instance in get_all_cloud_instances():
|
||||
if instance.is_instance():
|
||||
return instance.get_cloud_provider_name().value
|
||||
|
||||
return Environment.ON_PREMISE.value
|
||||
|
||||
|
||||
class EnvironmentCollector(SystemInfoCollector):
|
||||
def __init__(self):
|
||||
super().__init__(name=ENVIRONMENT_COLLECTOR)
|
||||
|
||||
def collect(self) -> dict:
|
||||
return {"environment": get_monkey_environment()}
|
|
@ -0,0 +1,16 @@
|
|||
import logging
|
||||
import socket
|
||||
|
||||
from common.data.system_info_collectors_names import HOSTNAME_COLLECTOR
|
||||
from infection_monkey.system_info.system_info_collector import SystemInfoCollector
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HostnameCollector(SystemInfoCollector):
|
||||
def __init__(self):
|
||||
super().__init__(name=HOSTNAME_COLLECTOR)
|
||||
|
||||
def collect(self) -> dict:
|
||||
return {"hostname": socket.getfqdn()}
|
|
@ -0,0 +1,50 @@
|
|||
import logging
|
||||
import psutil
|
||||
|
||||
from common.data.system_info_collectors_names import PROCESS_LIST_COLLECTOR
|
||||
from infection_monkey.system_info.system_info_collector import SystemInfoCollector
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Linux doesn't have WindowsError
|
||||
try:
|
||||
WindowsError
|
||||
except NameError:
|
||||
# noinspection PyShadowingBuiltins
|
||||
WindowsError = psutil.AccessDenied
|
||||
|
||||
|
||||
class ProcessListCollector(SystemInfoCollector):
|
||||
def __init__(self):
|
||||
super().__init__(name=PROCESS_LIST_COLLECTOR)
|
||||
|
||||
def collect(self) -> dict:
|
||||
"""
|
||||
Adds process information from the host to the system information.
|
||||
Currently lists process name, ID, parent ID, command line
|
||||
and the full image path of each process.
|
||||
"""
|
||||
logger.debug("Reading process list")
|
||||
processes = {}
|
||||
for process in psutil.process_iter():
|
||||
try:
|
||||
processes[process.pid] = {
|
||||
"name": process.name(),
|
||||
"pid": process.pid,
|
||||
"ppid": process.ppid(),
|
||||
"cmdline": " ".join(process.cmdline()),
|
||||
"full_image_path": process.exe(),
|
||||
}
|
||||
except (psutil.AccessDenied, WindowsError):
|
||||
# we may be running as non root and some processes are impossible to acquire in Windows/Linux.
|
||||
# In this case we'll just add what we know.
|
||||
processes[process.pid] = {
|
||||
"name": "null",
|
||||
"pid": process.pid,
|
||||
"ppid": process.ppid(),
|
||||
"cmdline": "ACCESS DENIED",
|
||||
"full_image_path": "null",
|
||||
}
|
||||
continue
|
||||
|
||||
return {'process_list': processes}
|
|
@ -0,0 +1,38 @@
|
|||
from infection_monkey.config import WormConfiguration
|
||||
from infection_monkey.utils.plugins.plugin import Plugin
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import infection_monkey.system_info.collectors
|
||||
|
||||
|
||||
class SystemInfoCollector(Plugin, metaclass=ABCMeta):
|
||||
"""
|
||||
ABC for system info collection. See system_info_collector_handler for more info. Basically, to implement a new system info
|
||||
collector, inherit from this class in an implementation in the infection_monkey.system_info.collectors class, and override
|
||||
the 'collect' method. Don't forget to parse your results in the Monkey Island and to add the collector to the configuration
|
||||
as well - see monkey_island.cc.services.processing.system_info_collectors for examples.
|
||||
|
||||
See the Wiki page "How to add a new System Info Collector to the Monkey?" for a detailed guide.
|
||||
"""
|
||||
def __init__(self, name="unknown"):
|
||||
self.name = name
|
||||
|
||||
@staticmethod
|
||||
def should_run(class_name) -> bool:
|
||||
return class_name in WormConfiguration.system_info_collectors_classes
|
||||
|
||||
@staticmethod
|
||||
def base_package_file():
|
||||
return infection_monkey.system_info.collectors.__file__
|
||||
|
||||
@staticmethod
|
||||
def base_package_name():
|
||||
return infection_monkey.system_info.collectors.__package__
|
||||
|
||||
@abstractmethod
|
||||
def collect(self) -> dict:
|
||||
"""
|
||||
Collect the relevant information and return it in a dictionary.
|
||||
To be implemented by each collector.
|
||||
"""
|
||||
raise NotImplementedError()
|
|
@ -0,0 +1,33 @@
|
|||
import logging
|
||||
from typing import Sequence
|
||||
|
||||
from infection_monkey.system_info.system_info_collector import SystemInfoCollector
|
||||
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SystemInfoCollectorsHandler(object):
|
||||
def __init__(self):
|
||||
self.collectors_list = self.config_to_collectors_list()
|
||||
|
||||
def execute_all_configured(self):
|
||||
successful_collections = 0
|
||||
system_info_telemetry = {}
|
||||
for collector in self.collectors_list:
|
||||
try:
|
||||
LOG.debug("Executing system info collector: '{}'".format(collector.name))
|
||||
collected_info = collector.collect()
|
||||
system_info_telemetry[collector.name] = collected_info
|
||||
successful_collections += 1
|
||||
except Exception as e:
|
||||
# If we failed one collector, no need to stop execution. Log and continue.
|
||||
LOG.error("Collector {} failed. Error info: {}".format(collector.name, e))
|
||||
LOG.info("All system info collectors executed. Total {} executed, out of which {} collected successfully.".
|
||||
format(len(self.collectors_list), successful_collections))
|
||||
|
||||
SystemInfoTelem({"collectors": system_info_telemetry}).send()
|
||||
|
||||
@staticmethod
|
||||
def config_to_collectors_list() -> Sequence[SystemInfoCollector]:
|
||||
return SystemInfoCollector.get_instances()
|
|
@ -5,15 +5,19 @@ __author__ = "itay.mizeretz"
|
|||
|
||||
class StateTelem(BaseTelem):
|
||||
|
||||
def __init__(self, is_done):
|
||||
def __init__(self, is_done, version="Unknown"):
|
||||
"""
|
||||
Default state telemetry constructor
|
||||
:param is_done: Whether the state of monkey is done.
|
||||
"""
|
||||
super(StateTelem, self).__init__()
|
||||
self.is_done = is_done
|
||||
self.version = version
|
||||
|
||||
telem_category = 'state'
|
||||
|
||||
def get_data(self):
|
||||
return {'done': self.is_done}
|
||||
return {
|
||||
'done': self.is_done,
|
||||
'version': self.version
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ class Environment(object, metaclass=ABCMeta):
|
|||
def testing(self, value):
|
||||
self._testing = value
|
||||
|
||||
_MONKEY_VERSION = "1.7.0"
|
||||
|
||||
def __init__(self):
|
||||
self.config = None
|
||||
self._testing = False # Assume env is not for unit testing.
|
||||
|
@ -58,9 +56,6 @@ class Environment(object, metaclass=ABCMeta):
|
|||
def is_develop(self):
|
||||
return self.get_deployment() == 'develop'
|
||||
|
||||
def get_version(self):
|
||||
return self._MONKEY_VERSION + ('-dev' if self.is_develop() else '')
|
||||
|
||||
def _get_from_config(self, key, default_value=None):
|
||||
val = default_value
|
||||
if self.config is not None:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import monkey_island.cc.auth
|
||||
from monkey_island.cc.environment import Environment
|
||||
from common.cloud.aws_instance import AwsInstance
|
||||
from common.cloud.aws.aws_instance import AwsInstance
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ from monkey_island.cc.utils import local_ip_addresses
|
|||
from monkey_island.cc.environment.environment import env
|
||||
from monkey_island.cc.database import is_db_server_up, get_db_version
|
||||
from monkey_island.cc.resources.monkey_download import MonkeyDownload
|
||||
from common.version import get_version
|
||||
from monkey_island.cc.bootloader_server import BootloaderHttpServer
|
||||
|
||||
|
||||
|
@ -66,8 +67,9 @@ def start_island_server():
|
|||
|
||||
|
||||
def log_init_info():
|
||||
logger.info(
|
||||
'Monkey Island Server is running. Listening on the following URLs: {}'.format(
|
||||
logger.info('Monkey Island Server is running!')
|
||||
logger.info(f"version: {get_version()}")
|
||||
logger.info('Listening on the following URLs: {}'.format(
|
||||
", ".join(["https://{}:{}".format(x, env.get_island_port()) for x in local_ip_addresses()])
|
||||
)
|
||||
)
|
||||
|
|
|
@ -9,6 +9,7 @@ from monkey_island.cc.models.monkey_ttl import MonkeyTtl, create_monkey_ttl_docu
|
|||
from monkey_island.cc.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS
|
||||
from monkey_island.cc.models.command_control_channel import CommandControlChannel
|
||||
from monkey_island.cc.utils import local_ip_addresses
|
||||
from common.cloud import environment_names
|
||||
|
||||
MAX_MONKEYS_AMOUNT_TO_CACHE = 100
|
||||
|
||||
|
@ -42,6 +43,10 @@ class Monkey(Document):
|
|||
ttl_ref = ReferenceField(MonkeyTtl)
|
||||
tunnel = ReferenceField("self")
|
||||
command_control_channel = EmbeddedDocumentField(CommandControlChannel)
|
||||
|
||||
# Environment related fields
|
||||
environment = StringField(default=environment_names.Environment.UNKNOWN.value,
|
||||
choices=environment_names.ALL_ENVIRONMENTS_NAMES)
|
||||
aws_instance_id = StringField(required=False) # This field only exists when the monkey is running on an AWS
|
||||
|
||||
# instance. See https://github.com/guardicore/monkey/issues/426.
|
||||
|
@ -55,7 +60,8 @@ class Monkey(Document):
|
|||
raise MonkeyNotFoundError("info: {0} | id: {1}".format(ex, str(db_id)))
|
||||
|
||||
@staticmethod
|
||||
def get_single_monkey_by_guid(monkey_guid):
|
||||
# See https://www.python.org/dev/peps/pep-0484/#forward-references
|
||||
def get_single_monkey_by_guid(monkey_guid) -> 'Monkey':
|
||||
try:
|
||||
return Monkey.objects.get(guid=monkey_guid)
|
||||
except DoesNotExist as ex:
|
||||
|
|
|
@ -6,7 +6,7 @@ import flask_restful
|
|||
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
|
||||
from common.cloud.aws_service import AwsService
|
||||
from common.cloud.aws.aws_service import AwsService
|
||||
|
||||
CLIENT_ERROR_FORMAT = "ClientError, error message: '{}'. Probably, the IAM role that has been associated with the " \
|
||||
"instance doesn't permit SSM calls. "
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import flask_restful
|
||||
import logging
|
||||
|
||||
from monkey_island.cc.environment.environment import env
|
||||
from common.version import get_version
|
||||
from monkey_island.cc.services.version_update import VersionUpdateService
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
@ -17,7 +17,7 @@ class VersionUpdate(flask_restful.Resource):
|
|||
# even when not authenticated
|
||||
def get(self):
|
||||
return {
|
||||
'current_version': env.get_version(),
|
||||
'current_version': get_version(),
|
||||
'newer_version': VersionUpdateService.get_newer_version(),
|
||||
'download_link': VersionUpdateService.get_download_link()
|
||||
}
|
||||
|
|
|
@ -153,9 +153,18 @@ class ConfigService:
|
|||
def ssh_key_exists(keys, user, ip):
|
||||
return [key for key in keys if key['user'] == user and key['ip'] == ip]
|
||||
|
||||
def _filter_none_values(data):
|
||||
if isinstance(data, dict):
|
||||
return {k: ConfigService._filter_none_values(v) for k, v in data.items() if k is not None and v is not None}
|
||||
elif isinstance(data, list):
|
||||
return [ConfigService._filter_none_values(item) for item in data if item is not None]
|
||||
else:
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def update_config(config_json, should_encrypt):
|
||||
# PBA file upload happens on pba_file_upload endpoint and corresponding config options are set there
|
||||
config_json = ConfigService._filter_none_values(config_json)
|
||||
monkey_island.cc.services.post_breach_files.set_config_PBA_files(config_json)
|
||||
if should_encrypt:
|
||||
try:
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
from common.data.system_info_collectors_names \
|
||||
import AWS_COLLECTOR, ENVIRONMENT_COLLECTOR, HOSTNAME_COLLECTOR, PROCESS_LIST_COLLECTOR
|
||||
|
||||
WARNING_SIGN = " \u26A0"
|
||||
|
||||
SCHEMA = {
|
||||
|
@ -99,6 +102,44 @@ SCHEMA = {
|
|||
}
|
||||
]
|
||||
},
|
||||
"system_info_collectors_classes": {
|
||||
"title": "System Information Collectors",
|
||||
"type": "string",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
ENVIRONMENT_COLLECTOR
|
||||
],
|
||||
"title": "Collect which environment this machine is on (on prem/cloud)",
|
||||
"attack_techniques": []
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
AWS_COLLECTOR
|
||||
],
|
||||
"title": "If on AWS, collect more information about the instance",
|
||||
"attack_techniques": []
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
HOSTNAME_COLLECTOR
|
||||
],
|
||||
"title": "Collect the machine's hostname",
|
||||
"attack_techniques": []
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
PROCESS_LIST_COLLECTOR
|
||||
],
|
||||
"title": "Collect running processes on the machine",
|
||||
"attack_techniques": []
|
||||
},
|
||||
],
|
||||
},
|
||||
"post_breach_acts": {
|
||||
"title": "Post breach actions",
|
||||
"type": "string",
|
||||
|
@ -433,6 +474,21 @@ SCHEMA = {
|
|||
"attack_techniques": ["T1003"],
|
||||
"description": "Determines whether to use Mimikatz"
|
||||
},
|
||||
"system_info_collectors_classes": {
|
||||
"title": "System info collectors",
|
||||
"type": "array",
|
||||
"uniqueItems": True,
|
||||
"items": {
|
||||
"$ref": "#/definitions/system_info_collectors_classes"
|
||||
},
|
||||
"default": [
|
||||
ENVIRONMENT_COLLECTOR,
|
||||
AWS_COLLECTOR,
|
||||
HOSTNAME_COLLECTOR,
|
||||
PROCESS_LIST_COLLECTOR
|
||||
],
|
||||
"description": "Determines which system information collectors will collect information."
|
||||
},
|
||||
}
|
||||
},
|
||||
"life_cycle": {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
|
||||
from common.cloud.aws_instance import AwsInstance
|
||||
from common.cloud.aws_service import AwsService
|
||||
from common.cloud.aws.aws_instance import AwsInstance
|
||||
from common.cloud.aws.aws_service import AwsService
|
||||
from common.cmd.aws.aws_cmd_runner import AwsCmdRunner
|
||||
from common.cmd.cmd import Cmd
|
||||
from common.cmd.cmd_runner import CmdRunner
|
||||
|
@ -54,7 +54,7 @@ class RemoteRunAwsService:
|
|||
|
||||
@staticmethod
|
||||
def is_running_on_aws():
|
||||
return RemoteRunAwsService.aws_instance.is_aws_instance()
|
||||
return RemoteRunAwsService.aws_instance.is_instance()
|
||||
|
||||
@staticmethod
|
||||
def update_aws_region_authless():
|
||||
|
@ -130,7 +130,8 @@ class RemoteRunAwsService:
|
|||
return r"[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {" \
|
||||
r"$true}; (New-Object System.Net.WebClient).DownloadFile('https://" + island_ip + \
|
||||
r":5000/api/monkey/download/monkey-windows-" + bit_text + r".exe','.\\monkey.exe'); " \
|
||||
r";Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s " + island_ip + r":5000'; "
|
||||
r";Start-Process -FilePath '.\\monkey.exe' " \
|
||||
r"-ArgumentList 'm0nk3y -s " + island_ip + r":5000'; "
|
||||
|
||||
@staticmethod
|
||||
def _get_run_monkey_cmd_line(is_linux, is_64bit, island_ip):
|
||||
|
|
|
@ -5,7 +5,7 @@ from datetime import datetime
|
|||
import boto3
|
||||
from botocore.exceptions import UnknownServiceError
|
||||
|
||||
from common.cloud.aws_instance import AwsInstance
|
||||
from common.cloud.aws.aws_instance import AwsInstance
|
||||
from monkey_island.cc.environment.environment import load_server_configuration_from_file
|
||||
from monkey_island.cc.services.reporting.exporter import Exporter
|
||||
|
||||
|
|
|
@ -386,10 +386,11 @@ class ReportService:
|
|||
@staticmethod
|
||||
def get_monkey_subnets(monkey_guid):
|
||||
network_info = mongo.db.telemetry.find_one(
|
||||
{'telem_category': 'system_info', 'monkey_guid': monkey_guid},
|
||||
{'telem_category': 'system_info',
|
||||
'monkey_guid': monkey_guid},
|
||||
{'data.network_info.networks': 1}
|
||||
)
|
||||
if network_info is None:
|
||||
if network_info is None or not network_info["data"]:
|
||||
return []
|
||||
|
||||
return \
|
||||
|
|
|
@ -15,28 +15,34 @@ __regular_report_generating_lock = threading.Semaphore()
|
|||
def safe_generate_reports():
|
||||
# Entering the critical section; Wait until report generation is available.
|
||||
__report_generating_lock.acquire()
|
||||
report = safe_generate_regular_report()
|
||||
attack_report = safe_generate_attack_report()
|
||||
# Leaving the critical section.
|
||||
__report_generating_lock.release()
|
||||
try:
|
||||
report = safe_generate_regular_report()
|
||||
attack_report = safe_generate_attack_report()
|
||||
finally:
|
||||
# Leaving the critical section.
|
||||
__report_generating_lock.release()
|
||||
return report, attack_report
|
||||
|
||||
|
||||
def safe_generate_regular_report():
|
||||
# Local import to avoid circular imports
|
||||
from monkey_island.cc.services.reporting.report import ReportService
|
||||
__regular_report_generating_lock.acquire()
|
||||
report = ReportService.generate_report()
|
||||
__regular_report_generating_lock.release()
|
||||
try:
|
||||
__regular_report_generating_lock.acquire()
|
||||
report = ReportService.generate_report()
|
||||
finally:
|
||||
__regular_report_generating_lock.release()
|
||||
return report
|
||||
|
||||
|
||||
def safe_generate_attack_report():
|
||||
# Local import to avoid circular imports
|
||||
from monkey_island.cc.services.attack.attack_report import AttackReportService
|
||||
__attack_report_generating_lock.acquire()
|
||||
attack_report = AttackReportService.generate_new_report()
|
||||
__attack_report_generating_lock.release()
|
||||
try:
|
||||
__attack_report_generating_lock.acquire()
|
||||
attack_report = AttackReportService.generate_new_report()
|
||||
finally:
|
||||
__attack_report_generating_lock.release()
|
||||
return attack_report
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
import logging
|
||||
|
||||
from monkey_island.cc.models import Monkey
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import \
|
||||
test_passed_findings_for_unreached_segments
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def process_state_telemetry(telemetry_json):
|
||||
monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
|
||||
NodeService.add_communication_info(monkey, telemetry_json['command_control_channel'])
|
||||
|
@ -15,3 +20,6 @@ def process_state_telemetry(telemetry_json):
|
|||
if telemetry_json['data']['done']:
|
||||
current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
|
||||
test_passed_findings_for_unreached_segments(current_monkey)
|
||||
|
||||
if telemetry_json['data']['version']:
|
||||
logger.info(f"monkey {telemetry_json['monkey_guid']} has version {telemetry_json['data']['version']}")
|
||||
|
|
|
@ -1,25 +1,23 @@
|
|||
import logging
|
||||
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.models import Monkey
|
||||
from monkey_island.cc.services import mimikatz_utils
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence
|
||||
from monkey_island.cc.services.wmi_handler import WMIHandler
|
||||
from monkey_island.cc.encryptor import encryptor
|
||||
from monkey_island.cc.services import mimikatz_utils
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \
|
||||
SystemInfoTelemetryDispatcher
|
||||
from monkey_island.cc.services.wmi_handler import WMIHandler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def process_system_info_telemetry(telemetry_json):
|
||||
dispatcher = SystemInfoTelemetryDispatcher()
|
||||
telemetry_processing_stages = [
|
||||
process_ssh_info,
|
||||
process_credential_info,
|
||||
process_mimikatz_and_wmi_info,
|
||||
process_aws_data,
|
||||
update_db_with_new_hostname,
|
||||
test_antivirus_existence,
|
||||
dispatcher.dispatch_collector_results_to_relevant_processors
|
||||
]
|
||||
|
||||
# Calling safe_process_telemetry so if one of the stages fail, we log and move on instead of failing the rest of
|
||||
|
@ -34,7 +32,7 @@ def safe_process_telemetry(processing_function, telemetry_json):
|
|||
processing_function(telemetry_json)
|
||||
except Exception as err:
|
||||
logger.error(
|
||||
"Error {} while in {} stage of processing telemetry.".format(str(err), processing_function.func_name),
|
||||
"Error {} while in {} stage of processing telemetry.".format(str(err), processing_function.__name__),
|
||||
exc_info=True)
|
||||
|
||||
|
||||
|
@ -104,14 +102,3 @@ def process_mimikatz_and_wmi_info(telemetry_json):
|
|||
wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)
|
||||
wmi_handler.process_and_handle_wmi_info()
|
||||
|
||||
|
||||
def process_aws_data(telemetry_json):
|
||||
if 'aws' in telemetry_json['data']:
|
||||
if 'instance_id' in telemetry_json['data']['aws']:
|
||||
monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id')
|
||||
mongo.db.monkey.update_one({'_id': monkey_id},
|
||||
{'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}})
|
||||
|
||||
|
||||
def update_db_with_new_hostname(telemetry_json):
|
||||
Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']).set_hostname(telemetry_json['data']['hostname'])
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import logging
|
||||
|
||||
from monkey_island.cc.models.monkey import Monkey
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def process_aws_telemetry(collector_results, monkey_guid):
|
||||
relevant_monkey = Monkey.get_single_monkey_by_guid(monkey_guid)
|
||||
|
||||
if "instance_id" in collector_results:
|
||||
instance_id = collector_results["instance_id"]
|
||||
relevant_monkey.aws_instance_id = instance_id
|
||||
relevant_monkey.save()
|
||||
logger.debug("Updated Monkey {} with aws instance id {}".format(str(relevant_monkey), instance_id))
|
|
@ -0,0 +1,12 @@
|
|||
import logging
|
||||
|
||||
from monkey_island.cc.models.monkey import Monkey
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def process_environment_telemetry(collector_results, monkey_guid):
|
||||
relevant_monkey = Monkey.get_single_monkey_by_guid(monkey_guid)
|
||||
relevant_monkey.environment = collector_results["environment"]
|
||||
relevant_monkey.save()
|
||||
logger.debug("Updated Monkey {} with env {}".format(str(relevant_monkey), collector_results))
|
|
@ -0,0 +1,9 @@
|
|||
import logging
|
||||
|
||||
from monkey_island.cc.models.monkey import Monkey
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def process_hostname_telemetry(collector_results, monkey_guid):
|
||||
Monkey.get_single_monkey_by_guid(monkey_guid).set_hostname(collector_results["hostname"])
|
|
@ -0,0 +1,64 @@
|
|||
import logging
|
||||
import typing
|
||||
|
||||
from common.data.system_info_collectors_names \
|
||||
import AWS_COLLECTOR, ENVIRONMENT_COLLECTOR, HOSTNAME_COLLECTOR, PROCESS_LIST_COLLECTOR
|
||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.aws import process_aws_telemetry
|
||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.environment import process_environment_telemetry
|
||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.hostname import process_hostname_telemetry
|
||||
from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS = {
|
||||
AWS_COLLECTOR: [process_aws_telemetry],
|
||||
ENVIRONMENT_COLLECTOR: [process_environment_telemetry],
|
||||
HOSTNAME_COLLECTOR: [process_hostname_telemetry],
|
||||
PROCESS_LIST_COLLECTOR: [test_antivirus_existence]
|
||||
}
|
||||
|
||||
|
||||
class SystemInfoTelemetryDispatcher(object):
|
||||
def __init__(self, collector_to_parsing_functions: typing.Mapping[str, typing.List[typing.Callable]] = None):
|
||||
"""
|
||||
:param collector_to_parsing_functions: Map between collector names and a list of functions that process the output of
|
||||
that collector. If `None` is supplied, uses the default one; This should be the normal flow, overriding the
|
||||
collector->functions mapping is useful mostly for testing.
|
||||
"""
|
||||
if collector_to_parsing_functions is None:
|
||||
collector_to_parsing_functions = SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS
|
||||
self.collector_to_processing_functions = collector_to_parsing_functions
|
||||
|
||||
def dispatch_collector_results_to_relevant_processors(self, telemetry_json):
|
||||
"""
|
||||
If the telemetry has collectors' results, dispatches the results to the relevant processing functions.
|
||||
:param telemetry_json: Telemetry sent from the Monkey
|
||||
"""
|
||||
if "collectors" in telemetry_json["data"]:
|
||||
self.dispatch_single_result_to_relevant_processor(telemetry_json)
|
||||
|
||||
def dispatch_single_result_to_relevant_processor(self, telemetry_json):
|
||||
relevant_monkey_guid = telemetry_json['monkey_guid']
|
||||
|
||||
for collector_name, collector_results in telemetry_json["data"]["collectors"].items():
|
||||
self.dispatch_result_of_single_collector_to_processing_functions(
|
||||
collector_name,
|
||||
collector_results,
|
||||
relevant_monkey_guid)
|
||||
|
||||
def dispatch_result_of_single_collector_to_processing_functions(
|
||||
self,
|
||||
collector_name,
|
||||
collector_results,
|
||||
relevant_monkey_guid):
|
||||
if collector_name in self.collector_to_processing_functions:
|
||||
for processing_function in self.collector_to_processing_functions[collector_name]:
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
processing_function(collector_results, relevant_monkey_guid)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Error {} while processing {} system info telemetry".format(str(e), collector_name),
|
||||
exc_info=True)
|
||||
else:
|
||||
logger.warning("Unknown system info collector name: {}".format(collector_name))
|
|
@ -0,0 +1,31 @@
|
|||
import uuid
|
||||
|
||||
from monkey_island.cc.models import Monkey
|
||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \
|
||||
SystemInfoTelemetryDispatcher
|
||||
from monkey_island.cc.testing.IslandTestCase import IslandTestCase
|
||||
|
||||
|
||||
class TestEnvironmentTelemetryProcessing(IslandTestCase):
|
||||
def test_process_environment_telemetry(self):
|
||||
self.fail_if_not_testing_env()
|
||||
self.clean_monkey_db()
|
||||
|
||||
# Arrange
|
||||
monkey_guid = str(uuid.uuid4())
|
||||
a_monkey = Monkey(guid=monkey_guid)
|
||||
a_monkey.save()
|
||||
dispatcher = SystemInfoTelemetryDispatcher()
|
||||
|
||||
on_premise = "On Premise"
|
||||
telem_json = {
|
||||
"data": {
|
||||
"collectors": {
|
||||
"EnvironmentCollector": {"environment": on_premise},
|
||||
}
|
||||
},
|
||||
"monkey_guid": monkey_guid
|
||||
}
|
||||
dispatcher.dispatch_collector_results_to_relevant_processors(telem_json)
|
||||
|
||||
self.assertEqual(Monkey.get_single_monkey_by_guid(monkey_guid).environment, on_premise)
|
|
@ -0,0 +1,57 @@
|
|||
import uuid
|
||||
|
||||
from monkey_island.cc.models import Monkey
|
||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \
|
||||
SystemInfoTelemetryDispatcher
|
||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \
|
||||
process_aws_telemetry
|
||||
from monkey_island.cc.testing.IslandTestCase import IslandTestCase
|
||||
|
||||
TEST_SYS_INFO_TO_PROCESSING = {
|
||||
"AwsCollector": [process_aws_telemetry],
|
||||
}
|
||||
|
||||
|
||||
class SystemInfoTelemetryDispatcherTest(IslandTestCase):
|
||||
def test_dispatch_to_relevant_collector_bad_inputs(self):
|
||||
self.fail_if_not_testing_env()
|
||||
|
||||
dispatcher = SystemInfoTelemetryDispatcher(TEST_SYS_INFO_TO_PROCESSING)
|
||||
|
||||
# Bad format telem JSONs - throws
|
||||
bad_empty_telem_json = {}
|
||||
self.assertRaises(KeyError, dispatcher.dispatch_collector_results_to_relevant_processors, bad_empty_telem_json)
|
||||
bad_no_data_telem_json = {"monkey_guid": "bla"}
|
||||
self.assertRaises(KeyError, dispatcher.dispatch_collector_results_to_relevant_processors, bad_no_data_telem_json)
|
||||
bad_no_monkey_telem_json = {"data": {"collectors": {"AwsCollector": "Bla"}}}
|
||||
self.assertRaises(KeyError, dispatcher.dispatch_collector_results_to_relevant_processors, bad_no_monkey_telem_json)
|
||||
|
||||
# Telem JSON with no collectors - nothing gets dispatched
|
||||
good_telem_no_collectors = {"monkey_guid": "bla", "data": {"bla": "bla"}}
|
||||
good_telem_empty_collectors = {"monkey_guid": "bla", "data": {"bla": "bla", "collectors": {}}}
|
||||
|
||||
dispatcher.dispatch_collector_results_to_relevant_processors(good_telem_no_collectors)
|
||||
dispatcher.dispatch_collector_results_to_relevant_processors(good_telem_empty_collectors)
|
||||
|
||||
def test_dispatch_to_relevant_collector(self):
|
||||
self.fail_if_not_testing_env()
|
||||
self.clean_monkey_db()
|
||||
|
||||
a_monkey = Monkey(guid=str(uuid.uuid4()))
|
||||
a_monkey.save()
|
||||
|
||||
dispatcher = SystemInfoTelemetryDispatcher()
|
||||
|
||||
# JSON with results - make sure functions are called
|
||||
instance_id = "i-0bd2c14bd4c7d703f"
|
||||
telem_json = {
|
||||
"data": {
|
||||
"collectors": {
|
||||
"AwsCollector": {"instance_id": instance_id},
|
||||
}
|
||||
},
|
||||
"monkey_guid": a_monkey.guid
|
||||
}
|
||||
dispatcher.dispatch_collector_results_to_relevant_processors(telem_json)
|
||||
|
||||
self.assertEquals(Monkey.get_single_monkey_by_guid(a_monkey.guid).aws_instance_id, instance_id)
|
|
@ -7,36 +7,36 @@ from monkey_island.cc.models.zero_trust.event import Event
|
|||
from monkey_island.cc.services.telemetry.zero_trust_tests.known_anti_viruses import ANTI_VIRUS_KNOWN_PROCESS_NAMES
|
||||
|
||||
|
||||
def test_antivirus_existence(telemetry_json):
|
||||
current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
|
||||
if 'process_list' in telemetry_json['data']:
|
||||
process_list_event = Event.create_event(
|
||||
title="Process list",
|
||||
message="Monkey on {} scanned the process list".format(current_monkey.hostname),
|
||||
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL)
|
||||
events = [process_list_event]
|
||||
def test_antivirus_existence(process_list_json, monkey_guid):
|
||||
current_monkey = Monkey.get_single_monkey_by_guid(monkey_guid)
|
||||
|
||||
av_processes = filter_av_processes(telemetry_json)
|
||||
process_list_event = Event.create_event(
|
||||
title="Process list",
|
||||
message="Monkey on {} scanned the process list".format(current_monkey.hostname),
|
||||
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL)
|
||||
events = [process_list_event]
|
||||
|
||||
for process in av_processes:
|
||||
events.append(Event.create_event(
|
||||
title="Found AV process",
|
||||
message="The process '{}' was recognized as an Anti Virus process. Process "
|
||||
"details: {}".format(process[1]['name'], json.dumps(process[1])),
|
||||
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL
|
||||
))
|
||||
av_processes = filter_av_processes(process_list_json["process_list"])
|
||||
|
||||
if len(av_processes) > 0:
|
||||
test_status = zero_trust_consts.STATUS_PASSED
|
||||
else:
|
||||
test_status = zero_trust_consts.STATUS_FAILED
|
||||
AggregateFinding.create_or_add_to_existing(
|
||||
test=zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events
|
||||
)
|
||||
for process in av_processes:
|
||||
events.append(Event.create_event(
|
||||
title="Found AV process",
|
||||
message="The process '{}' was recognized as an Anti Virus process. Process "
|
||||
"details: {}".format(process[1]['name'], json.dumps(process[1])),
|
||||
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL
|
||||
))
|
||||
|
||||
if len(av_processes) > 0:
|
||||
test_status = zero_trust_consts.STATUS_PASSED
|
||||
else:
|
||||
test_status = zero_trust_consts.STATUS_FAILED
|
||||
AggregateFinding.create_or_add_to_existing(
|
||||
test=zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events
|
||||
)
|
||||
|
||||
|
||||
def filter_av_processes(telemetry_json):
|
||||
all_processes = list(telemetry_json['data']['process_list'].items())
|
||||
def filter_av_processes(process_list):
|
||||
all_processes = list(process_list.items())
|
||||
av_processes = []
|
||||
for process in all_processes:
|
||||
process_name = process[1]['name']
|
||||
|
|
|
@ -2,6 +2,7 @@ import logging
|
|||
|
||||
import requests
|
||||
|
||||
from common.version import get_version
|
||||
from monkey_island.cc.environment.environment import env
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
@ -39,7 +40,7 @@ class VersionUpdateService:
|
|||
Checks if newer monkey version is available
|
||||
:return: False if not, version in string format ('1.6.2') otherwise
|
||||
"""
|
||||
url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % (env.get_deployment(), env.get_version())
|
||||
url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % (env.get_deployment(), get_version())
|
||||
|
||||
reply = requests.get(url, timeout=15)
|
||||
|
||||
|
@ -53,4 +54,4 @@ class VersionUpdateService:
|
|||
|
||||
@staticmethod
|
||||
def get_download_link():
|
||||
return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % (env.get_deployment(), env.get_version())
|
||||
return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % (env.get_deployment(), get_version())
|
||||
|
|
|
@ -30,7 +30,7 @@ let infectionMonkeyImage = require('../images/infection-monkey.svg');
|
|||
let guardicoreLogoImage = require('../images/guardicore-logo.png');
|
||||
let notificationIcon = require('../images/notification-logo-512x512.png');
|
||||
|
||||
const reportZeroTrustRoute = '/report/zero_trust';
|
||||
const reportZeroTrustRoute = '/report/zeroTrust';
|
||||
|
||||
class AppComponent extends AuthComponent {
|
||||
updateStatus = () => {
|
||||
|
|
|
@ -30,7 +30,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
lastAction: 'none',
|
||||
sections: [],
|
||||
selectedSection: 'attack',
|
||||
allMonkeysAreDead: true,
|
||||
monkeysRan: false,
|
||||
PBAwinFile: [],
|
||||
PBAlinuxFile: [],
|
||||
showAttackAlert: false
|
||||
|
@ -363,13 +363,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
this.authFetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
// This check is used to prevent unnecessary re-rendering
|
||||
let allMonkeysAreDead = (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']);
|
||||
if (allMonkeysAreDead !== this.state.allMonkeysAreDead) {
|
||||
this.setState({
|
||||
allMonkeysAreDead: allMonkeysAreDead
|
||||
});
|
||||
}
|
||||
this.setState({monkeysRan: res['completed_steps']['run_monkey']});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -470,15 +464,15 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
</div>)
|
||||
};
|
||||
|
||||
renderRunningMonkeysWarning = () => {
|
||||
renderConfigWontChangeWarning = () => {
|
||||
return (<div>
|
||||
{this.state.allMonkeysAreDead ?
|
||||
'' :
|
||||
{this.state.monkeysRan ?
|
||||
<div className="alert alert-warning">
|
||||
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
|
||||
Some monkeys are currently running. Note that changing the configuration will only apply to new
|
||||
infections.
|
||||
Changed configuration will only apply to new infections.
|
||||
"Start over" to run again with different configuration.
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
</div>)
|
||||
};
|
||||
|
@ -520,7 +514,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
{this.renderAttackAlertModal()}
|
||||
<h1 className="page-title">Monkey Configuration</h1>
|
||||
{this.renderNav()}
|
||||
{this.renderRunningMonkeysWarning()}
|
||||
{this.renderConfigWontChangeWarning()}
|
||||
{content}
|
||||
<div className="text-center">
|
||||
<button type="submit" onClick={this.onSubmit} className="btn btn-success btn-lg" style={{margin: '5px'}}>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import {Col, Modal} from 'react-bootstrap';
|
||||
import {Col} from 'react-bootstrap';
|
||||
import {Link} from 'react-router-dom';
|
||||
import AuthComponent from '../AuthComponent';
|
||||
import StartOverModal from '../ui-components/StartOverModal';
|
||||
import '../../styles/StartOverPage.scss';
|
||||
|
||||
class StartOverPageComponent extends AuthComponent {
|
||||
constructor(props) {
|
||||
|
@ -12,6 +14,9 @@ class StartOverPageComponent extends AuthComponent {
|
|||
showCleanDialog: false,
|
||||
allMonkeysAreDead: false
|
||||
};
|
||||
|
||||
this.cleanup = this.cleanup.bind(this);
|
||||
this.closeModal = this.closeModal.bind(this);
|
||||
}
|
||||
|
||||
updateMonkeysRunning = () => {
|
||||
|
@ -25,48 +30,14 @@ class StartOverPageComponent extends AuthComponent {
|
|||
});
|
||||
};
|
||||
|
||||
renderCleanDialogModal = () => {
|
||||
return (
|
||||
<Modal show={this.state.showCleanDialog} onHide={() => this.setState({showCleanDialog: false})}>
|
||||
<Modal.Body>
|
||||
<h2>
|
||||
<div className="text-center">Reset environment</div>
|
||||
</h2>
|
||||
<p style={{'fontSize': '1.2em', 'marginBottom': '2em'}}>
|
||||
Are you sure you want to reset the environment?
|
||||
</p>
|
||||
{
|
||||
!this.state.allMonkeysAreDead ?
|
||||
<div className="alert alert-warning">
|
||||
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
|
||||
Some monkeys are still running. It's advised to kill all monkeys before resetting.
|
||||
</div>
|
||||
:
|
||||
<div/>
|
||||
}
|
||||
<div className="text-center">
|
||||
<button type="button" className="btn btn-danger btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => {
|
||||
this.cleanup();
|
||||
this.setState({showCleanDialog: false});
|
||||
}}>
|
||||
Reset environment
|
||||
</button>
|
||||
<button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => this.setState({showCleanDialog: false})}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={12} lg={8}>
|
||||
{this.renderCleanDialogModal()}
|
||||
<StartOverModal cleaned = {this.state.cleaned}
|
||||
showCleanDialog = {this.state.showCleanDialog}
|
||||
allMonkeysAreDead = {this.state.allMonkeysAreDead}
|
||||
onVerify = {this.cleanup}
|
||||
onClose = {this.closeModal}/>
|
||||
<h1 className="page-title">Start Over</h1>
|
||||
<div style={{'fontSize': '1.2em'}}>
|
||||
<p>
|
||||
|
@ -104,7 +75,7 @@ class StartOverPageComponent extends AuthComponent {
|
|||
this.setState({
|
||||
cleaned: false
|
||||
});
|
||||
this.authFetch('/api?action=reset')
|
||||
return this.authFetch('/api?action=reset')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res['status'] === 'OK') {
|
||||
|
@ -112,8 +83,14 @@ class StartOverPageComponent extends AuthComponent {
|
|||
cleaned: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(this.updateMonkeysRunning());
|
||||
};
|
||||
|
||||
closeModal = () => {
|
||||
this.setState({
|
||||
showCleanDialog: false
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
export default StartOverPageComponent;
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
import {Modal} from 'react-bootstrap';
|
||||
import React from 'react';
|
||||
import {GridLoader} from 'react-spinners';
|
||||
|
||||
|
||||
class StartOverModal extends React.PureComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showCleanDialog: this.props.showCleanDialog,
|
||||
allMonkeysAreDead: this.props.allMonkeysAreDead,
|
||||
loading: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props !== prevProps) {
|
||||
this.setState({ showCleanDialog: this.props.showCleanDialog,
|
||||
allMonkeysAreDead: this.props.allMonkeysAreDead})
|
||||
}
|
||||
}
|
||||
|
||||
render = () => {
|
||||
return (
|
||||
<Modal show={this.state.showCleanDialog} onHide={() => this.props.onClose()}>
|
||||
<Modal.Body>
|
||||
<h2>
|
||||
<div className='text-center'>Reset environment</div>
|
||||
</h2>
|
||||
<p style={{'fontSize': '1.2em', 'marginBottom': '2em'}}>
|
||||
Are you sure you want to reset the environment?
|
||||
</p>
|
||||
{
|
||||
!this.state.allMonkeysAreDead ?
|
||||
<div className='alert alert-warning'>
|
||||
<i className='glyphicon glyphicon-warning-sign' style={{'marginRight': '5px'}}/>
|
||||
Some monkeys are still running. It's advised to kill all monkeys before resetting.
|
||||
</div>
|
||||
:
|
||||
<div/>
|
||||
}
|
||||
{
|
||||
this.state.loading ? <div className={'modalLoader'}><GridLoader/></div> : this.showModalButtons()
|
||||
}
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
)
|
||||
};
|
||||
|
||||
showModalButtons() {
|
||||
return (<div className='text-center'>
|
||||
<button type='button' className='btn btn-danger btn-lg' style={{margin: '5px'}}
|
||||
onClick={this.modalVerificationOnClick}>
|
||||
Reset environment
|
||||
</button>
|
||||
<button type='button' className='btn btn-success btn-lg' style={{margin: '5px'}}
|
||||
onClick={() => this.setState({showCleanDialog: false})}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>)
|
||||
}
|
||||
|
||||
modalVerificationOnClick = async () => {
|
||||
this.setState({loading: true});
|
||||
this.props.onVerify()
|
||||
.then(() => {this.setState({loading: false});
|
||||
this.props.onClose();})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default StartOverModal;
|
|
@ -0,0 +1,9 @@
|
|||
$yellow: #ffcc00;
|
||||
|
||||
.modalLoader div{
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.modalLoader div>div{
|
||||
background-color: $yellow;
|
||||
}
|
|
@ -6,7 +6,7 @@ PYTHON_FOLDER=/var/monkey/monkey_island/bin/python
|
|||
|
||||
# Prepare python virtualenv
|
||||
pip3 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER
|
||||
virtualenv -p python3 ${PYTHON_FOLDER}
|
||||
python3 -m virtualenv -p python3 ${PYTHON_FOLDER}
|
||||
|
||||
# install pip requirements
|
||||
${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER
|
||||
|
@ -22,7 +22,7 @@ if [ -d "/etc/systemd/network" ]; then
|
|||
systemctl enable monkey-island
|
||||
fi
|
||||
|
||||
${MONKEY_FOLDER}/monkey_island/create_certificate.sh
|
||||
${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/
|
||||
|
||||
service monkey-island start
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Package: gc-monkey-island
|
||||
Architecture: amd64
|
||||
Maintainer: Guardicore
|
||||
Homepage: http://www.guardicore.com
|
||||
Maintainer: Guardicore <support@infectionmonkey.com>
|
||||
Homepage: https://www.infectionmonkey.com
|
||||
Priority: optional
|
||||
Version: 1.0
|
||||
Description: Guardicore Infection Monkey Island installation package
|
|
@ -6,7 +6,7 @@ PYTHON_FOLDER=/var/monkey/monkey_island/bin/python
|
|||
|
||||
# Prepare python virtualenv
|
||||
pip3 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER
|
||||
virtualenv -p python3 ${PYTHON_FOLDER}
|
||||
python3 -m virtualenv -p python3 ${PYTHON_FOLDER}
|
||||
|
||||
# install pip requirements
|
||||
${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER
|
||||
|
@ -25,7 +25,7 @@ if [ -d "/etc/systemd/network" ]; then
|
|||
systemctl enable monkey-island
|
||||
fi
|
||||
|
||||
${MONKEY_FOLDER}/monkey_island/create_certificate.sh
|
||||
${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/
|
||||
|
||||
service monkey-island start
|
||||
service monkey-mongo start
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
openssl genrsa -out ./cc/server.key 2048
|
||||
openssl req -new -key ./cc/server.key -out ./cc/server.csr -subj "/OU=Monkey Department/CN=monkey.com"
|
||||
openssl x509 -req -days 366 -in ./cc/server.csr -signkey ./cc/server.key -out ./cc/server.crt
|
||||
server_root=${1:-"./cc"}
|
||||
|
||||
|
||||
openssl genrsa -out "$server_root"/server.key 2048
|
||||
openssl req -new -key "$server_root"/server.key -out "$server_root"/server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com"
|
||||
openssl x509 -req -days 366 -in "$server_root"/server.csr -signkey "$server_root"/server.key -out $server_root/server.crt
|
||||
|
||||
|
|
|
@ -10,16 +10,25 @@ MONGODB_DIR=$1 # If using deb, this should be: /var/monkey/monkey_island/bin/mon
|
|||
|
||||
if [[ ${os_version_monkey} == "Ubuntu 16.04"* ]]; then
|
||||
echo Detected Ubuntu 16.04
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.6.12.tgz"
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.2.3.tgz"
|
||||
elif [[ ${os_version_monkey} == "Ubuntu 18.04"* ]]; then
|
||||
echo Detected Ubuntu 18.04
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1804-4.2.0.tgz"
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1804-4.2.3.tgz"
|
||||
elif [[ ${os_version_monkey} == "Ubuntu 19.10"* ]]; then
|
||||
echo Detected Ubuntu 19.10
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1804-4.2.3.tgz"
|
||||
elif [[ ${os_version_monkey} == "Debian GNU/Linux 8"* ]]; then
|
||||
echo Detected Debian 8
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian81-3.6.12.tgz"
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian81-4.0.16.tgz"
|
||||
elif [[ ${os_version_monkey} == "Debian GNU/Linux 9"* ]]; then
|
||||
echo Detected Debian 9
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian92-3.6.12.tgz"
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian92-4.2.3.tgz"
|
||||
elif [[ ${os_version_monkey} == "Debian GNU/Linux 10"* ]]; then
|
||||
echo Detected Debian 10
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian10-4.2.3.tgz"
|
||||
elif [[ ${os_version_monkey} == "Kali GNU/Linux"* ]]; then
|
||||
echo Detected Kali Linux
|
||||
export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian10-4.2.3.tgz"
|
||||
else
|
||||
echo Unsupported OS
|
||||
exit 1
|
||||
|
@ -32,7 +41,7 @@ pushd "${TEMP_MONGO}" || {
|
|||
}
|
||||
|
||||
if exists wget; then
|
||||
wget ${tgz_url} -O mongodb.tgz
|
||||
wget -q ${tgz_url} -O mongodb.tgz
|
||||
else
|
||||
if exists curl; then
|
||||
curl --output mongodb.tgz ${tgz_url}
|
||||
|
|
|
@ -15,7 +15,9 @@ def main():
|
|||
hookspath=None,
|
||||
runtime_hooks=None,
|
||||
binaries=None,
|
||||
datas=None,
|
||||
datas=[
|
||||
("../common/BUILD", "/common")
|
||||
],
|
||||
excludes=None,
|
||||
win_no_prefer_redirects=None,
|
||||
win_private_assemblies=None,
|
||||
|
@ -35,6 +37,7 @@ def main():
|
|||
debug=False,
|
||||
strip=get_exe_strip(),
|
||||
upx=True,
|
||||
upx_exclude=['vcruntime140.dll'],
|
||||
console=True,
|
||||
icon=get_exe_icon())
|
||||
|
||||
|
@ -44,7 +47,7 @@ def is_windows():
|
|||
|
||||
|
||||
def is_32_bit():
|
||||
return platform.architecture()[0] == "32bit"
|
||||
return sys.maxsize <= 2**32
|
||||
|
||||
|
||||
def process_datas(orig_datas):
|
||||
|
|
|
@ -103,4 +103,4 @@
|
|||
|
||||
#### How to run
|
||||
|
||||
1. When your current working directory is monkey, run ./monkey_island/linux/run.sh (located under /linux)
|
||||
1. When your current working directory is monkey, run `chmod 755 ./monkey_island/linux/run.sh` followed by `./monkey_island/linux/run.sh` (located under /linux)
|
||||
|
|
|
@ -3,10 +3,6 @@ bson
|
|||
python-dateutil
|
||||
tornado
|
||||
werkzeug
|
||||
jinja2
|
||||
markupsafe
|
||||
itsdangerous
|
||||
click
|
||||
flask
|
||||
Flask-Pymongo
|
||||
Flask-Restful
|
||||
|
|
|
@ -4,3 +4,4 @@ log_cli_level = DEBUG
|
|||
log_cli_format = %(asctime)s [%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s
|
||||
log_cli_date_format=%H:%M:%S
|
||||
addopts = -v --capture=sys
|
||||
norecursedirs = node_modules dist
|
||||
|
|
Loading…
Reference in New Issue