Merge branch 'release/1.10.0' into develop

This commit is contained in:
Mike Salvatore 2021-03-30 15:59:20 -04:00
commit 176ad01c14
76 changed files with 743 additions and 276 deletions

3
.gitmodules vendored
View File

@ -4,6 +4,3 @@
[submodule "docs/themes/learn"] [submodule "docs/themes/learn"]
path = docs/themes/learn path = docs/themes/learn
url = https://github.com/guardicode/hugo-theme-learn.git url = https://github.com/guardicode/hugo-theme-learn.git
[submodule "monkey/infection_monkey/system_info/collectors/scoutsuite"]
path = monkey/common/cloud/scoutsuite
url = https://github.com/guardicode/ScoutSuite.git

View File

@ -27,7 +27,6 @@ install:
- pip install flake8 pytest pytest-cov dlint isort # for next stages - pip install flake8 pytest pytest-cov dlint isort # for next stages
- pip install coverage # for code coverage - pip install coverage # for code coverage
- pip install -r monkey/infection_monkey/requirements.txt # for unit tests - pip install -r monkey/infection_monkey/requirements.txt # for unit tests
- pip install -r monkey/common/cloud/scoutsuite/requirements.txt
- pip install pipdeptree - pip install pipdeptree
# Fail builds on possible conflicting dependencies. # Fail builds on possible conflicting dependencies.
- pipdeptree --warn fail - pipdeptree --warn fail
@ -56,7 +55,7 @@ install:
script: script:
# Check Python code # Check Python code
## Check syntax errors and fail the build if any are found. ## Check syntax errors and fail the build if any are found.
- flake8 ./monkey --exclude=monkey/common/cloud/scoutsuite --config=./ci_scripts/flake8_syntax_check.ini - flake8 ./monkey --config=./ci_scripts/flake8_syntax_check.ini
## Warn about linter issues. ## 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. ### --exit-zero forces Flake8 to use the exit status code 0 even if there are errors, which means this will NOT fail the build.

View File

@ -66,7 +66,6 @@ MONGO_PATH="$ISLAND_PATH/bin/mongodb"
ISLAND_BINARIES_PATH="$ISLAND_PATH/cc/binaries" ISLAND_BINARIES_PATH="$ISLAND_PATH/cc/binaries"
INFECTION_MONKEY_DIR="$monkey_home/monkey/infection_monkey" INFECTION_MONKEY_DIR="$monkey_home/monkey/infection_monkey"
MONKEY_BIN_DIR="$INFECTION_MONKEY_DIR/bin" MONKEY_BIN_DIR="$INFECTION_MONKEY_DIR/bin"
SCOUTSUITE_DIR="$monkey_home/monkey/common/cloud/scoutsuite"
if ! has_sudo; then if ! has_sudo; then
log_message "You need root permissions for some of this script operations. \ log_message "You need root permissions for some of this script operations. \
@ -142,10 +141,6 @@ sudo apt-get install -y libffi-dev upx libssl-dev libc++1
requirements_monkey="$INFECTION_MONKEY_DIR/requirements.txt" requirements_monkey="$INFECTION_MONKEY_DIR/requirements.txt"
${python_cmd} -m pip install -r "${requirements_monkey}" --user --upgrade || handle_error ${python_cmd} -m pip install -r "${requirements_monkey}" --user --upgrade || handle_error
log_message "Installing ScoutSuite requirements"
requirements_scoutsuite="$SCOUTSUITE_DIR/requirements.txt"
${python_cmd} -m pip install -r "${requirements_scoutsuite}" --user --upgrade || handle_error
agents=${3:-true} agents=${3:-true}
# Download binaries # Download binaries
if [ "$agents" = true ] ; then if [ "$agents" = true ] ; then

View File

