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"]
path = docs/themes/learn
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 coverage # for code coverage
- pip install -r monkey/infection_monkey/requirements.txt # for unit tests
- pip install -r monkey/common/cloud/scoutsuite/requirements.txt
- pip install pipdeptree
# Fail builds on possible conflicting dependencies.
- pipdeptree --warn fail
@ -56,7 +55,7 @@ install:
script:
# Check Python code
## 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.
### --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"
INFECTION_MONKEY_DIR="$monkey_home/monkey/infection_monkey"
MONKEY_BIN_DIR="$INFECTION_MONKEY_DIR/bin"
SCOUTSUITE_DIR="$monkey_home/monkey/common/cloud/scoutsuite"
if ! has_sudo; then
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"
${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}
# Download binaries
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).
- [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 to reset the password?](#how-to-reset-the-password)
- [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)
- [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)
- [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**!
@ -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/).
- **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.

View File

@ -8,33 +8,48 @@ disableToc: false
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
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):
dependency problems - leaving unconfigured
Errors were encountered while processing:
gc-monkey-island
```
```sh
sudo apt update
sudo dpkg -i monkey_island.deb # this might print errors
```
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
dependencies, and then install the Monkey Island:
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
Errors were encountered while processing:
gc-monkey-island
```
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
dependencies, and then install the Monkey Island:
```sh
sudo apt install -f
```
```sh
sudo apt install -f
```
## 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:
```sh
sudo docker load -i dk.monkeyisland.1.9.0.tar
sudo docker pull mongo
sudo docker load -i dk.monkeyisland.1.10.0.tar
sudo docker pull mongo:4.2
sudo mkdir -p /var/monkey-mongo/data/db
sudo docker run --name monkey-mongo --network=host -v /var/monkey-mongo/data/db:/data/db -d mongo
sudo docker run --name monkey-island --network=host -d guardicore/monkey-island:1.9.0
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.10.0
```
## Upgrading

View File

@ -16,8 +16,9 @@ tags: ["setup", "vmware"]
1. Log in to the machine with the following credentials:
1. Username: **monkeyuser**
1. Password: **Noon.Earth.Always**
1. It's recommended you change the machine passwords by running the following
commands: `sudo passwd monkeyuser`, `sudo passwd root`.
1. For security purposes, it's recommended that you change the machine
passwords by running the following commands: `sudo passwd monkeyuser`, `sudo
passwd root`.
## 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
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
with the username `root` and the password `G3aJ9szrvkxTmfAG`. After logging
in, edit the interfaces file by entering the following command in the
with the username `monkeyuser` and the password `Noon.Earth.Always`. After logging
in, edit the Netplan configuration by entering the following command in the
prompt:
```sh
sudo nano /etc/network/interfaces
sudo nano /etc/netplan/00-installer-config.yaml
```
Change the lines:
Make the following changes:
```sh
auto ens160
iface ens160 inet dhcp
```diff
# This is the network config written by 'subiquity'
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:
```sh
auto ens160
iface ens160 inet static
address AAA.BBB.CCC.DDD
netmask XXX.XXX.XXX.XXX
gateway YYY.YYY.YYY.YYY
```
Replace `XXX.XXX.XXX.XXX` with the desired IP addess of the VM. Replace
`YYY.YYY.YYY.YYY` with the default gateway.
Save the changes then run the command:
```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
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
| 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 |
|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------|
| 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` |
| monkey-island-vmware.ova | OVA | 1.9.0 | `3861d46518e8a92e49992b26dbff9fe8e8a4ac5fd24d68e68b13e7fd3fa22247` |
| 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-32.exe | Windows Agent | 1.8.2 | `86a7d7065e73b795e38f2033be0c53f3ac808cc67478aed794a7a6c89123979f` |
| 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:
`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
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`.
### Running telemetry performance test

View File

@ -4,5 +4,5 @@ from abc import ABCMeta, abstractmethod
class Analyzer(object, metaclass=ABCMeta):
@abstractmethod
def analyze_test_results(self):
def analyze_test_results(self) -> bool:
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

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 envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.island_configs.config_template import ConfigTemplate
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Elastic(ConfigTemplate):
@ -10,5 +10,6 @@ class Elastic(ConfigTemplate):
config_values.update({
"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"]
})

View File

@ -1,9 +1,10 @@
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)

View File

@ -1,9 +1,10 @@
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.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):

View File