@ -7,7 +7,7 @@ pre: "<i class='fas fa-question'></i> "
Here are some of the most common questions we receive about the Infection Monkey. If the answer you're looking for isn't here, talk with us [on our Slack channel](https://infectionmonkey.slack.com/join/shared_invite/enQtNDU5MjAxMjg1MjU1LWM0NjVmNWE2ZTMzYzAxOWJiYmMxMzU0NWU3NmUxYjcyNjk0YWY2MDkwODk4NGMyNDU4NzA4MDljOWNmZWViNDU), email us at [support@infectionmonkey.com](mailto:support@infectionmonkey.com) or [open an issue on GitHub](https://github.com/guardicore/monkey). Here are some of the most common questions we receive about the Infection Monkey. If the answer you're looking for isn't here, talk with us [on our Slack channel](https://infectionmonkey.slack.com/join/shared_invite/enQtNDU5MjAxMjg1MjU1LWM0NjVmNWE2ZTMzYzAxOWJiYmMxMzU0NWU3NmUxYjcyNjk0YWY2MDkwODk4NGMyNDU4NzA4MDljOWNmZWViNDU), email us at [support@infectionmonkey.com](mailto:support@infectionmonkey.com) or [open an issue on GitHub](https://github.com/guardicore/monkey).
- [Where can I get the latest Monkey version? 📰](#where-can-i-get-the-latest-monkey-version) - [Where can I get the latest Monkey version?](#where-can-i-get-the-latest-monkey-version)
- [How long does a single Monkey run for? Is there a time limit?](#how-long-does-a-single-monkey-run-for-is-there-a-time-limit) - [How long does a single Monkey run for? Is there a time limit?](#how-long-does-a-single-monkey-run-for-is-there-a-time-limit)
- [How to reset the password?](#how-to-reset-the-password) - [How to reset the password?](#how-to-reset-the-password)
- [Should I run the Monkey continuously?](#should-i-run-the-monkey-continuously) - [Should I run the Monkey continuously?](#should-i-run-the-monkey-continuously)
@ -24,9 +24,9 @@ Here are some of the most common questions we receive about the Infection Monkey
- [After I've set up Monkey Island, how can I execute the Monkey?](#after-ive-set-up-monkey-island-how-can-i-execute-the-monkey) - [After I've set up Monkey Island, how can I execute the Monkey?](#after-ive-set-up-monkey-island-how-can-i-execute-the-monkey)
- [How can I make the monkey propagate “deeper” into the network?](#how-can-i-make-the-monkey-propagate-deeper-into-the-network) - [How can I make the monkey propagate “deeper” into the network?](#how-can-i-make-the-monkey-propagate-deeper-into-the-network)
- [The report returns a blank screen](#the-report-returns-a-blank-screen) - [The report returns a blank screen](#the-report-returns-a-blank-screen)
- [How can I get involved with the project? 👩‍💻👨‍💻](#how-can-i-get-involved-with-the-project) - [How can I get involved with the project?](#how-can-i-get-involved-with-the-project)
## Where can I get the latest Monkey version? 📰 ## Where can I get the latest Monkey version?
For the latest **stable** release for users, visit [our downloads page](https://www.guardicore.com/infectionmonkey/#download). **This is the recommended and supported version**! For the latest **stable** release for users, visit [our downloads page](https://www.guardicore.com/infectionmonkey/#download). **This is the recommended and supported version**!
@ -167,7 +167,7 @@ This is sometimes caused when Monkey Island is installed with an old version of
- **Linux**: First, uninstall the current version with `sudo apt uninstall mongodb` and then install the latest version using the [official mongodb manual](https://docs.mongodb.com/manual/administration/install-community/). - **Linux**: First, uninstall the current version with `sudo apt uninstall mongodb` and then install the latest version using the [official mongodb manual](https://docs.mongodb.com/manual/administration/install-community/).
- **Windows**: First, remove the MongoDB binaries from the `monkey\monkey_island\bin\mongodb` folder. Download and install the latest version of mongodb using the [official mongodb manual](https://docs.mongodb.com/manual/administration/install-community/). After installation is complete, copy the files from the `C:\Program Files\MongoDB\Server\4.2\bin` folder to the `monkey\monkey_island\bin\mongodb folder`. Try to run the Island again and everything should work. - **Windows**: First, remove the MongoDB binaries from the `monkey\monkey_island\bin\mongodb` folder. Download and install the latest version of mongodb using the [official mongodb manual](https://docs.mongodb.com/manual/administration/install-community/). After installation is complete, copy the files from the `C:\Program Files\MongoDB\Server\4.2\bin` folder to the `monkey\monkey_island\bin\mongodb folder`. Try to run the Island again and everything should work.
## How can I get involved with the project? 👩‍💻👨‍💻 ## How can I get involved with the project?
The Monkey is an open-source project, and we weclome contributions and contributors. Check out the [contribution documentation](../development) for more information. The Monkey is an open-source project, and we weclome contributions and contributors. Check out the [contribution documentation](../development) for more information.

View File

@ -8,33 +8,48 @@ disableToc: false
tags: ["setup", "debian", "linux"] tags: ["setup", "debian", "linux"]
--- ---
## Supported Distros
This Debian package has been tested on Ubuntu Bionic 18.04 LTS and Ubuntu Focal 20.04 LTS.
## Deployment ## Deployment
To extract the `tar.gz` file, run `tar -xvzf monkey-island-debian.tar.gz`. 1. Update your package list by running:
```sh
sudo apt update
```
1. If you are using Ubuntu Focal 20.04, run the following commands to install
Python 3.7:
```sh
sudo apt install software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt install python3.7 python3.7-dev
```
1. Extract the tarball by running:
```sh
tar -xvzf monkey-island-debian.tgz
```
1. Install the Monkey Island Debian package:
```sh
sudo dpkg -i monkey_island.deb # this might print errors
```
1. If, at this point, you receive dpkg errors that look like this:
Once you've extracted the package, deploy it using run the following commands: ```sh
dpkg: error processing package gc-monkey-island (--install):
```sh
sudo apt update
sudo dpkg -i monkey_island.deb # this might print errors
```
If, at this point, you receive dpkg printed errors that look like this:
```sh
dpkg: error processing package gc-monkey-island (--install):
dependency problems - leaving unconfigured dependency problems - leaving unconfigured
Errors were encountered while processing: Errors were encountered while processing:
gc-monkey-island gc-monkey-island
``` ```
It just means that not all dependencies were pre-installed on your system. It just means that not all dependencies were pre-installed on your system.
That's no problem! Just run the following command, which will install all That's no problem! Just run the following command, which will install all
dependencies, and then install the Monkey Island: dependencies, and then install the Monkey Island:
```sh ```sh
sudo apt install -f sudo apt install -f
``` ```
## Troubleshooting ## Troubleshooting

View File

@ -14,11 +14,11 @@ To extract the `tar.gz` file, run `tar -xvzf monkey-island-docker.tar.gz`.
Once you've extracted the container from the tar.gz file, run the following commands: Once you've extracted the container from the tar.gz file, run the following commands:
```sh ```sh
sudo docker load -i dk.monkeyisland.1.9.0.tar sudo docker load -i dk.monkeyisland.1.10.0.tar
sudo docker pull mongo sudo docker pull mongo:4.2
sudo mkdir -p /var/monkey-mongo/data/db sudo mkdir -p /var/monkey-mongo/data/db
sudo docker run --name monkey-mongo --network=host -v /var/monkey-mongo/data/db:/data/db -d mongo sudo docker run --name monkey-mongo --network=host -v /var/monkey-mongo/data/db:/data/db -d mongo:4.2
sudo docker run --name monkey-island --network=host -d guardicore/monkey-island:1.9.0 sudo docker run --name monkey-island --network=host -d guardicore/monkey-island:1.10.0
``` ```
## Upgrading ## Upgrading

View File

@ -16,8 +16,9 @@ tags: ["setup", "vmware"]
1. Log in to the machine with the following credentials: 1. Log in to the machine with the following credentials:
1. Username: **monkeyuser** 1. Username: **monkeyuser**
1. Password: **Noon.Earth.Always** 1. Password: **Noon.Earth.Always**
1. It's recommended you change the machine passwords by running the following 1. For security purposes, it's recommended that you change the machine
commands: `sudo passwd monkeyuser`, `sudo passwd root`. passwords by running the following commands: `sudo passwd monkeyuser`, `sudo
passwd root`.
## OVA network modes ## OVA network modes
@ -26,37 +27,43 @@ You can use the OVA in one of two modes:
1. In a network with the DHCP configured — In this case, the Monkey Island will 1. In a network with the DHCP configured — In this case, the Monkey Island will
automatically query and receive an IP address from the network. automatically query and receive an IP address from the network.
1. With a static IP address — In this case, you should log in to the VM console 1. With a static IP address — In this case, you should log in to the VM console
with the username `root` and the password `G3aJ9szrvkxTmfAG`. After logging with the username `monkeyuser` and the password `Noon.Earth.Always`. After logging
in, edit the interfaces file by entering the following command in the in, edit the Netplan configuration by entering the following command in the
prompt: prompt:
```sh ```sh
sudo nano /etc/network/interfaces sudo nano /etc/netplan/00-installer-config.yaml
``` ```
Change the lines: Make the following changes:
```sh ```diff
auto ens160 # This is the network config written by 'subiquity'
iface ens160 inet dhcp network:
ethernets:
ens160:
- dhcp4: true
+ dhcp4: false
+ addresses: [XXX.XXX.XXX.XXX/24]
+ gateway4: YYY.YYY.YYY.YYY
+ nameservers:
+ addresses: [1.1.1.1]
version: 2
``` ```
to the following: Replace `XXX.XXX.XXX.XXX` with the desired IP addess of the VM. Replace
`YYY.YYY.YYY.YYY` with the default gateway.
```sh
auto ens160
iface ens160 inet static
address AAA.BBB.CCC.DDD
netmask XXX.XXX.XXX.XXX
gateway YYY.YYY.YYY.YYY
```
Save the changes then run the command: Save the changes then run the command:
```sh ```sh
sudo ifdown ens160 && ifup ens160 sudo netplan apply
``` ```
If this configuration does not suit your needs, see
https://netplan.io/examples/ for more information about how to configure
Netplan.
## Upgrading ## Upgrading
Currently, there's no "upgrade-in-place" option when a new version is released. Currently, there's no "upgrade-in-place" option when a new version is released.

View File

@ -35,6 +35,24 @@ $ sha256sum monkey-linux-64
## Latest version checksums ## Latest version checksums
| Filename | Type | Version | SHA256 |
|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------|
| monkey-windows-64.exe | Windows Agent | 1.10.0 | `3b499a4cf1a67a33a91c73b05884e4d6749e990e444fa1d2a3281af4db833fa1` |
| monkey-windows-32.exe | Windows Agent | 1.10.0 | `8e891e90b11b97fbbef27f1408c1fcad486b19c612773f2d6a9edac5d4cdb47f` |
| monkey-linux-64 | Linux Agent | 1.10.0 | `932f703510b6484c3824fc797f90f99722e38a7f8956cf6fa58fdecb3790ab93` |
| monkey-linux-32 | Linux Agent | 1.10.0 | `a6de7d571051292b9db966afe025413dc20b214c4aab53e48d90d8e04264f4f5` |
| infection_monkey_deb.tgz | Debian Package | 1.10.0 | `534d85c4abc78e2c86a74d8b88759b091b62077dd9e32f02eeb43d716d359ff6` |
| infection_monkey_debzt.tgz | Debian Package | 1.10.0 | `bd01d8482f80990e6cc0ed654c07dbd80da71eebe3dd244365e9bc00f86b1c03` |
| Monkey Island v1.10.0_3593_windows.exe | Windows Installer | 1.10.0 | `ebd2c5627d21dd8670def02c3a5a995f9e799ba567cf4caacd702654264ddf06` |
| Monkey Island v1.10.0_3593_windowszt.exe | Windows Installer | 1.10.0 | `60aaf3b32e5d06c91fe0d4f1b950529517ac33796f67e9ccfef0e8ce1c5372d8` |
| infection_monkey_docker_docker_20210326_171631.tgz | Docker | 1.10.0 | `e4f9c7c5aafe7e38b33d2927a9c0cf6a3ac27858d3d0e3f2252c2e91809a78db` |
| infection_monkey_docker_dockerzt_20210326_172035.tgz | Docker | 1.10.0 | `248640e9eaa18e4c27f67237f0594d9533732f372ba4674d5d1bea43ab498cf5` |
| monkey-island-vmware.ova | OVA | 1.10.0 | `3472ad4ae557ddad7d7db8fbbfcfd33c4f2d95d870b18fa4cab49af6b562009c` |
| monkey-island-vmwarezt.ova | OVA | 1.10.0 | `3472ad4ae557ddad7d7db8fbbfcfd33c4f2d95d870b18fa4cab49af6b562009c` |
## Older checksums
| Filename | Type | Version | SHA256 | | Filename | Type | Version | SHA256 |
|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------| |------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------|
| monkey-windows-64.exe | Windows Agent | 1.9.0 | `24622cb8dbabb0cf4b25ecd3c13800c72ec5b59b76895b737ece509640d4c068` | | monkey-windows-64.exe | Windows Agent | 1.9.0 | `24622cb8dbabb0cf4b25ecd3c13800c72ec5b59b76895b737ece509640d4c068` |
@ -49,12 +67,6 @@ $ sha256sum monkey-linux-64
| infection_monkey_docker_dockerzt_20200806_154742.tgz | Docker | 1.9.0 | `a84dbaad32ae42cc2d359ffbe062aec493a7253cf706a2d45f0d0b1c230f9348` | | infection_monkey_docker_dockerzt_20200806_154742.tgz | Docker | 1.9.0 | `a84dbaad32ae42cc2d359ffbe062aec493a7253cf706a2d45f0d0b1c230f9348` |
| monkey-island-vmware.ova | OVA | 1.9.0 | `3861d46518e8a92e49992b26dbff9fe8e8a4ac5fd24d68e68b13e7fd3fa22247` | | monkey-island-vmware.ova | OVA | 1.9.0 | `3861d46518e8a92e49992b26dbff9fe8e8a4ac5fd24d68e68b13e7fd3fa22247` |
| monkey-island-vmwarezt.ova | OVA | 1.9.0 | `03d356eb35e6515146f5bd798bb62cb15c56fcdf83a5281cf6cdc9b901586026` | | monkey-island-vmwarezt.ova | OVA | 1.9.0 | `03d356eb35e6515146f5bd798bb62cb15c56fcdf83a5281cf6cdc9b901586026` |
## Older checksums
| Filename | Type | Version | SHA256 |
|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------|
| monkey-windows-64.exe | Windows Agent | 1.8.2 | `2e6a1cb5523d87ddfd48f75b10114617343fbac8125fa950ba7f00289b38b550` | | monkey-windows-64.exe | Windows Agent | 1.8.2 | `2e6a1cb5523d87ddfd48f75b10114617343fbac8125fa950ba7f00289b38b550` |
| monkey-windows-32.exe | Windows Agent | 1.8.2 | `86a7d7065e73b795e38f2033be0c53f3ac808cc67478aed794a7a6c89123979f` | | monkey-windows-32.exe | Windows Agent | 1.8.2 | `86a7d7065e73b795e38f2033be0c53f3ac808cc67478aed794a7a6c89123979f` |
| monkey-linux-64 | Linux Agent | 1.8.2 | `4dce4a115d41b43adffc11672fae2164265f8902267f1355d02bebb802bd45c5` | | monkey-linux-64 | Linux Agent | 1.8.2 | `4dce4a115d41b43adffc11672fae2164265f8902267f1355d02bebb802bd45c5` |

View File

@ -19,10 +19,10 @@ instead will just test performance of endpoints in already present island state.
Example run command: Example run command:
`monkey\envs\monkey_zoo\blackbox>python -m pytest -s --island=35.207.152.72:5000 test_blackbox.py` `monkey\monkey>python -m pytest -s --island=35.207.152.72:5000 ..\envs\monkey_zoo\blackbox\test_blackbox.py`
#### Running in PyCharm #### Running in PyCharm
Configure a PyTest configuration with the additional arguments `-s --island=35.207.152.72`, and to run from Configure a PyTest configuration with the additional arguments `-s --island=35.207.152.72:5000`, and to run from
directory `monkey\envs\monkey_zoo\blackbox`. directory `monkey\envs\monkey_zoo\blackbox`.
### Running telemetry performance test ### Running telemetry performance test

View File

@ -4,5 +4,5 @@ from abc import ABCMeta, abstractmethod
class Analyzer(object, metaclass=ABCMeta): class Analyzer(object, metaclass=ABCMeta):
@abstractmethod @abstractmethod
def analyze_test_results(self): def analyze_test_results(self) -> bool:
raise NotImplementedError() raise NotImplementedError()

View File

@ -0,0 +1,70 @@
from typing import List
from pprint import pformat
import dpath.util
from common.config_value_paths import USER_LIST_PATH, PASSWORD_LIST_PATH, NTLM_HASH_LIST_PATH, LM_HASH_LIST_PATH
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
# Query for telemetry collection to see if password restoration was successful
TELEM_QUERY = {'telem_category': 'exploit',
'data.exploiter': 'ZerologonExploiter',
'data.info.password_restored': True}
class ZerologonAnalyzer(Analyzer):
def __init__(self, island_client: MonkeyIslandClient, expected_credentials: List[str]):
self.island_client = island_client
self.expected_credentials = expected_credentials
self.log = AnalyzerLog(self.__class__.__name__)
def analyze_test_results(self):
self.log.clear()
is_creds_gathered = self._analyze_credential_gathering()
is_creds_restored = self._analyze_credential_restore()
return is_creds_gathered and is_creds_restored
def _analyze_credential_gathering(self) -> bool:
config = self.island_client.get_config()
credentials_on_island = ZerologonAnalyzer._get_relevant_credentials(config)
return self._is_all_credentials_in_list(credentials_on_island)
@staticmethod
def _get_relevant_credentials(config: dict):
credentials_on_island = []
credentials_on_island.extend(dpath.util.get(config['configuration'], USER_LIST_PATH))
credentials_on_island.extend(dpath.util.get(config['configuration'], NTLM_HASH_LIST_PATH))
credentials_on_island.extend(dpath.util.get(config['configuration'], LM_HASH_LIST_PATH))
return credentials_on_island
def _is_all_credentials_in_list(self,
all_creds: List[str]) -> bool:
credentials_missing = [cred for cred in self.expected_credentials if cred not in all_creds]
self._log_creds_not_gathered(credentials_missing)
return not credentials_missing
def _log_creds_not_gathered(self, missing_creds: List[str]):
if not missing_creds:
self.log.add_entry("Zerologon exploiter gathered all credentials expected.")
else:
for cred in missing_creds:
self.log.add_entry(f"Credential Zerologon exploiter failed to gathered:{cred}.")
def _analyze_credential_restore(self) -> bool:
cred_restore_telems = self.island_client.find_telems_in_db(TELEM_QUERY)
self._log_credential_restore(cred_restore_telems)
return bool(cred_restore_telems)
def _log_credential_restore(self, telem_list: List[dict]):
if telem_list:
self.log.add_entry("Zerologon exploiter telemetry contains indicators that credentials "
"were successfully restored.")
else:
self.log.add_entry("Credential restore failed or credential restore "
"telemetry not found on the Monkey Island.")
self.log.add_entry(f"Query for credential restore telem: {pformat(TELEM_QUERY)}")

View File

@ -1,4 +1,4 @@
from envs.monkey_zoo.blackbox.island_configs.config_template import ConfigTemplate from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
# Disables a lot of config values not required for a specific feature test # Disables a lot of config values not required for a specific feature test

View File

@ -0,0 +1,14 @@
from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Drupal(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update({
"internal.classes.finger_classes": ["PingScanner", "HTTPFinger"],
"basic.exploiters.exploiter_classes": ["DrupalExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.28"]
})

View File

@ -1,7 +1,7 @@
from copy import copy from copy import copy
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.island_configs.config_template import ConfigTemplate from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Elastic(ConfigTemplate): class Elastic(ConfigTemplate):
@ -10,5 +10,6 @@ class Elastic(ConfigTemplate):
config_values.update({ config_values.update({
"basic.exploiters.exploiter_classes": ["ElasticGroovyExploiter"], "basic.exploiters.exploiter_classes": ["ElasticGroovyExploiter"],
"internal.classes.finger_classes": ["PingScanner", "HTTPFinger", "ElasticFinger"],
"basic_network.scope.subnet_scan_list": ["10.2.2.4", "10.2.2.5"] "basic_network.scope.subnet_scan_list": ["10.2.2.4", "10.2.2.5"]
}) })

View File

@ -1,9 +1,10 @@
from copy import copy from copy import copy
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Hadoop(BaseTemplate): class Hadoop(ConfigTemplate):
config_values = copy(BaseTemplate.config_values) config_values = copy(BaseTemplate.config_values)

View File

@ -1,9 +1,10 @@
from copy import copy from copy import copy
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Mssql(BaseTemplate): class Mssql(ConfigTemplate):
config_values = copy(BaseTemplate.config_values) config_values = copy(BaseTemplate.config_values)
config_values.update({ config_values.update({

View File

@ -1,4 +1,4 @@
from envs.monkey_zoo.blackbox.island_configs.config_template import ConfigTemplate from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Performance(ConfigTemplate): class Performance(ConfigTemplate):

View File

@ -1,9 +1,10 @@
from copy import copy from copy import copy
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class ShellShock(BaseTemplate): class ShellShock(ConfigTemplate):
config_values = copy(BaseTemplate.config_values) config_values = copy(BaseTemplate.config_values)
config_values.update({ config_values.update({

View File

@ -1,9 +1,10 @@
from copy import copy from copy import copy
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class SmbMimikatz(BaseTemplate): class SmbMimikatz(ConfigTemplate):
config_values = copy(BaseTemplate.config_values) config_values = copy(BaseTemplate.config_values)
config_values.update({ config_values.update({

View File

@ -1,9 +1,10 @@
from copy import copy from copy import copy
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class SmbPth(BaseTemplate): class SmbPth(ConfigTemplate):
config_values = copy(BaseTemplate.config_values) config_values = copy(BaseTemplate.config_values)
config_value_list = { config_value_list = {

View File

@ -1,8 +1,11 @@
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Ssh(BaseTemplate): class Ssh(ConfigTemplate):
config_values = BaseTemplate.config_values config_values = copy(BaseTemplate.config_values)
config_values.update({ config_values.update({
"basic.exploiters.exploiter_classes": ["SSHExploiter"], "basic.exploiters.exploiter_classes": ["SSHExploiter"],

View File

@ -0,0 +1,14 @@
from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Struts2(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update({
"basic.exploiters.exploiter_classes": ["Struts2Exploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.23", "10.2.2.24"]
})

View File

@ -1,8 +1,11 @@
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Tunneling(BaseTemplate): class Tunneling(ConfigTemplate):
config_values = BaseTemplate.config_values config_values = copy(BaseTemplate.config_values)
config_values.update({ config_values.update({
"basic.exploiters.exploiter_classes": ["SmbExploiter", "basic.exploiters.exploiter_classes": ["SmbExploiter",
@ -13,6 +16,8 @@ class Tunneling(BaseTemplate):
"10.2.1.10", "10.2.1.10",
"10.2.0.11", "10.2.0.11",
"10.2.0.12"], "10.2.0.12"],
"basic_network.scope.depth": 3,
"internal.general.keep_tunnel_open_time": 180,
"basic.credentials.exploit_password_list": ["Password1!", "basic.credentials.exploit_password_list": ["Password1!",
"3Q=(Ge(+&w]*", "3Q=(Ge(+&w]*",
"`))jU7L(w}", "`))jU7L(w}",

View File

@ -0,0 +1,14 @@
from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Weblogic(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update({
"basic.exploiters.exploiter_classes": ["WebLogicExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.18", "10.2.2.19"]
})

View File

@ -1,8 +1,11 @@
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class WmiMimikatz(BaseTemplate): class WmiMimikatz(ConfigTemplate):
config_values = BaseTemplate.config_values config_values = copy(BaseTemplate.config_values)
config_values.update({ config_values.update({
"basic.exploiters.exploiter_classes": ["WmiExploiter"], "basic.exploiters.exploiter_classes": ["WmiExploiter"],

View File

@ -0,0 +1,22 @@
from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class WmiPth(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["WmiExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.15"],
"basic.credentials.exploit_password_list": ["Password1!"],
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
"internal.classes.finger_classes": ["PingScanner", "HTTPFinger"],
"internal.exploits.exploit_ntlm_hash_list": [
"5da0889ea2081aa79f6852294cba4a5e",
"50c9987a6bf1ac59398df9f911122c9b",
],
}
)

View File

@ -0,0 +1,16 @@
from copy import copy
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Zerologon(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update({
"basic.exploiters.exploiter_classes": ["ZerologonExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.25"],
# Empty list to make sure ZeroLogon adds "Administrator" username
"basic.credentials.exploit_user_list": []
})

View File

@ -4,7 +4,7 @@ import dpath.util
from typing_extensions import Type from typing_extensions import Type
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
from envs.monkey_zoo.blackbox.island_configs.config_template import ConfigTemplate from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class IslandConfigParser: class IslandConfigParser:

View File

@ -1,6 +1,7 @@
import json import json
import logging import logging
from time import sleep from time import sleep
from typing import Union
from bson import json_util from bson import json_util
@ -8,6 +9,7 @@ from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import Monkey
SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5 SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5
MONKEY_TEST_ENDPOINT = 'api/test/monkey' MONKEY_TEST_ENDPOINT = 'api/test/monkey'
TELEMETRY_TEST_ENDPOINT = 'api/test/telemetry'
LOG_TEST_ENDPOINT = 'api/test/log' LOG_TEST_ENDPOINT = 'api/test/log'
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -67,6 +69,13 @@ class MonkeyIslandClient(object):
MonkeyIslandClient.form_find_query_for_request(query)) MonkeyIslandClient.form_find_query_for_request(query))
return MonkeyIslandClient.get_test_query_results(response) return MonkeyIslandClient.get_test_query_results(response)
def find_telems_in_db(self, query: dict):
if query is None:
raise TypeError
response = self.requests.get(TELEMETRY_TEST_ENDPOINT,
MonkeyIslandClient.form_find_query_for_request(query))
return MonkeyIslandClient.get_test_query_results(response)
def get_all_monkeys_from_db(self): def get_all_monkeys_from_db(self):
response = self.requests.get(MONKEY_TEST_ENDPOINT, response = self.requests.get(MONKEY_TEST_ENDPOINT,
MonkeyIslandClient.form_find_query_for_request(None)) MonkeyIslandClient.form_find_query_for_request(None))
@ -78,7 +87,7 @@ class MonkeyIslandClient(object):
return MonkeyIslandClient.get_test_query_results(response) return MonkeyIslandClient.get_test_query_results(response)
@staticmethod @staticmethod
def form_find_query_for_request(query): def form_find_query_for_request(query: Union[dict, None]) -> dict:
return {'find_query': json_util.dumps(query)} return {'find_query': json_util.dumps(query)}
@staticmethod @staticmethod

View File

@ -1,11 +0,0 @@
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate
class Struts2(BaseTemplate):
config_values = BaseTemplate.config_values
config_values.update({
"basic.exploiters.exploiter_classes": ["Struts2Exploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.23", "10.2.2.24"]
})

View File

@ -1,11 +0,0 @@
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate
class Weblogic(BaseTemplate):
config_values = BaseTemplate.config_values
config_values.update({
"basic.exploiters.exploiter_classes": ["WebLogicExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.18", "10.2.2.19"]
})

View File

@ -1,18 +0,0 @@
from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate
class WmiPth(BaseTemplate):
config_values = BaseTemplate.config_values
config_values.update({
"basic.exploiters.exploiter_classes": ["WmiExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.15"],
"basic.credentials.exploit_password_list": ["Password1!"],
"basic.credentials.exploit_user_list": ["Administrator",
"m0nk3y",
"user"],
"internal.classes.finger_classes": ["PingScanner",
"HTTPFinger"],
"internal.classes.exploits.exploit_ntlm_hash_list": ["5da0889ea2081aa79f6852294cba4a5e",
"50c9987a6bf1ac59398df9f911122c9b"]
})

View File

@ -7,24 +7,27 @@ from typing_extensions import Type
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import \ from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import \
CommunicationAnalyzer CommunicationAnalyzer
from envs.monkey_zoo.blackbox.analyzers.zerologon_analyzer import ZerologonAnalyzer
from envs.monkey_zoo.blackbox.island_client.island_config_parser import \ from envs.monkey_zoo.blackbox.island_client.island_config_parser import \
IslandConfigParser IslandConfigParser
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import \ from envs.monkey_zoo.blackbox.island_client.monkey_island_client import \
MonkeyIslandClient MonkeyIslandClient
from envs.monkey_zoo.blackbox.island_configs.config_template import ConfigTemplate from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
from envs.monkey_zoo.blackbox.island_configs.elastic import Elastic from envs.monkey_zoo.blackbox.config_templates.drupal import Drupal
from envs.monkey_zoo.blackbox.island_configs.hadoop import Hadoop from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic
from envs.monkey_zoo.blackbox.island_configs.mssql import Mssql from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop
from envs.monkey_zoo.blackbox.island_configs.performance import Performance from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql
from envs.monkey_zoo.blackbox.island_configs.shellshock import ShellShock from envs.monkey_zoo.blackbox.config_templates.performance import Performance
from envs.monkey_zoo.blackbox.island_configs.smb_mimikatz import SmbMimikatz from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock
from envs.monkey_zoo.blackbox.island_configs.smb_pth import SmbPth from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz
from envs.monkey_zoo.blackbox.island_configs.ssh import Ssh from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth
from envs.monkey_zoo.blackbox.island_configs.struts2 import Struts2 from envs.monkey_zoo.blackbox.config_templates.ssh import Ssh
from envs.monkey_zoo.blackbox.island_configs.tunneling import Tunneling from envs.monkey_zoo.blackbox.config_templates.struts2 import Struts2
from envs.monkey_zoo.blackbox.island_configs.weblogic import Weblogic from envs.monkey_zoo.blackbox.config_templates.tunneling import Tunneling
from envs.monkey_zoo.blackbox.island_configs.wmi_mimikatz import WmiMimikatz from envs.monkey_zoo.blackbox.config_templates.weblogic import Weblogic
from envs.monkey_zoo.blackbox.island_configs.wmi_pth import WmiPth from envs.monkey_zoo.blackbox.config_templates.wmi_mimikatz import WmiMimikatz
from envs.monkey_zoo.blackbox.config_templates.wmi_pth import WmiPth
from envs.monkey_zoo.blackbox.config_templates.zerologon import Zerologon
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import \ from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import \
TestLogsHandler TestLogsHandler
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
@ -44,8 +47,10 @@ DEFAULT_TIMEOUT_SECONDS = 5*60
MACHINE_BOOTUP_WAIT_SECONDS = 30 MACHINE_BOOTUP_WAIT_SECONDS = 30
GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'hadoop-2', 'hadoop-3', 'mssql-16', GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'hadoop-2', 'hadoop-3', 'mssql-16',
'mimikatz-14', 'mimikatz-15', 'struts2-23', 'struts2-24', 'tunneling-9', 'tunneling-10', 'mimikatz-14', 'mimikatz-15', 'struts2-23', 'struts2-24', 'tunneling-9', 'tunneling-10',
'tunneling-11', 'tunneling-12', 'weblogic-18', 'weblogic-19', 'shellshock-8', 'zerologon-25'] 'tunneling-11', 'tunneling-12', 'weblogic-18', 'weblogic-19', 'shellshock-8', 'zerologon-25',
'drupal-28']
LOG_DIR_PATH = "./logs" LOG_DIR_PATH = "./logs"
logging.basicConfig(level=logging.INFO)
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -138,6 +143,9 @@ class TestMonkeyBlackbox:
def test_smb_pth(self, island_client): def test_smb_pth(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH") TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH")
def test_drupal_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, Drupal, "Drupal_exploiter")
def test_elastic_exploiter(self, island_client): def test_elastic_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, Elastic, "Elastic_exploiter") TestMonkeyBlackbox.run_exploitation_test(island_client, Elastic, "Elastic_exploiter")
@ -159,6 +167,22 @@ class TestMonkeyBlackbox:
def test_wmi_pth(self, island_client): def test_wmi_pth(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, WmiPth, "WMI_PTH") TestMonkeyBlackbox.run_exploitation_test(island_client, WmiPth, "WMI_PTH")
def test_zerologon_exploiter(self, island_client):
test_name = "Zerologon_exploiter"
expected_creds = ["Administrator",
"aad3b435b51404eeaad3b435b51404ee",
"2864b62ea4496934a5d6e86f50b834a5"]
raw_config = IslandConfigParser.get_raw_config(Zerologon, island_client)
analyzer = ZerologonAnalyzer(island_client, expected_creds)
log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path())
ExploitationTest(
name=test_name,
island_client=island_client,
raw_config=raw_config,
analyzers=[analyzer],
timeout=DEFAULT_TIMEOUT_SECONDS,
log_handler=log_handler).run()
@pytest.mark.skip(reason="Perfomance test that creates env from fake telemetries is faster, use that instead.") @pytest.mark.skip(reason="Perfomance test that creates env from fake telemetries is faster, use that instead.")
def test_report_generation_performance(self, island_client, quick_performance_tests): def test_report_generation_performance(self, island_client, quick_performance_tests):
""" """

View File

@ -0,0 +1,13 @@
# BlackBox utility scripts
## Config generation script
This script is used to generate config files for manual tests.
Config file will be generated according to the templates in `envs/monkey_zoo/blackbox/config_templates`.
1. Reset the Island config to contain default configuration.
2. Run `envs/monkey_zoo/blackbox/utils/config_generation_script.py island_ip:5000` to populate
`envs/monkey_zoo/blackbox/utils/generated_configs` directory with configuration files.
!! It's important to target the Island you'll be testing, because configs contain Island's IPs
in the configuration !!

View File

@ -0,0 +1,78 @@
import argparse
import pathlib
from typing import Type
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
from envs.monkey_zoo.blackbox.config_templates.drupal import Drupal
from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic
from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop
from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql
from envs.monkey_zoo.blackbox.config_templates.performance import Performance
from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock
from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz
from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth
from envs.monkey_zoo.blackbox.config_templates.ssh import Ssh
from envs.monkey_zoo.blackbox.config_templates.struts2 import Struts2
from envs.monkey_zoo.blackbox.config_templates.tunneling import Tunneling
from envs.monkey_zoo.blackbox.config_templates.weblogic import Weblogic
from envs.monkey_zoo.blackbox.config_templates.wmi_mimikatz import WmiMimikatz
from envs.monkey_zoo.blackbox.config_templates.wmi_pth import WmiPth
from envs.monkey_zoo.blackbox.config_templates.zerologon import Zerologon
from envs.monkey_zoo.blackbox.island_client.island_config_parser import (
IslandConfigParser,
)
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import (
MonkeyIslandClient,
)
DST_DIR_NAME = "generated_configs"
DST_DIR_PATH = pathlib.Path(pathlib.Path(__file__).parent.absolute(), DST_DIR_NAME)
parser = argparse.ArgumentParser(description="Generate config files.")
parser.add_argument(
"island_ip",
metavar="IP:PORT",
help="Island endpoint. Example: 123.123.123.123:5000",
)
args = parser.parse_args()
island_client = MonkeyIslandClient(args.island_ip)
CONFIG_TEMPLATES = [
Elastic,
Hadoop,
Mssql,
Performance,
ShellShock,
SmbMimikatz,
SmbPth,
Ssh,
Struts2,
Tunneling,
Weblogic,
WmiMimikatz,
WmiPth,
Zerologon,
Drupal,
]
def generate_templates():
for template in CONFIG_TEMPLATES:
save_template_as_config(template)
def save_template_as_config(template: Type[ConfigTemplate]):
file_path = pathlib.Path(DST_DIR_PATH, f"{template.__name__}.conf")
file_contents = IslandConfigParser.get_raw_config(template, island_client)
save_to_file(file_path, file_contents)
def save_to_file(file_path, contents):
with open(file_path, "w") as file:
file.write(contents)
if __name__ == "__main__":
generate_templates()

View File

@ -0,0 +1 @@
.

View File

@ -89,6 +89,10 @@ data "google_compute_image" "zerologon-25" {
name = "zerologon-25" name = "zerologon-25"
project = local.monkeyzoo_project project = local.monkeyzoo_project
} }
data "google_compute_image" "drupal-28" {
name = "drupal-28"
project = local.monkeyzoo_project
}
data "google_compute_image" "island-linux-250" { data "google_compute_image" "island-linux-250" {
name = "island-linux-250" name = "island-linux-250"
project = local.monkeyzoo_project project = local.monkeyzoo_project

View File

@ -447,6 +447,21 @@ resource "google_compute_instance_from_template" "zerologon-25" {
} }
} }
resource "google_compute_instance_from_template" "drupal-28" {
name = "${local.resource_prefix}drupal-28"
source_instance_template = local.default_windows
boot_disk{
initialize_params {
image = data.google_compute_image.drupal-28.self_link
}
auto_delete = true
}
network_interface {
subnetwork="${local.resource_prefix}monkeyzoo-main"
network_ip="10.2.2.28"
}
}
resource "google_compute_instance_from_template" "island-linux-250" { resource "google_compute_instance_from_template" "island-linux-250" {
name = "${local.resource_prefix}island-linux-250" name = "${local.resource_prefix}island-linux-250"
machine_type = "n1-standard-2" machine_type = "n1-standard-2"

@ -1 +0,0 @@
Subproject commit 9de1e78ba475f925c66c5b645564ec9eb08e2309

View File

@ -1,5 +1,8 @@
# abstract, static method decorator # abstract, static method decorator
# noinspection PyPep8Naming # noinspection PyPep8Naming
from typing import List
class abstractstatic(staticmethod): class abstractstatic(staticmethod):
__slots__ = () __slots__ = ()
@ -8,3 +11,10 @@ class abstractstatic(staticmethod):
function.__isabstractmethod__ = True function.__isabstractmethod__ = True
__isabstractmethod__ = True __isabstractmethod__ = True
def get_value_from_dict(dict_data: dict, path: List[str]):
current_data = dict_data
for key in path:
current_data = current_data[key]
return current_data

View File

@ -3,7 +3,7 @@ import argparse
from pathlib import Path from pathlib import Path
MAJOR = "1" MAJOR = "1"
MINOR = "9" MINOR = "10"
PATCH = "0" PATCH = "0"
build_file_path = Path(__file__).parent.joinpath("BUILD") build_file_path = Path(__file__).parent.joinpath("BUILD")
with open(build_file_path, "r") as build_file: with open(build_file_path, "r") as build_file:

View File

@ -134,7 +134,9 @@ class MonkeyDrops(object):
'monkey_commandline': inner_monkey_cmdline} 'monkey_commandline': inner_monkey_cmdline}
monkey_process = subprocess.Popen(monkey_cmdline, shell=True, monkey_process = subprocess.Popen(monkey_cmdline, shell=True,
stdin=None, stdout=None, stderr=None, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True, creationflags=DETACHED_PROCESS) close_fds=True, creationflags=DETACHED_PROCESS)
LOG.info("Executed monkey process (PID=%d) with command line: %s", LOG.info("Executed monkey process (PID=%d) with command line: %s",
@ -145,6 +147,8 @@ class MonkeyDrops(object):
LOG.warning("Seems like monkey died too soon") LOG.warning("Seems like monkey died too soon")
def cleanup(self): def cleanup(self):
LOG.info("Cleaning up the dropper")
try: try:
if (self._config['source_path'].lower() != self._config['destination_path'].lower()) and \ if (self._config['source_path'].lower() != self._config['destination_path'].lower()) and \
os.path.exists(self._config['source_path']) and \ os.path.exists(self._config['source_path']) and \
@ -166,5 +170,7 @@ class MonkeyDrops(object):
LOG.debug("Dropper source file '%s' is marked for deletion on next boot", LOG.debug("Dropper source file '%s' is marked for deletion on next boot",
self._config['source_path']) self._config['source_path'])
T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send() T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send()
LOG.info("Dropper cleanup complete")
except AttributeError: except AttributeError:
LOG.error("Invalid configuration options. Failing") LOG.error("Invalid configuration options. Failing")

View File

@ -36,6 +36,7 @@ class DrupalExploiter(WebRCE):
exploit_config = super(DrupalExploiter, self).get_exploit_config() exploit_config = super(DrupalExploiter, self).get_exploit_config()
exploit_config['url_extensions'] = ['node/', # In Linux, no path is added exploit_config['url_extensions'] = ['node/', # In Linux, no path is added
'drupal/node/'] # However, Bitnami installations are under /drupal 'drupal/node/'] # However, Bitnami installations are under /drupal
exploit_config['dropper'] = True
return exploit_config return exploit_config
def add_vulnerable_urls(self, potential_urls, stop_checking=False): def add_vulnerable_urls(self, potential_urls, stop_checking=False):

View File

@ -252,9 +252,12 @@ class InfectionMonkey(object):
def collect_system_info_if_configured(self): def collect_system_info_if_configured(self):
LOG.debug("Calling for system info collection") LOG.debug("Calling for system info collection")
try:
system_info_collector = SystemInfoCollector() system_info_collector = SystemInfoCollector()
system_info = system_info_collector.get_info() system_info = system_info_collector.get_info()
SystemInfoTelem(system_info).send() SystemInfoTelem(system_info).send()
except Exception as e:
LOG.exception(f"Exception encountered during system info collection: {str(e)}")
def shutdown_by_not_alive_config(self): def shutdown_by_not_alive_config(self):
if not WormConfiguration.alive: if not WormConfiguration.alive:

View File

@ -1,24 +1,24 @@
# -*- mode: python -*- # -*- mode: python -*-
import os import os
import sys
import platform import platform
import sys
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
from PyInstaller.utils.hooks import collect_data_files
block_cipher = None block_cipher = None
def main(): def main():
print(collect_data_files('policyuniverse'))
a = Analysis(['main.py'], a = Analysis(['main.py'],
pathex=['..'], pathex=['..'],
hiddenimports=get_hidden_imports(), hiddenimports=get_hidden_imports(),
hookspath=['./pyinstaller_hooks'], hookspath=['./pyinstaller_hooks'],
runtime_hooks=None, runtime_hooks=None,
binaries=None, binaries=None,
datas=[ datas=[("../common/BUILD", "/common")],
("../common/BUILD", "/common")
],
excludes=None, excludes=None,
win_no_prefer_redirects=None, win_no_prefer_redirects=None,
win_private_assemblies=None, win_private_assemblies=None,
@ -48,7 +48,7 @@ def is_windows():
def is_32_bit(): def is_32_bit():
return sys.maxsize <= 2**32 return sys.maxsize <= 2 ** 32
def get_bin_folder(): def get_bin_folder():
@ -79,7 +79,10 @@ def get_linux_only_binaries():
def get_hidden_imports(): def get_hidden_imports():
return ['_cffi_backend', 'queue', '_mssql'] if is_windows() else ['_cffi_backend','_mssql'] imports = ['_cffi_backend', '_mssql']
if is_windows():
imports.append('queue')
return imports
def get_sc_binaries(): def get_sc_binaries():
@ -94,15 +97,15 @@ def get_traceroute_binaries():
def get_monkey_filename(): def get_monkey_filename():
name = 'monkey-' name = 'monkey-'
if is_windows(): if is_windows():
name = name+"windows-" name = name + "windows-"
else: else:
name = name+"linux-" name = name + "linux-"
if is_32_bit(): if is_32_bit():
name = name+"32" name = name + "32"
else: else:
name = name+"64" name = name + "64"
if is_windows(): if is_windows():
name = name+".exe" name = name + ".exe"
return name return name

View File

@ -43,8 +43,8 @@ class HTTPFinger(HostFinger):
LOG.info("Port %d is open on host %s " % (port[0], host)) LOG.info("Port %d is open on host %s " % (port[0], host))
break # https will be the same on the same port break # https will be the same on the same port
except Timeout: except Timeout:
pass LOG.debug(f"Timout while requesting headers from {url}")
except ConnectionError: # Someone doesn't like us except ConnectionError: # Someone doesn't like us
pass LOG.debug(f"Connection error while requesting headers from {url}")
return True return True

View File

@ -15,10 +15,6 @@ LOG = logging.getLogger(__name__)
__author__ = 'VakarisZ' __author__ = 'VakarisZ'
# Default commands for executing PBA file and then removing it
DEFAULT_LINUX_COMMAND = "chmod +x {0} ; {0} ; rm {0}"
DEFAULT_WINDOWS_COMMAND = "{0} & del {0}"
DIR_CHANGE_WINDOWS = 'cd %s & ' DIR_CHANGE_WINDOWS = 'cd %s & '
DIR_CHANGE_LINUX = 'cd %s ; ' DIR_CHANGE_LINUX = 'cd %s ; '
@ -31,30 +27,23 @@ class UsersPBA(PBA):
def __init__(self): def __init__(self):
super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION) super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION)
self.filename = '' self.filename = ''
if not is_windows_os(): if not is_windows_os():
# Add linux commands to PBA's # Add linux commands to PBA's
if WormConfiguration.PBA_linux_filename: if WormConfiguration.PBA_linux_filename:
self.filename = WormConfiguration.PBA_linux_filename
if WormConfiguration.custom_PBA_linux_cmd: if WormConfiguration.custom_PBA_linux_cmd:
# Add change dir command, because user will try to access his file # Add change dir command, because user will try to access his file
self.command = (DIR_CHANGE_LINUX % get_monkey_dir_path()) + WormConfiguration.custom_PBA_linux_cmd self.command = (DIR_CHANGE_LINUX % get_monkey_dir_path()) + WormConfiguration.custom_PBA_linux_cmd
self.filename = WormConfiguration.PBA_linux_filename
else:
file_path = os.path.join(get_monkey_dir_path(), WormConfiguration.PBA_linux_filename)
self.command = DEFAULT_LINUX_COMMAND.format(file_path)
self.filename = WormConfiguration.PBA_linux_filename
elif WormConfiguration.custom_PBA_linux_cmd: elif WormConfiguration.custom_PBA_linux_cmd:
self.command = WormConfiguration.custom_PBA_linux_cmd self.command = WormConfiguration.custom_PBA_linux_cmd
else: else:
# Add windows commands to PBA's # Add windows commands to PBA's
if WormConfiguration.PBA_windows_filename: if WormConfiguration.PBA_windows_filename:
self.filename = WormConfiguration.PBA_windows_filename
if WormConfiguration.custom_PBA_windows_cmd: if WormConfiguration.custom_PBA_windows_cmd:
# Add change dir command, because user will try to access his file # Add change dir command, because user will try to access his file
self.command = (DIR_CHANGE_WINDOWS % get_monkey_dir_path()) + WormConfiguration.custom_PBA_windows_cmd self.command = (DIR_CHANGE_WINDOWS % get_monkey_dir_path()) + WormConfiguration.custom_PBA_windows_cmd
self.filename = WormConfiguration.PBA_windows_filename
else:
file_path = os.path.join(get_monkey_dir_path(), WormConfiguration.PBA_windows_filename)
self.command = DEFAULT_WINDOWS_COMMAND.format(file_path)
self.filename = WormConfiguration.PBA_windows_filename
elif WormConfiguration.custom_PBA_windows_cmd: elif WormConfiguration.custom_PBA_windows_cmd:
self.command = WormConfiguration.custom_PBA_windows_cmd self.command = WormConfiguration.custom_PBA_windows_cmd

View File

@ -0,0 +1,152 @@
import pytest
from infection_monkey.post_breach.actions.users_custom_pba import (
DIR_CHANGE_LINUX, DIR_CHANGE_WINDOWS, UsersPBA)
MONKEY_DIR_PATH = "/dir/to/monkey/"
CUSTOM_LINUX_CMD = "command-for-linux"
CUSTOM_LINUX_FILENAME = "filename-for-linux"
CUSTOM_WINDOWS_CMD = "command-for-windows"
CUSTOM_WINDOWS_FILENAME = "filename-for-windows"
@pytest.fixture
def fake_monkey_dir_path(monkeypatch):
monkeypatch.setattr(
"infection_monkey.post_breach.actions.users_custom_pba.get_monkey_dir_path",
lambda: MONKEY_DIR_PATH,
)
@pytest.fixture
def set_os_linux(monkeypatch):
monkeypatch.setattr(
"infection_monkey.post_breach.actions.users_custom_pba.is_windows_os",
lambda: False,
)
@pytest.fixture
def set_os_windows(monkeypatch):
monkeypatch.setattr(
"infection_monkey.post_breach.actions.users_custom_pba.is_windows_os",
lambda: True,
)
@pytest.fixture
def mock_UsersPBA_linux_custom_file_and_cmd(
set_os_linux, fake_monkey_dir_path, monkeypatch
):
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.custom_PBA_linux_cmd",
CUSTOM_LINUX_CMD,
)
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.PBA_linux_filename",
CUSTOM_LINUX_FILENAME,
)
return UsersPBA()
def test_command_linux_custom_file_and_cmd(
mock_UsersPBA_linux_custom_file_and_cmd,
):
expected_command = f"cd {MONKEY_DIR_PATH} ; {CUSTOM_LINUX_CMD}"
assert mock_UsersPBA_linux_custom_file_and_cmd.command == expected_command
@pytest.fixture
def mock_UsersPBA_windows_custom_file_and_cmd(
set_os_windows, fake_monkey_dir_path, monkeypatch
):
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.custom_PBA_windows_cmd",
CUSTOM_WINDOWS_CMD,
)
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.PBA_windows_filename",
CUSTOM_WINDOWS_FILENAME,
)
return UsersPBA()
def test_command_windows_custom_file_and_cmd(
mock_UsersPBA_windows_custom_file_and_cmd,
):
expected_command = f"cd {MONKEY_DIR_PATH} & {CUSTOM_WINDOWS_CMD}"
assert mock_UsersPBA_windows_custom_file_and_cmd.command == expected_command
@pytest.fixture
def mock_UsersPBA_linux_custom_file(set_os_linux, fake_monkey_dir_path, monkeypatch):
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.custom_PBA_linux_cmd", None
)
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.PBA_linux_filename",
CUSTOM_LINUX_FILENAME,
)
return UsersPBA()
def test_command_linux_custom_file(mock_UsersPBA_linux_custom_file):
expected_command = ""
assert mock_UsersPBA_linux_custom_file.command == expected_command
@pytest.fixture
def mock_UsersPBA_windows_custom_file(
set_os_windows, fake_monkey_dir_path, monkeypatch
):
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.custom_PBA_windows_cmd", None
)
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.PBA_windows_filename",
CUSTOM_WINDOWS_FILENAME,
)
return UsersPBA()
def test_command_windows_custom_file(mock_UsersPBA_windows_custom_file):
expected_command = ""
assert mock_UsersPBA_windows_custom_file.command == expected_command
@pytest.fixture
def mock_UsersPBA_linux_custom_cmd(set_os_linux, fake_monkey_dir_path, monkeypatch):
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.custom_PBA_linux_cmd",
CUSTOM_LINUX_CMD,
)
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.PBA_linux_filename", None
)
return UsersPBA()
def test_command_linux_custom_cmd(mock_UsersPBA_linux_custom_cmd):
expected_command = CUSTOM_LINUX_CMD
assert mock_UsersPBA_linux_custom_cmd.command == expected_command
@pytest.fixture
def mock_UsersPBA_windows_custom_cmd(set_os_windows, fake_monkey_dir_path, monkeypatch):
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.custom_PBA_windows_cmd",
CUSTOM_WINDOWS_CMD,
)
monkeypatch.setattr(
"infection_monkey.config.WormConfiguration.PBA_windows_filename", None
)
return UsersPBA()
def test_command_windows_custom_cmd(mock_UsersPBA_windows_custom_cmd):
expected_command = CUSTOM_WINDOWS_CMD
assert mock_UsersPBA_windows_custom_cmd.command == expected_command

View File

@ -1,3 +1,4 @@
cryptography==2.5
WinSys-3.x>=0.5.2 WinSys-3.x>=0.5.2
cffi>=1.14 cffi>=1.14
ecdsa==0.15 ecdsa==0.15
@ -15,3 +16,6 @@ pypykatz==0.3.12
pysmb==1.2.5 pysmb==1.2.5
requests>=2.24 requests>=2.24
wmi==1.5.1 ; sys_platform == 'win32' wmi==1.5.1 ; sys_platform == 'win32'
urllib3==1.25.8
git+https://github.com/guardicode/ScoutSuite
simplejson

View File

@ -1,15 +0,0 @@
import pkgutil
import sys
from pathlib import PurePath
_scoutsuite_api_package = pkgutil.get_loader('common.cloud.scoutsuite.ScoutSuite.__main__')
def _add_scoutsuite_to_python_path():
scoutsuite_path = PurePath(_scoutsuite_api_package.path).parent.parent.__str__()
sys.path.append(scoutsuite_path)
# Add ScoutSuite to python path because this way
# we don't need to change any imports in ScoutSuite code
_add_scoutsuite_to_python_path()

View File

@ -1,5 +0,0 @@
import common.cloud.scoutsuite.ScoutSuite.api_run as scoutsuite_api
def run(*args, **kwargs):
return scoutsuite_api.run(*args, **kwargs)

View File

@ -1,8 +1,9 @@
import logging import logging
from typing import Union from typing import Union
import infection_monkey.system_info.collectors.scoutsuite_collector.scoutsuite_api as scoutsuite_api import ScoutSuite.api_run
from common.cloud.scoutsuite.ScoutSuite.providers.base.provider import BaseProvider from ScoutSuite.providers.base.provider import BaseProvider
from common.cloud.scoutsuite_consts import CloudProviders from common.cloud.scoutsuite_consts import CloudProviders
from common.utils.exceptions import ScoutSuiteScanError from common.utils.exceptions import ScoutSuiteScanError
from infection_monkey.config import WormConfiguration from infection_monkey.config import WormConfiguration
@ -22,7 +23,7 @@ def scan_cloud_security(cloud_type: CloudProviders):
def run_scoutsuite(cloud_type: str) -> Union[BaseProvider, dict]: def run_scoutsuite(cloud_type: str) -> Union[BaseProvider, dict]:
return scoutsuite_api.run(provider=cloud_type, return ScoutSuite.api_run.run(provider=cloud_type,
aws_access_key_id=WormConfiguration.aws_access_key_id, aws_access_key_id=WormConfiguration.aws_access_key_id,
aws_secret_access_key=WormConfiguration.aws_secret_access_key, aws_secret_access_key=WormConfiguration.aws_secret_access_key,
aws_session_token=WormConfiguration.aws_session_token) aws_session_token=WormConfiguration.aws_session_token)

View File

@ -1,5 +1,5 @@
import logging import logging
import os import subprocess
import sys import sys
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR
@ -46,16 +46,21 @@ class WindowsInfoCollector(InfoCollector):
return self.info return self.info
def get_installed_packages(self): def get_installed_packages(self):
LOG.info('getting installed packages') LOG.info('Getting installed packages')
self.info["installed_packages"] = os.popen("dism /online /get-packages").read()
self.info["installed_features"] = os.popen("dism /online /get-features").read() packages = subprocess.check_output("dism /online /get-packages", shell=True)
self.info["installed_packages"] = packages.decode('utf-8', errors='ignore')
features = subprocess.check_output("dism /online /get-features", shell=True)
self.info["installed_features"] = features.decode('utf-8', errors='ignore')
LOG.debug('Got installed packages') LOG.debug('Got installed packages')
def get_wmi_info(self): def get_wmi_info(self):
LOG.info('getting wmi info') LOG.info('Getting wmi info')
for wmi_class_name in WMI_CLASSES: for wmi_class_name in WMI_CLASSES:
self.info['wmi'][wmi_class_name] = WMIUtils.get_wmi_class(wmi_class_name) self.info['wmi'][wmi_class_name] = WMIUtils.get_wmi_class(wmi_class_name)
LOG.debug('finished get_wmi_info') LOG.debug('Finished get_wmi_info')
def get_mimikatz_info(self): def get_mimikatz_info(self):
LOG.info("Gathering mimikatz info") LOG.info("Gathering mimikatz info")

View File

@ -1,5 +1,5 @@
from common.cloud.scoutsuite.ScoutSuite.output.result_encoder import ScoutJsonEncoder from ScoutSuite.output.result_encoder import ScoutJsonEncoder
from common.cloud.scoutsuite.ScoutSuite.providers.base.provider import BaseProvider from ScoutSuite.providers.base.provider import BaseProvider
from common.common_consts.telem_categories import TelemCategoryEnum from common.common_consts.telem_categories import TelemCategoryEnum
from infection_monkey.telemetry.base_telem import BaseTelem from infection_monkey.telemetry.base_telem import BaseTelem

View File

@ -7,6 +7,7 @@ from werkzeug.exceptions import NotFound
import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.environment.environment_singleton as env_singleton
from common.common_consts.api_url_consts import T1216_PBA_FILE_DOWNLOAD_PATH from common.common_consts.api_url_consts import T1216_PBA_FILE_DOWNLOAD_PATH
from monkey_island.cc.resources.test.telemetry_test import TelemetryTest
from monkey_island.cc.resources.zero_trust.zero_trust_report import ZeroTrustReport from monkey_island.cc.resources.zero_trust.zero_trust_report import ZeroTrustReport
from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH
from monkey_island.cc.server_utils.custom_json_encoder import CustomJSONEncoder from monkey_island.cc.server_utils.custom_json_encoder import CustomJSONEncoder
@ -145,9 +146,11 @@ def init_api_resources(api):
api.add_resource(ScoutSuiteAuth, '/api/scoutsuite_auth/<string:provider>') api.add_resource(ScoutSuiteAuth, '/api/scoutsuite_auth/<string:provider>')
api.add_resource(AWSKeys, '/api/aws_keys') api.add_resource(AWSKeys, '/api/aws_keys')
# Resources used by black box tests
api.add_resource(MonkeyTest, '/api/test/monkey') api.add_resource(MonkeyTest, '/api/test/monkey')
api.add_resource(ClearCaches, '/api/test/clear_caches') api.add_resource(ClearCaches, '/api/test/clear_caches')
api.add_resource(LogTest, '/api/test/log') api.add_resource(LogTest, '/api/test/log')
api.add_resource(TelemetryTest, '/api/test/telemetry')
def init_app(mongo_url): def init_app(mongo_url):

View File

@ -0,0 +1,13 @@
import flask_restful
from bson import json_util
from flask import request
from monkey_island.cc.database import mongo
from monkey_island.cc.resources.auth.auth import jwt_required
class TelemetryTest(flask_restful.Resource):
@jwt_required
def get(self, **kw):
find_query = json_util.loads(request.args.get('find_query'))
return {'results': list(mongo.db.telemetry.find(find_query))}

View File

@ -4,7 +4,7 @@ from monkey_island.cc.services.config import ConfigService
__author__ = "VakarisZ" __author__ = "VakarisZ"
from monkey_island.cc.services.config_schema.config_value_paths import CURRENT_SERVER_PATH from common.config_value_paths import CURRENT_SERVER_PATH
class T1065(AttackTechnique): class T1065(AttackTechnique):

View File

@ -14,7 +14,7 @@ from monkey_island.cc.services.config_schema.config_schema import SCHEMA
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
from monkey_island.cc.services.config_schema.config_value_paths import (AWS_KEYS_PATH, EXPORT_MONKEY_TELEMS_PATH, from common.config_value_paths import (AWS_KEYS_PATH, EXPORT_MONKEY_TELEMS_PATH,
LM_HASH_LIST_PATH, NTLM_HASH_LIST_PATH, LM_HASH_LIST_PATH, NTLM_HASH_LIST_PATH,
PASSWORD_LIST_PATH, SSH_KEYS_PATH, PASSWORD_LIST_PATH, SSH_KEYS_PATH,
STARTED_ON_ISLAND_PATH, USER_LIST_PATH) STARTED_ON_ISLAND_PATH, USER_LIST_PATH)

View File

@ -159,7 +159,8 @@ INTERNAL = {
8080, 8080,
443, 443,
8008, 8008,
7001 7001,
9200
], ],
"description": "List of ports the monkey will check if are being used for HTTP" "description": "List of ports the monkey will check if are being used for HTTP"
}, },
@ -181,7 +182,6 @@ INTERNAL = {
443, 443,
8008, 8008,
3306, 3306,
9200,
7001, 7001,
8088 8088
], ],

View File

@ -11,33 +11,39 @@ MONKEY = {
"type": "object", "type": "object",
"properties": { "properties": {
"custom_PBA_linux_cmd": { "custom_PBA_linux_cmd": {
"title": "Linux post breach command", "title": "Linux post-breach command",
"type": "string", "type": "string",
"default": "", "default": "",
"description": "Linux command to be executed after breaching." "description": "Command to be executed after breaching. "
"Use this field to run custom commands or execute uploaded "
"files on exploited machines.\nExample: "
"\"chmod +x ./my_script.sh; ./my_script.sh ; rm ./my_script.sh\""
}, },
"PBA_linux_file": { "PBA_linux_file": {
"title": "Linux post breach file", "title": "Linux post-breach file",
"type": "string", "type": "string",
"format": "data-url", "format": "data-url",
"description": "File to be executed after breaching. " "description": "File to be uploaded after breaching. "
"If you want custom execution behavior, " "Use the 'Linux post-breach command' field to "
"specify it in 'Linux post breach command' field. " "change permissions, run, or delete the file. "
"Reference your file by filename." "Reference your file by filename."
}, },
"custom_PBA_windows_cmd": { "custom_PBA_windows_cmd": {
"title": "Windows post breach command", "title": "Windows post-breach command",
"type": "string", "type": "string",
"default": "", "default": "",
"description": "Windows command to be executed after breaching." "description": "Command to be executed after breaching. "
"Use this field to run custom commands or execute uploaded "
"files on exploited machines.\nExample: "
"\"my_script.bat & del my_script.bat\""
}, },
"PBA_windows_file": { "PBA_windows_file": {
"title": "Windows post breach file", "title": "Windows post-breach file",
"type": "string", "type": "string",
"format": "data-url", "format": "data-url",
"description": "File to be executed after breaching. " "description": "File to be uploaded after breaching. "
"If you want custom execution behavior, " "Use the 'Windows post-breach command' field to "
"specify it in 'Windows post breach command' field. " "change permissions, run, or delete the file. "
"Reference your file by filename." "Reference your file by filename."
}, },
"PBA_windows_filename": { "PBA_windows_filename": {

View File

@ -1,5 +1,5 @@
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.config_schema.config_value_paths import INACCESSIBLE_SUBNETS_PATH from common.config_value_paths import INACCESSIBLE_SUBNETS_PATH
def get_config_network_segments_as_subnet_groups(): def get_config_network_segments_as_subnet_groups():

View File

@ -12,7 +12,7 @@ from monkey_island.cc.database import mongo
from monkey_island.cc.models import Monkey from monkey_island.cc.models import Monkey
from monkey_island.cc.services.utils.network_utils import get_subnets, local_ip_addresses from monkey_island.cc.services.utils.network_utils import get_subnets, local_ip_addresses
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.config_schema.config_value_paths import (EXPLOITER_CLASSES_PATH, LOCAL_NETWORK_SCAN_PATH, from common.config_value_paths import (EXPLOITER_CLASSES_PATH, LOCAL_NETWORK_SCAN_PATH,
PASSWORD_LIST_PATH, SUBNET_SCAN_LIST_PATH, PASSWORD_LIST_PATH, SUBNET_SCAN_LIST_PATH,
USER_LIST_PATH) USER_LIST_PATH)
from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups

View File

@ -3,6 +3,7 @@ import json
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.models.zero_trust.scoutsuite_data_json import ScoutSuiteRawDataJson from monkey_island.cc.models.zero_trust.scoutsuite_data_json import ScoutSuiteRawDataJson
from monkey_island.cc.services.zero_trust.scoutsuite.consts.scoutsuite_findings_list import SCOUTSUITE_FINDINGS from monkey_island.cc.services.zero_trust.scoutsuite.consts.scoutsuite_findings_list import SCOUTSUITE_FINDINGS
from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICES
from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_parser import RuleParser from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_parser import RuleParser
from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_rule_service import ScoutSuiteRuleService from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_rule_service import ScoutSuiteRuleService
from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_zt_finding_service import ScoutSuiteZTFindingService from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_zt_finding_service import ScoutSuiteZTFindingService
@ -13,14 +14,14 @@ def process_scoutsuite_telemetry(telemetry_json):
telemetry_json['data'] = json.dumps(telemetry_json['data']) telemetry_json['data'] = json.dumps(telemetry_json['data'])
ScoutSuiteRawDataJson.add_scoutsuite_data(telemetry_json['data']) ScoutSuiteRawDataJson.add_scoutsuite_data(telemetry_json['data'])
scoutsuite_data = json.loads(telemetry_json['data'])['data'] scoutsuite_data = json.loads(telemetry_json['data'])['data']
create_scoutsuite_findings(scoutsuite_data) create_scoutsuite_findings(scoutsuite_data[SERVICES])
update_data(telemetry_json) update_data(telemetry_json)
def create_scoutsuite_findings(scoutsuite_data): def create_scoutsuite_findings(cloud_services: dict):
for finding in SCOUTSUITE_FINDINGS: for finding in SCOUTSUITE_FINDINGS:
for rule in finding.rules: for rule in finding.rules:
rule_data = RuleParser.get_rule_data(scoutsuite_data, rule) rule_data = RuleParser.get_rule_data(cloud_services, rule)
rule = ScoutSuiteRuleService.get_rule_from_rule_data(rule_data) rule = ScoutSuiteRuleService.get_rule_from_rule_data(rule_data)
ScoutSuiteZTFindingService.process_rule(finding, rule) ScoutSuiteZTFindingService.process_rule(finding, rule)

View File

@ -1,13 +0,0 @@
import pkgutil
import sys
from pathlib import PurePath
_scoutsuite_api_package = pkgutil.get_loader('common.cloud.scoutsuite.ScoutSuite.__main__')
def _add_scoutsuite_to_python_path():
scoutsuite_path = PurePath(_scoutsuite_api_package.path).parent.parent.__str__()
sys.path.append(scoutsuite_path)
_add_scoutsuite_to_python_path()

View File

@ -1,7 +1,6 @@
from enum import Enum from enum import Enum
import dpath.util from common.utils.code_utils import get_value_from_dict
from common.utils.exceptions import RulePathCreatorNotFound from common.utils.exceptions import RulePathCreatorNotFound
from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators_list import \ from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators_list import \
RULE_PATH_CREATORS_LIST RULE_PATH_CREATORS_LIST
@ -23,7 +22,7 @@ class RuleParser:
@staticmethod @staticmethod
def get_rule_data(scoutsuite_data: dict, rule_name: Enum) -> dict: def get_rule_data(scoutsuite_data: dict, rule_name: Enum) -> dict:
rule_path = RuleParser._get_rule_path(rule_name) rule_path = RuleParser._get_rule_path(rule_name)
return dpath.util.get(scoutsuite_data, rule_path) return get_value_from_dict(scoutsuite_data, rule_path)
@staticmethod @staticmethod
def _get_rule_path(rule_name: Enum): def _get_rule_path(rule_name: Enum):

View File

@ -3,7 +3,7 @@ from enum import Enum
from typing import List, Type from typing import List, Type
from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.rule_name_enum import RuleNameEnum from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.rule_name_enum import RuleNameEnum
from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import FINDINGS, SERVICES, SERVICE_TYPES from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import FINDINGS, SERVICE_TYPES
class AbstractRulePathCreator(ABC): class AbstractRulePathCreator(ABC):
@ -21,4 +21,4 @@ class AbstractRulePathCreator(ABC):
@classmethod @classmethod
def build_rule_path(cls, rule_name: Enum) -> List[str]: def build_rule_path(cls, rule_name: Enum) -> List[str]:
assert(rule_name in cls.supported_rules) assert(rule_name in cls.supported_rules)
return [SERVICES, cls.service_type.value, FINDINGS, rule_name.value] return [cls.service_type.value, FINDINGS, rule_name.value]

View File

@ -4,6 +4,7 @@ import pytest
from common.utils.exceptions import RulePathCreatorNotFound from common.utils.exceptions import RulePathCreatorNotFound
from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.ec2_rules import EC2Rules from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.ec2_rules import EC2Rules
from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICES
from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_parser import RuleParser from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_parser import RuleParser
from monkey_island.cc.services.zero_trust.test_common.raw_scoutsute_data import RAW_SCOUTSUITE_DATA from monkey_island.cc.services.zero_trust.test_common.raw_scoutsute_data import RAW_SCOUTSUITE_DATA
@ -28,9 +29,9 @@ EXPECTED_RESULT = {'description': 'Security Group Opens All Ports to All',
def test_get_rule_data(): def test_get_rule_data():
# Test proper parsing of the raw data to rule # Test proper parsing of the raw data to rule
results = RuleParser.get_rule_data(RAW_SCOUTSUITE_DATA, ALL_PORTS_OPEN) results = RuleParser.get_rule_data(RAW_SCOUTSUITE_DATA[SERVICES], ALL_PORTS_OPEN)
assert results == EXPECTED_RESULT assert results == EXPECTED_RESULT
with pytest.raises(RulePathCreatorNotFound): with pytest.raises(RulePathCreatorNotFound):
RuleParser.get_rule_data(RAW_SCOUTSUITE_DATA, ExampleRules.NON_EXSISTENT_RULE) RuleParser.get_rule_data(RAW_SCOUTSUITE_DATA[SERVICES], ExampleRules.NON_EXSISTENT_RULE)
pass pass

View File

@ -6,7 +6,7 @@ from common.cloud.scoutsuite_consts import CloudProviders
from common.utils.exceptions import InvalidAWSKeys from common.utils.exceptions import InvalidAWSKeys
from monkey_island.cc.server_utils.encryptor import encryptor from monkey_island.cc.server_utils.encryptor import encryptor
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.config_schema.config_value_paths import AWS_KEYS_PATH from common.config_value_paths import AWS_KEYS_PATH
def is_cloud_authentication_setup(provider: CloudProviders) -> Tuple[bool, str]: def is_cloud_authentication_setup(provider: CloudProviders) -> Tuple[bool, str]:
@ -14,7 +14,7 @@ def is_cloud_authentication_setup(provider: CloudProviders) -> Tuple[bool, str]:
if is_aws_keys_setup(): if is_aws_keys_setup():
return True, "AWS keys already setup." return True, "AWS keys already setup."
import common.cloud.scoutsuite.ScoutSuite.providers.aws.authentication_strategy as auth_strategy import ScoutSuite.providers.aws.authentication_strategy as auth_strategy
try: try:
profile = auth_strategy.AWSAuthenticationStrategy().authenticate() profile = auth_strategy.AWSAuthenticationStrategy().authenticate()
return True, f" Profile \"{profile.session.profile_name}\" is already setup. " return True, f" Profile \"{profile.session.profile_name}\" is already setup. "

View File

@ -6,7 +6,7 @@ import dpath.util
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.server_utils import encryptor from monkey_island.cc.server_utils import encryptor
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.config_schema.config_value_paths import AWS_KEYS_PATH from common.config_value_paths import AWS_KEYS_PATH
from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import is_aws_keys_setup from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import is_aws_keys_setup
from monkey_island.cc.test_common.fixtures import FixtureEnum from monkey_island.cc.test_common.fixtures import FixtureEnum

View File

@ -2,7 +2,7 @@
To profile specific methods on island a `@profile(sort_args=['cumulative'], print_args=[100])` To profile specific methods on island a `@profile(sort_args=['cumulative'], print_args=[100])`
decorator can be used. decorator can be used.
Use it as any other decorator. After decorated method is used, a file will appear in a Use it as a parameterised decorator(`@profile()`). After decorated method is used, a file will appear in a
directory provided in `profiler_decorator.py`. Filename describes the path of directory provided in `profiler_decorator.py`. Filename describes the path of
the method that was profiled. For example if method `monkey_island/cc/resources/netmap.get` the method that was profiled. For example if method `monkey_island/cc/resources/netmap.get`
was profiled, then the results of this profiling will appear in was profiled, then the results of this profiling will appear in

View File

@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"version": "1.9.0", "version": "1.10.0",
"name": "infection-monkey", "name": "infection-monkey",
"description": "Infection Monkey C&C UI", "description": "Infection Monkey C&C UI",
"scripts": { "scripts": {

View File

@ -43,7 +43,7 @@ class AdvancedMultiSelect extends React.Component {
infoPaneParams: getDefaultPaneParams( infoPaneParams: getDefaultPaneParams(
this.infoPaneRefString, this.infoPaneRefString,
this.registry, this.registry,
this.isUnsafeOptionSelected(this.props.value) this.isUnsafeOptionSelected(props.value)
) )
}; };
} }
@ -94,9 +94,11 @@ class AdvancedMultiSelect extends React.Component {
} }
setMasterCheckboxState(selectValues) { setMasterCheckboxState(selectValues) {
this.setState(() => ({ let newState = this.getMasterCheckboxState(selectValues);
masterCheckboxState: this.getMasterCheckboxState(selectValues)
})); if (newState != this.state.masterCheckboxState) {
this.setState({masterCheckboxState: newState});
}
} }
getMasterCheckboxState(selectValues) { getMasterCheckboxState(selectValues) {
@ -162,11 +164,12 @@ class AdvancedMultiSelect extends React.Component {
render() { render() {
const { const {
schema, autofocus,
id, id,
required,
multiple, multiple,
autofocus required,
schema,
value
} = this.props; } = this.props;
return ( return (
@ -179,7 +182,7 @@ class AdvancedMultiSelect extends React.Component {
<ChildCheckboxContainer id={id} multiple={multiple} required={required} <ChildCheckboxContainer id={id} multiple={multiple} required={required}
autoFocus={autofocus} isSafe={this.isSafe} autoFocus={autofocus} isSafe={this.isSafe}
onPaneClick={this.setPaneInfo} onCheckboxClick={this.onChildCheckboxClick} onPaneClick={this.setPaneInfo} onCheckboxClick={this.onChildCheckboxClick}
selectedValues={this.props.value} enumOptions={this.enumOptions}/> selectedValues={value} enumOptions={this.enumOptions}/>
<InfoPane title={this.state.infoPaneParams.title} <InfoPane title={this.state.infoPaneParams.title}
body={this.state.infoPaneParams.content} body={this.state.infoPaneParams.content}
@ -188,6 +191,10 @@ class AdvancedMultiSelect extends React.Component {
</div> </div>
); );
} }
componentDidUpdate(_prevProps) {
this.setMasterCheckboxState(this.props.value);
}
} }
export default AdvancedMultiSelect; export default AdvancedMultiSelect;

View File

@ -26,5 +26,6 @@ tqdm>=4.47
virtualenv>=20.0.26 virtualenv>=20.0.26
werkzeug>=1.0.1 werkzeug>=1.0.1
wheel>=0.34.2 wheel>=0.34.2
git+https://github.com/guardicode/ScoutSuite
pyjwt>=1.5.1 # not directly required, pinned by Snyk to avoid a vulnerability pyjwt==1.7 # not directly required, pinned by Snyk to avoid a vulnerability