@ -1,9 +1,10 @@
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.update({

View File

@ -1,9 +1,10 @@
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.update({

View File

@ -1,9 +1,10 @@
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_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):
config_values = BaseTemplate.config_values
class Ssh(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update({
"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):
config_values = BaseTemplate.config_values
class Tunneling(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update({
"basic.exploiters.exploiter_classes": ["SmbExploiter",
@ -13,6 +16,8 @@ class Tunneling(BaseTemplate):
"10.2.1.10",
"10.2.0.11",
"10.2.0.12"],
"basic_network.scope.depth": 3,
"internal.general.keep_tunnel_open_time": 180,
"basic.credentials.exploit_password_list": ["Password1!",
"3Q=(Ge(+&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):
config_values = BaseTemplate.config_values
class WmiMimikatz(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update({
"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 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:

View File

@ -1,6 +1,7 @@
import json
import logging
from time import sleep
from typing import Union
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
MONKEY_TEST_ENDPOINT = 'api/test/monkey'
TELEMETRY_TEST_ENDPOINT = 'api/test/telemetry'
LOG_TEST_ENDPOINT = 'api/test/log'
LOGGER = logging.getLogger(__name__)
@ -67,6 +69,13 @@ class MonkeyIslandClient(object):
MonkeyIslandClient.form_find_query_for_request(query))
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):
response = self.requests.get(MONKEY_TEST_ENDPOINT,
MonkeyIslandClient.form_find_query_for_request(None))
@ -78,7 +87,7 @@ class MonkeyIslandClient(object):
return MonkeyIslandClient.get_test_query_results(response)
@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)}
@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 \
CommunicationAnalyzer
from envs.monkey_zoo.blackbox.analyzers.zerologon_analyzer import ZerologonAnalyzer
from envs.monkey_zoo.blackbox.island_client.island_config_parser import \
IslandConfigParser
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.island_configs.elastic import Elastic
from envs.monkey_zoo.blackbox.island_configs.hadoop import Hadoop
from envs.monkey_zoo.blackbox.island_configs.mssql import Mssql
from envs.monkey_zoo.blackbox.island_configs.performance import Performance
from envs.monkey_zoo.blackbox.island_configs.shellshock import ShellShock
from envs.monkey_zoo.blackbox.island_configs.smb_mimikatz import SmbMimikatz
from envs.monkey_zoo.blackbox.island_configs.smb_pth import SmbPth
from envs.monkey_zoo.blackbox.island_configs.ssh import Ssh
from envs.monkey_zoo.blackbox.island_configs.struts2 import Struts2
from envs.monkey_zoo.blackbox.island_configs.tunneling import Tunneling
from envs.monkey_zoo.blackbox.island_configs.weblogic import Weblogic
from envs.monkey_zoo.blackbox.island_configs.wmi_mimikatz import WmiMimikatz
from envs.monkey_zoo.blackbox.island_configs.wmi_pth import WmiPth
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.log_handlers.test_logs_handler import \
TestLogsHandler
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
@ -44,8 +47,10 @@ DEFAULT_TIMEOUT_SECONDS = 5*60
MACHINE_BOOTUP_WAIT_SECONDS = 30
GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'hadoop-2', 'hadoop-3', 'mssql-16',
'mimikatz-14', 'mimikatz-15', 'struts2-23', 'struts2-24', 'tunneling-9', 'tunneling-10',
'tunneling-11', 'tunneling-12', 'weblogic-18', 'weblogic-19', 'shellshock-8', 'zerologon-25']
'tunneling-11', 'tunneling-12', 'weblogic-18', 'weblogic-19', 'shellshock-8', 'zerologon-25',
'drupal-28']
LOG_DIR_PATH = "./logs"
logging.basicConfig(level=logging.INFO)
LOGGER = logging.getLogger(__name__)
@ -138,6 +143,9 @@ class TestMonkeyBlackbox:
def test_smb_pth(self, island_client):
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):
TestMonkeyBlackbox.run_exploitation_test(island_client, Elastic, "Elastic_exploiter")
@ -159,6 +167,22 @@ class TestMonkeyBlackbox:
def test_wmi_pth(self, island_client):
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.")
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"
project = local.monkeyzoo_project
}
data "google_compute_image" "drupal-28" {
name = "drupal-28"
project = local.monkeyzoo_project
}
data "google_compute_image" "island-linux-250" {
name = "island-linux-250"
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" {
name = "${local.resource_prefix}island-linux-250"
machine_type = "n1-standard-2"

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

View File

@ -1,5 +1,8 @@
# abstract, static method decorator
# noinspection PyPep8Naming
from typing import List
class abstractstatic(staticmethod):
__slots__ = ()
@ -8,3 +11,10 @@ class abstractstatic(staticmethod):
function.__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
MAJOR = "1"
MINOR = "9"
MINOR = "10"
PATCH = "0"
build_file_path = Path(__file__).parent.joinpath("BUILD")
with open(build_file_path, "r") as build_file:

View File

@ -134,7 +134,9 @@ class MonkeyDrops(object):
'monkey_commandline': inner_monkey_cmdline}
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)
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")
def cleanup(self):
LOG.info("Cleaning up the dropper")
try:
if (self._config['source_path'].lower() != self._config['destination_path'].lower()) 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",
self._config['source_path'])
T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send()
LOG.info("Dropper cleanup complete")
except AttributeError:
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['url_extensions'] = ['node/', # In Linux, no path is added
'drupal/node/'] # However, Bitnami installations are under /drupal
exploit_config['dropper'] = True
return exploit_config
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):
LOG.debug("Calling for system info collection")
system_info_collector = SystemInfoCollector()
system_info = system_info_collector.get_info()
SystemInfoTelem(system_info).send()
try:
system_info_collector = SystemInfoCollector()
system_info = system_info_collector.get_info()
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):
if not WormConfiguration.alive:

View File

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

View File

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

View File

@ -15,10 +15,6 @@ LOG = logging.getLogger(__name__)
__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_LINUX = 'cd %s ; '
@ -31,30 +27,23 @@ class UsersPBA(PBA):
def __init__(self):
super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION)
self.filename = ''
if not is_windows_os():
# Add linux commands to PBA's
if WormConfiguration.PBA_linux_filename:
self.filename = WormConfiguration.PBA_linux_filename
if WormConfiguration.custom_PBA_linux_cmd:
# 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.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:
self.command = WormConfiguration.custom_PBA_linux_cmd
else:
# Add windows commands to PBA's
if WormConfiguration.PBA_windows_filename:
self.filename = WormConfiguration.PBA_windows_filename
if WormConfiguration.custom_PBA_windows_cmd:
# 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.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:
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
cffi>=1.14
ecdsa==0.15
@ -15,3 +16,6 @@ pypykatz==0.3.12
pysmb==1.2.5
requests>=2.24
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
from typing import Union
import infection_monkey.system_info.collectors.scoutsuite_collector.scoutsuite_api as scoutsuite_api
from common.cloud.scoutsuite.ScoutSuite.providers.base.provider import BaseProvider
import ScoutSuite.api_run
from ScoutSuite.providers.base.provider import BaseProvider
from common.cloud.scoutsuite_consts import CloudProviders
from common.utils.exceptions import ScoutSuiteScanError
from infection_monkey.config import WormConfiguration
@ -22,10 +23,10 @@ def scan_cloud_security(cloud_type: CloudProviders):
def run_scoutsuite(cloud_type: str) -> Union[BaseProvider, dict]:
return scoutsuite_api.run(provider=cloud_type,
aws_access_key_id=WormConfiguration.aws_access_key_id,
aws_secret_access_key=WormConfiguration.aws_secret_access_key,
aws_session_token=WormConfiguration.aws_session_token)
return ScoutSuite.api_run.run(provider=cloud_type,
aws_access_key_id=WormConfiguration.aws_access_key_id,
aws_secret_access_key=WormConfiguration.aws_secret_access_key,
aws_session_token=WormConfiguration.aws_session_token)
def send_scoutsuite_run_results(run_results: BaseProvider):

View File

@ -1,5 +1,5 @@
import logging
import os
import subprocess
import sys
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR
@ -46,16 +46,21 @@ class WindowsInfoCollector(InfoCollector):
return self.info
def get_installed_packages(self):
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()
LOG.info('Getting installed packages')
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')
def get_wmi_info(self):
LOG.info('getting wmi info')
LOG.info('Getting wmi info')
for wmi_class_name in WMI_CLASSES:
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):
LOG.info("Gathering mimikatz info")

View File

@ -1,5 +1,5 @@
from common.cloud.scoutsuite.ScoutSuite.output.result_encoder import ScoutJsonEncoder
from common.cloud.scoutsuite.ScoutSuite.providers.base.provider import BaseProvider
from ScoutSuite.output.result_encoder import ScoutJsonEncoder
from ScoutSuite.providers.base.provider import BaseProvider
from common.common_consts.telem_categories import TelemCategoryEnum
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
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.server_utils.consts import MONKEY_ISLAND_ABS_PATH
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(AWSKeys, '/api/aws_keys')
# Resources used by black box tests
api.add_resource(MonkeyTest, '/api/test/monkey')
api.add_resource(ClearCaches, '/api/test/clear_caches')
api.add_resource(LogTest, '/api/test/log')
api.add_resource(TelemetryTest, '/api/test/telemetry')
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"
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):

View File

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

View File

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

View File

@ -11,33 +11,39 @@ MONKEY = {
"type": "object",
"properties": {
"custom_PBA_linux_cmd": {
"title": "Linux post breach command",
"title": "Linux post-breach command",
"type": "string",
"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": {
"title": "Linux post breach file",
"title": "Linux post-breach file",
"type": "string",
"format": "data-url",
"description": "File to be executed after breaching. "
"If you want custom execution behavior, "
"specify it in 'Linux post breach command' field. "
"description": "File to be uploaded after breaching. "
"Use the 'Linux post-breach command' field to "
"change permissions, run, or delete the file. "
"Reference your file by filename."
},
"custom_PBA_windows_cmd": {
"title": "Windows post breach command",
"title": "Windows post-breach command",
"type": "string",
"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": {
"title": "Windows post breach file",
"title": "Windows post-breach file",
"type": "string",
"format": "data-url",
"description": "File to be executed after breaching. "
"If you want custom execution behavior, "
"specify it in 'Windows post breach command' field. "
"description": "File to be uploaded after breaching. "
"Use the 'Windows post-breach command' field to "
"change permissions, run, or delete the file. "
"Reference your file by filename."
},
"PBA_windows_filename": {

View File

@ -1,5 +1,5 @@
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():

View File

@ -12,9 +12,9 @@ from monkey_island.cc.database import mongo
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.config import ConfigService
from monkey_island.cc.services.config_schema.config_value_paths import (EXPLOITER_CLASSES_PATH, LOCAL_NETWORK_SCAN_PATH,
PASSWORD_LIST_PATH, SUBNET_SCAN_LIST_PATH,
USER_LIST_PATH)
from common.config_value_paths import (EXPLOITER_CLASSES_PATH, LOCAL_NETWORK_SCAN_PATH,
PASSWORD_LIST_PATH, SUBNET_SCAN_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.node import NodeService
from monkey_island.cc.services.reporting.pth_report import PTHReportService

View File

@ -3,6 +3,7 @@ import json
from monkey_island.cc.database import mongo
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.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.scoutsuite_rule_service import ScoutSuiteRuleService
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'])
ScoutSuiteRawDataJson.add_scoutsuite_data(telemetry_json['data'])
scoutsuite_data = json.loads(telemetry_json['data'])['data']
create_scoutsuite_findings(scoutsuite_data)
create_scoutsuite_findings(scoutsuite_data[SERVICES])
update_data(telemetry_json)
def create_scoutsuite_findings(scoutsuite_data):
def create_scoutsuite_findings(cloud_services: dict):
for finding in SCOUTSUITE_FINDINGS:
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)
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
import dpath.util
from common.utils.code_utils import get_value_from_dict
from common.utils.exceptions import RulePathCreatorNotFound
from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators_list import \
RULE_PATH_CREATORS_LIST
@ -23,7 +22,7 @@ class RuleParser:
@staticmethod
def get_rule_data(scoutsuite_data: dict, rule_name: Enum) -> dict:
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
def _get_rule_path(rule_name: Enum):

View File

@ -3,7 +3,7 @@ from enum import Enum
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.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):
@ -21,4 +21,4 @@ class AbstractRulePathCreator(ABC):
@classmethod
def build_rule_path(cls, rule_name: Enum) -> List[str]:
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 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.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():
# 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
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

View File

@ -6,7 +6,7 @@ from common.cloud.scoutsuite_consts import CloudProviders
from common.utils.exceptions import InvalidAWSKeys
from monkey_island.cc.server_utils.encryptor import encryptor
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]:
@ -14,7 +14,7 @@ def is_cloud_authentication_setup(provider: CloudProviders) -> Tuple[bool, str]:
if is_aws_keys_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:
profile = auth_strategy.AWSAuthenticationStrategy().authenticate()
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.server_utils import encryptor
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.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])`
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
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

View File

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

View File

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

View File

@ -26,5 +26,6 @@ tqdm>=4.47
virtualenv>=20.0.26
werkzeug>=1.0.1
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