Compare commits

..

1 Commits

Author SHA1 Message Date
Mike Salvatore 376559bb77 Build: Uninstall pipenv, pip, and setuptools from AppImage
Once the dependencies are installed, Pipenv, pip, and setuptools are no
longer needed. They take up a combined 45MB of space.
2022-04-28 12:15:46 -04:00
924 changed files with 16629 additions and 39571 deletions

View File

@ -1,16 +0,0 @@
---
name: "🏗Refactor"
about: Refactor existing code
title: ''
labels: Refactor
assignees: ''
---
# Refactor
## Component(s) to be refactored
-
## Explanation

View File

@ -1,8 +0,0 @@
---
name: "📒Blank"
about: A blank issue for anything not covered by another template
title: ''
labels:
assignees: ''
---

View File

@ -4,5 +4,5 @@ contact_links:
url: https://join.slack.com/t/infectionmonkey/shared_invite/enQtNDU5MjAxMjg1MjU1LWM0NjVmNWE2ZTMzYzAxOWJiYmMxMzU0NWU3NmUxYjcyNjk0YWY2MDkwODk4NGMyNDU4NzA4MDljOWNmZWViNDU url: https://join.slack.com/t/infectionmonkey/shared_invite/enQtNDU5MjAxMjg1MjU1LWM0NjVmNWE2ZTMzYzAxOWJiYmMxMzU0NWU3NmUxYjcyNjk0YWY2MDkwODk4NGMyNDU4NzA4MDljOWNmZWViNDU
about: Our community Slack channel - you can ask questions or suggest things here. about: Our community Slack channel - you can ask questions or suggest things here.
- name: FAQs - name: FAQs
url: https://www.guardicore.com/infectionmonkey/docs/faq/ url: https://www.guardicore.com/infectionmonkey/faq/
about: Frequently Asked Questions - if you have a question, see if we've already answered it! about: Frequently Asked Questions - if you have a question, see if we've already answered it!

View File

@ -17,3 +17,7 @@ Add any further explanations here.
* [ ] Have you successfully tested your changes locally? Elaborate: * [ ] Have you successfully tested your changes locally? Elaborate:
> Tested by {Running the Monkey locally with relevant config/running Island/...} > Tested by {Running the Monkey locally with relevant config/running Island/...}
* [ ] If applicable, add screenshots or log transcripts of the feature working * [ ] If applicable, add screenshots or log transcripts of the feature working
## Explain Changes
Are the commit messages enough? If not, elaborate.

5
.gitignore vendored
View File

@ -59,7 +59,7 @@ coverage.xml
*.log *.log
# Sphinx documentation # Sphinx documentation
/monkey/monkey_island/docs/source/_autosummary docs/_build/
# PyBuilder # PyBuilder
target/ target/
@ -104,6 +104,3 @@ venv/
# mypy # mypy
.mypy_cache .mypy_cache
# MacOS
.DS_Store

View File

@ -35,17 +35,6 @@ repos:
hooks: hooks:
- id: eslint - id: eslint
args: ["monkey/monkey_island/cc/ui/src/", "--fix", "--max-warnings=0"] args: ["monkey/monkey_island/cc/ui/src/", "--fix", "--max-warnings=0"]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.971
hooks:
- id: mypy
additional_dependencies: [types-ipaddress, types-paramiko, types-python-dateutil, types-requests]
exclude: "vulture_allowlist.py"
args: [--ignore-missing-imports]
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.7.2
hooks:
- id: shellcheck
- repo: local - repo: local
hooks: hooks:
- id: pytest - id: pytest

View File

@ -0,0 +1,52 @@
{
"id": "JFXftJml8DpmuCPBA9rL",
"name": "Add details about your new PBA",
"task": {
"dod": "You should add your new PBA's details to the configuration.",
"tests": [],
"hints": [
"Have a look at the details of the other techniques."
]
},
"content": [
{
"type": "text",
"text": "In order to make sure that the new `ScheduleJobs` PBA is shown in the configuration on the Monkey Island, you need to add its details to the configuration file(s). <br><br>\n\nSince this particular PBA is related to the MITRE techniques [T1168](https://attack.mitre.org/techniques/T1168) and [T1053](https://attack.mitre.org/techniques/T1053), make sure to link the PBA with these techniques in the configuration as well. <br><br>\n\nEach part of the configuration has an important role \n- *enum* — contains the relevant PBA's class name(s)\n- *title* — holds the name of the PBA which is displayed in the configuration on the Monkey Island\n- *info* — consists of an elaboration on the PBA's working which is displayed in the configuration on the Monkey Island\n- *attack_techniques* — has the IDs of the MITRE techniques associated with the PBA\n\n## Manual test \nOnce you think you're done...\n- Run the Monkey Island\n- You should be able to see your new PBA under the \"Monkey\" tab in the configuration, along with its information when you click on it\n\n<img src=\"https://i.imgur.com/a5VSkL5.gif\" height=400>"
},
{
"type": "snippet",
"lines": [
" \"Removes the file afterwards.\",",
" \"attack_techniques\": [\"T1166\"],",
" },",
"* {",
"+ # Swimmer: ADD DETAILS HERE!",
"* \"type\": \"string\",",
"* \"enum\": [\"ScheduleJobs\"],",
"* \"title\": \"Job Scheduling\",",
"* \"safe\": True,",
"* \"info\": \"Attempts to create a scheduled job on the system and remove it.\",",
"* \"attack_techniques\": [\"T1168\", \"T1053\"],",
"* },",
" {",
" \"type\": \"string\",",
" \"enum\": [\"Timestomping\"],"
],
"firstLineNumber": 52,
"path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"comments": []
},
{
"type": "text",
"text": "- The PBA details in this file are reflected on the Monkey Island in the PBA configuration.\n- PBAs are also linked to the relevant MITRE techniques in this file, whose results can then be seen in the MITRE ATT&CK report on the Monkey Island."
}
],
"symbols": {},
"file_version": "2.0.3",
"meta": {
"app_version": "0.5.7-0",
"file_blobs": {
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "7d62ac36e875ca3c249d808250cb3268e4d3d68d"
}
}
}

View File

@ -0,0 +1,90 @@
{
"id": "afMu3y3ny5lnrYFWl3EI",
"name": "Add a new Post Breach Action (PBA)",
"task": {
"dod": "You should add a new PBA to the Monkey which discovers all user accounts on the machine.",
"tests": [],
"hints": [
"See `ScheduleJobs` PBA for an example of a PBA which only uses shell commands.",
"Make sure to add the PBA to the configuration as well.",
"MITRE ATT&CK technique T1087 articulates that adversaries may attempt to get a listing of accounts on a system or within an environment which can help them determine which accounts can aid in follow-on behavior. Therefore, the AccountDiscovery PBA is relevant to it which will enable the ATT&CK technique and show it in ATT&CK report."
]
},
"content": [
{
"type": "text",
"text": "Read our [documentation](https://www.guardicore.com/infectionmonkey/docs/development/adding-post-breach-actions/) about adding a new PBA.\n\nAfter that we want you to add the AccountDiscovery PBA. The commands that add users for Windows and Linux can be retrieved from \\`get\\_commands\\_to\\_discover\\_accounts\\` — make sure you see how to use this function correctly.\n\nNote that the PBA should impact the T1087 MITRE technique as well.\n\n**Manual test to confirm**\n--------------------------\n\n1. Run the Monkey Island.\n \n2. Make sure your new PBA is enabled by default in the config. For this test, disable network scanning, exploiting, and all other PBAs.\n \n3. Run the Monkey Agent.\n \n4. See the PBA in the security report and in the MITRE report under the relevant technique."
},
{
"type": "snippet",
"lines": [
" POST_BREACH_JOB_SCHEDULING = \"Schedule jobs\"",
" POST_BREACH_TIMESTOMPING = \"Modify files' timestamps\"",
" POST_BREACH_SIGNED_SCRIPT_PROXY_EXEC = \"Signed script proxy execution\"",
"*POST_BREACH_ACCOUNT_DISCOVERY = \"Account discovery\"",
"+# SWIMMER: Put the new const here!",
" POST_BREACH_CLEAR_CMD_HISTORY = \"Clear command history\""
],
"firstLineNumber": 7,
"path": "monkey/common/common_consts/post_breach_consts.py",
"comments": []
},
{
"type": "snippet",
"lines": [
" ",
" class AccountDiscovery(PBA):",
" def __init__(self, telemetry_messenger: ITelemetryMessenger):",
"* linux_cmds, windows_cmds = get_commands_to_discover_accounts()",
"+ # SWIMMER: Implement here!",
"* super().__init__(",
"+ pass",
"* telemetry_messenger,",
"* POST_BREACH_ACCOUNT_DISCOVERY,",
"* linux_cmd=\" \".join(linux_cmds),",
"* windows_cmd=windows_cmds,",
"* )"
],
"firstLineNumber": 8,
"path": "monkey/infection_monkey/post_breach/actions/discover_accounts.py",
"comments": []
},
{
"type": "snippet",
"lines": [
" \"with the help of a pre-existing signed script.\",",
" \"attack_techniques\": [\"T1216\"],",
" },",
"* {",
"+ # SWIMMER: Add details here!",
"* \"type\": \"string\",",
"* \"enum\": [\"AccountDiscovery\"],",
"* \"title\": \"Account Discovery\",",
"* \"safe\": True,",
"* \"info\": \"Attempts to get a listing of user accounts on the system.\",",
"* \"attack_techniques\": [\"T1087\"],",
"* },",
" {",
" \"type\": \"string\",",
" \"enum\": [\"ClearCommandHistory\"],"
],
"firstLineNumber": 78,
"path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"comments": []
},
{
"type": "text",
"text": "Many PBAs use shell commands or scripts — see `Timestomping` and `AccountDiscovery`.\n\nOn the other hand, some are less straightforward. You can override functions and implement new classes depending on what is required, to implement complicated PBAs — see `SignedScriptProxyExecution` and `ModifyShellStartupFiles`. \n \n\nThis PBA, along with the others, will run on a system after it has been breached. The purpose of this code is to test whether target systems allow attackers to gather details about all the user accounts that are present on a system or in an environment."
}
],
"symbols": {},
"file_version": "2.0.3",
"meta": {
"app_version": "0.6.6-2",
"file_blobs": {
"monkey/common/common_consts/post_breach_consts.py": "19b6c4f19b7223f115976a0050ca04ab97e52f8e",
"monkey/infection_monkey/post_breach/actions/discover_accounts.py": "a153cf5b6185c9771414fc5ae49d441efc7294b6",
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "d6831ed63b17f327d719a05840d7e51202fa5ccb"
}
}
}

View File

@ -4,137 +4,95 @@
group: travis_latest group: travis_latest
branches: language: python
only:
- develop
- master
- fix-travis
jobs: env:
include: - PIP_CACHE_DIR=$HOME/.cache/pip PIPENV_CACHE_DIR=$HOME/.cache/pipenv LIBSODIUM_MAKE_ARGS=-j8
- name: "Monkey Linux"
language: python
python:
- 3.7
os: linux
dist: focal
vm:
size: x-large
env:
- PIP_CACHE_DIR=$HOME/.cache/pip PIPENV_CACHE_DIR=$HOME/.cache/pipenv LIBSODIUM_MAKE_ARGS=-j8
cache: cache:
- pip: true - pip: true
- npm: true - npm: true
- directories: - directories:
- "$HOME/.npm" - "$HOME/.npm"
- $PIP_CACHE_DIR - $PIP_CACHE_DIR
- $PIPENV_CACHE_DIR - $PIPENV_CACHE_DIR
install: python:
# Python - 3.7
- nproc
- pip install pip --upgrade
- pipenv --version
# Install island and monkey requirements as they are needed by UT's
- pushd monkey/monkey_island
- pipenv sync --dev # This installs dependencies from lock
- popd
- pushd monkey/infection_monkey
- pipenv sync --dev # This installs dependencies from lock
- popd
# node + npm + eslint os: linux
- node --version vm:
- npm --version size: x-large
- nvm --version
- nvm install 16
- nvm use node
- npm i -g eslint
- node --version
- npm --version
# hugo (for documentation)
- curl -L https://github.com/gohugoio/hugo/releases/download/v0.92.0/hugo_0.92.0_Linux-64bit.tar.gz --output hugo.tar.gz
# print hugo version (useful for debugging documentation build errors)
- tar -zxf hugo.tar.gz
- ./hugo version
script: install:
# check python code # Python
## check syntax errors and fail the build if any are found. - nproc
- flake8 . - pip install pipenv --upgrade
## check import order # Install island and monkey requirements as they are needed by UT's
- python -m isort ./monkey --check-only - pushd monkey/monkey_island
- pipenv sync --dev # This installs dependencies from lock
- popd
- pushd monkey/infection_monkey
- pipenv sync --dev # This installs dependencies from lock
- popd
## check that all python is properly formatted. fail otherwise. # node + npm + eslint
- python -m black --check . - node --version
- npm --version
- nvm --version
- nvm install 16
- nvm use node
- npm i -g eslint
- node --version
- npm --version
## check that there is no dead python code # hugo (for documentation)
- python -m vulture . - curl -L https://github.com/gohugoio/hugo/releases/download/v0.92.0/hugo_0.92.0_Linux-64bit.tar.gz --output hugo.tar.gz
# print hugo version (useful for debugging documentation build errors)
- tar -zxf hugo.tar.gz
- ./hugo version
## run unit tests and generate coverage data script:
- cd monkey # this is our source dir # Check Python code
- pip install pytest-xdist ## Check syntax errors and fail the build if any are found.
- python -m pytest -n auto --dist loadscope --cov=. # have to use `python -m pytest` instead of `pytest` to add "{$builddir}/monkey/monkey" to sys.path. - flake8 .
# check js code. the npm install must happen after the flake8 because the node_modules folder will cause a lot of errors. ## Check import order
- cd monkey_island/cc/ui - python -m isort ./monkey --check-only
- npm ci # see https://docs.npmjs.com/cli/ci.html
- eslint ./src --quiet # test for errors
- JS_WARNINGS_AMOUNT_UPPER_LIMIT=0
- eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT # test for max warnings
# build documentation ## Check that all python is properly formatted. Fail otherwise.
- cd $TRAVIS_BUILD_DIR/docs - python -m black --check .
- ../hugo --verbose --environment staging
# verify swimm ## Check that there is no dead python code
- cd $TRAVIS_BUILD_DIR - python -m vulture .
- curl -l https://releases.swimm.io/ci/latest/packed-swimm-linux-cli --output swimm-cli
- chmod u+x swimm-cli
- ./swimm-cli --version
- ./swimm-cli verify
after_success: ## Run unit tests and generate coverage data
# Upload code coverage results to codecov.io, see https://github.com/codecov/codecov-bash for more information - cd monkey # This is our source dir
- bash <(curl -s https://codecov.io/bash) - pip install pytest-xdist
- python -m pytest -n auto --cov=. # Have to use `python -m pytest` instead of `pytest` to add "{$builddir}/monkey/monkey" to sys.path.
- name: "Monkey Windows" # Check JS code. The npm install must happen AFTER the flake8 because the node_modules folder will cause a lot of errors.
language: bash - cd monkey_island/cc/ui
os: windows - npm ci # See https://docs.npmjs.com/cli/ci.html
vm: - eslint ./src --quiet # Test for errors
size: x-large - JS_WARNINGS_AMOUNT_UPPER_LIMIT=0
before_install: - eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT # Test for max warnings
- choco install python --version=3.7.9
- python -m pip install -U pip setuptools virtualenv
- python -m virtualenv $HOME/venv
- source $HOME/venv/Scripts/activate
env:
PATH=/c/Python37:/c/Python37/Scripts:$PATH
cache:
pip: true
directories:
- $LOCALAPPDATA/pip/Cache
- $LOCALAPPDATA/pipenv/Cache
install:
# Python
- nproc
- pip install pipenv==2022.7.4
# Install island and monkey requirements as they are needed by UT's
- pushd monkey/monkey_island
- pipenv sync --dev # This installs dependencies from lock
- popd
- pushd monkey/infection_monkey
- pipenv sync --dev # This installs dependencies from lock
- popd
script: # Build documentation
## run unit tests and generate coverage data - cd $TRAVIS_BUILD_DIR/docs
- cd monkey # this is our source dir - ../hugo --verbose --environment staging
- pip install pytest-xdist
- python -m pytest -n auto --dist loadscope
# verify swimm
- cd $TRAVIS_BUILD_DIR
- curl -L https://releases.swimm.io/ci/latest/packed-swimm-linux-cli --output swimm-cli
- chmod u+x swimm-cli
- ./swimm-cli --version
- ./swimm-cli verify
after_success:
# Upload code coverage results to codecov.io, see https://github.com/codecov/codecov-bash for more information
- bash <(curl -s https://codecov.io/bash)
notifications: notifications:
slack: # Notify to slack slack: # Notify to slack

View File

@ -10,23 +10,10 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- credentials.json file for storing Monkey Island user login information. #1206 - credentials.json file for storing Monkey Island user login information. #1206
- "GET /api/propagation-credentials/<string:guid>" endpoint for agents to - "GET /api/propagation-credentials/<string:guid>" endpoint for agents to
retrieve updated credentials from the Island. #1538 retrieve updated credentials from the Island. #1538
- "GET /api/island/ip-addresses" endpoint to get IP addresses of the Island server
network interfaces. #1996
- SSHCollector as a configurable System info Collector. #1606 - SSHCollector as a configurable System info Collector. #1606
- deployment_scrips/install-infection-monkey-service.sh to install an AppImage - deployment_scrips/install-infection-monkey-service.sh to install an AppImage
as a service. #1552 as a service. #1552
- The ability to download the Monkey Island logs from the Infection Map page. #1640 - The ability to download the Monkey Island logs from the Infection Map page. #1640
- `/api/reset-agent-configuration` endpoint. #2036
- `/api/clear-simulation-data` endpoint. #2036
- `/api/registration-status` endpoint. #2149
- authentication to `/api/island/version`. #2109
- `/api/agent-events` endpoint. #2155, #2300
- The ability to customize the file extension used by ransomware when
encrypting files. #1242
- `/api/agents` endpoint. #2362
- `/api/agent-signals` endpoint. #2261
- `/api/agent-logs/<uuid:agent_id>` endpoint. #2274
- `/api/machines` endpoint. #2362
### Changed ### Changed
- Reset workflow. Now it's possible to delete data gathered by agents without - Reset workflow. Now it's possible to delete data gathered by agents without
@ -46,29 +33,6 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- The "/api/netmap/nodeStates" endpoint to "/api/netmap/node-states". #1888 - The "/api/netmap/nodeStates" endpoint to "/api/netmap/node-states". #1888
- All "/api/monkey_control" endpoints to "/api/monkey-control". #1888 - All "/api/monkey_control" endpoints to "/api/monkey-control". #1888
- All "/api/monkey" endpoints to "/api/agent". #1888 - All "/api/monkey" endpoints to "/api/agent". #1888
- Analytics and version update queries are sent separately instead of just one query. #2165
- Update MongoDB version to 4.4.x. #1924
- Endpoint to get agent binaries from "/api/agent/download/<string:os>" to
"/api/agent-binaries/<string:os>". #1978
- Depth flag (-d) on the agent now acts the way you would expect(it represents
the current depth of the agent, not hops remaining). #2033
- Agent configuration structure. #1996, #1998, #1961, #1997, #1994, #1741,
#1761, #1695, #1605, #2028, #2003
- `/api/island-mode` to accept and return new "unset" mode. #2036
- `/api/version-update` to `api/island/version`. #2109
- `/api/island-mode` to `/api/island/mode`. #2106
- `/api/log/island/download` endpoint to `/api/island/log`. #2107
- `/api/auth` endpoint to `/api/authenticate`. #2105
- `/api/registration` endpoint to `/api/register`. #2105
- `/api/file-upload` endpoit to `/api/pba/upload`. #2154
- Improved the speed of ransomware encryption by 2-3x. #2123
- "-s/--server" to "-s/--servers". #2216
- "-s/--servers" accepts list of servers separated by comma. #2216
- Tunneling to relays to provide better firewall evasion, faster Island
connection times, unlimited hops, and a more resilient way for agents to call
home. #2216, #1583
- "/api/monkey-control/stop-all-agents" to "/api/agent-signals/terminate-all-agents". #2261
- "Local network scan" option to "Scan Agent's networks". #2299
### Removed ### Removed
- VSFTPD exploiter. #1533 - VSFTPD exploiter. #1533
@ -107,16 +71,6 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- The /api/t1216-pba/download endpoint. #1864 - The /api/t1216-pba/download endpoint. #1864
- Island log download button from "Telemetries"(previously called "Logs") page. #1640 - Island log download button from "Telemetries"(previously called "Logs") page. #1640
- "/api/client-monkey" endpoint. #1889 - "/api/client-monkey" endpoint. #1889
- "+dev" from version numbers. #1553
- agent's "--config" argument. #906
- Option to export monkey telemetries. #1998
- "/api/configuration/import" endpoint. #2002
- "/api/configuration/export" endpoint. #2002
- "/api/island-configuration" endpoint. #2003
- "-t/--tunnel" from agent command line arguments. #2216
- "/api/monkey-control/neets-to-stop". #2261
- "GET /api/test/monkey" endpoint. #2269
- "GET /api/test/log" endpoint. #2269
### Fixed ### Fixed
- A bug in network map page that caused delay of telemetry log loading. #1545 - A bug in network map page that caused delay of telemetry log loading. #1545
@ -130,8 +84,6 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- A bug where windows executable was not self deleting. #1763 - A bug where windows executable was not self deleting. #1763
- Incorrect line number in the telemetry overview window on the Map page. #1850 - Incorrect line number in the telemetry overview window on the Map page. #1850
- Automatic jumping to the bottom in the telemetry overview windows. #1850 - Automatic jumping to the bottom in the telemetry overview windows. #1850
- 2-second delay when the Island server starts, and it's not running on AWS. #1636
- Malformed MSSQL agent launch command. #2018
### Security ### Security
- Change SSH exploiter so that it does not set the permissions of the agent - Change SSH exploiter so that it does not set the permissions of the agent

View File

@ -1,7 +1,7 @@
# Infection Monkey # Infection Monkey
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/guardicore/monkey)](https://github.com/guardicore/monkey/releases) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/guardicore/monkey)](https://github.com/guardicore/monkey/releases)
[![Build Status](https://app.travis-ci.com/guardicore/monkey.svg?branch=develop)](https://app.travis-ci.com/guardicore/monkey) [![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=develop)](https://travis-ci.com/guardicore/monkey)
[![codecov](https://codecov.io/gh/guardicore/monkey/branch/develop/graph/badge.svg)](https://codecov.io/gh/guardicore/monkey) [![codecov](https://codecov.io/gh/guardicore/monkey/branch/develop/graph/badge.svg)](https://codecov.io/gh/guardicore/monkey)
![GitHub stars](https://img.shields.io/github/stars/guardicore/monkey) ![GitHub stars](https://img.shields.io/github/stars/guardicore/monkey)
@ -18,16 +18,17 @@ The Infection Monkey is comprised of two parts:
* **Monkey** - A tool which infects other machines and propagates to them. * **Monkey** - A tool which infects other machines and propagates to them.
* **Monkey Island** - A dedicated server to control and visualize the Infection Monkey's progress inside the data center. * **Monkey Island** - A dedicated server to control and visualize the Infection Monkey's progress inside the data center.
To read more about the Monkey, visit [akamai.com/infectionmonkey](https://www.akamai.com/infectionmonkey). To read more about the Monkey, visit [infectionmonkey.com](https://infectionmonkey.com).
## 💥 We're Hiring 💥 ## 💥 We're Hiring 💥
We are looking for a software engineering manager with a passion for UX and We are looking for a senior developer with a passion for cybersecurity to join
cybersecurity to join the Infection Monkey development team. This is a remote the Infection Monkey development team. This is a remote position and is open
position and is open anywhere in Israel. You can learn more about Infection anywhere in the US Eastern timezone. If you're excited about Infection Monkey,
Monkey on our [website](https://www.akamai.com/infectionmonkey). we want to see your resume. You can learn more about Infection Monkey on our
[website](https://www.guardicore.com/infectionmonkey/).
For more information, or to apply, see the official job post: For more information, or to apply, see the official job post
- [Israel](https://akamaicareers.inflightcloud.com/jobdetails/aka_ext/028224?section=aka_ext&job=028224) [here](https://akamaicareers.inflightcloud.com/jobdetails/aka_ext/028789?section=aka_ext&job=028789).

View File

@ -13,10 +13,6 @@ export TKPATH="${TK_LIBRARY}"
# Export SSL certificate # Export SSL certificate
export SSL_CERT_FILE="${APPDIR}/opt/_internal/certs.pem" export SSL_CERT_FILE="${APPDIR}/opt/_internal/certs.pem"
if [ "$1" == "service" ] ; then
exec /bin/bash "${APPDIR}/install-infection-monkey-service.sh" ${@:2}
fi
# Call the entry point # Call the entry point
for opt in "$@" for opt in "$@"
do do

View File

@ -1,9 +1,9 @@
#!/bin/bash #!/bin/bash
LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
PYTHON_VERSION="3.7.14" PYTHON_VERSION="3.7.13"
PYTHON_APPIMAGE_URL="https://github.com/niess/python-appimage/releases/download/python3.7/python${PYTHON_VERSION}-cp37-cp37m-manylinux1_x86_64.AppImage" PYTHON_APPIMAGE_URL="https://github.com/niess/python-appimage/releases/download/python3.7/python${PYTHON_VERSION}-cp37-cp37m-manylinux1_x86_64.AppImage"
APPIMAGE_DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")") APPIMAGE_DIR="$(realpath $(dirname $BASH_SOURCE[0]))"
APPDIR="$APPIMAGE_DIR/squashfs-root" APPDIR="$APPIMAGE_DIR/squashfs-root"
BUILD_DIR="$APPDIR/usr/src" BUILD_DIR="$APPDIR/usr/src"
@ -30,7 +30,7 @@ setup_build_dir() {
local deployment_type=$3 local deployment_type=$3
local is_release_build=$4 local is_release_build=$4
pushd "$APPIMAGE_DIR" || handle_error pushd $APPIMAGE_DIR
setup_python_37_appdir setup_python_37_appdir
@ -38,7 +38,6 @@ setup_build_dir() {
copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$BUILD_DIR" copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$BUILD_DIR"
copy_server_config_to_build_dir copy_server_config_to_build_dir
copy_infection_monkey_service_to_build_dir
modify_deployment "$deployment_type" "$BUILD_DIR" modify_deployment "$deployment_type" "$BUILD_DIR"
add_agent_binaries_to_build_dir "$agent_binary_dir" "$BUILD_DIR" add_agent_binaries_to_build_dir "$agent_binary_dir" "$BUILD_DIR"
@ -50,7 +49,7 @@ setup_build_dir() {
remove_python_appdir_artifacts remove_python_appdir_artifacts
popd || handle_error popd
} }
setup_python_37_appdir() { setup_python_37_appdir() {
@ -65,28 +64,37 @@ setup_python_37_appdir() {
rm "$PYTHON_APPIMAGE" rm "$PYTHON_APPIMAGE"
} }
copy_infection_monkey_service_to_build_dir() {
cp "$APPIMAGE_DIR"/install-infection-monkey-service.sh "$APPDIR"
}
copy_server_config_to_build_dir() { copy_server_config_to_build_dir() {
cp "$APPIMAGE_DIR"/server_config.json.standard "$BUILD_DIR"/monkey_island/cc/server_config.json cp "$APPIMAGE_DIR"/server_config.json.standard "$BUILD_DIR"/monkey_island/cc/server_config.json
} }
install_monkey_island_python_dependencies() { install_monkey_island_python_dependencies() {
log_message "Installing island requirements" log_message "Installing island requirements"
log_message "Installing pipenv" log_message "Installing pipenv"
"$APPDIR"/AppRun -m pip install pipenv==2022.7.4 || handle_error "$APPDIR"/AppRun -m pip install pipenv || handle_error
export CI=1
log_message "Installing dependencies" requirements_island="$BUILD_DIR/monkey_island/requirements.txt"
pushd "$BUILD_DIR/monkey_island" || handle_error generate_requirements_from_pipenv_lock "$requirements_island"
"$APPDIR"/AppRun -m pipenv --python "$APPDIR/AppRun" sync --system || handle_error
popd || handle_error
log_message "Uninstalling pipenv (build dependency only)" log_message "Installing island python requirements"
"$APPDIR"/AppRun -m pip uninstall --yes pipenv virtualenv || handle_error "$APPDIR"/AppRun -m pip install -r "${requirements_island}" --ignore-installed || handle_error
log_message "Uninstalling pipenv"
"$APPDIR"/AppRun -m pip uninstall -y pipenv || handle_error
log_message "Uninstalling pip"
"$APPDIR"/AppRun -m pip uninstall -y pip setuptools || handle_error
}
generate_requirements_from_pipenv_lock () {
local requirements_island=$1
log_message "Generating a requirements.txt file with 'pipenv lock -r'"
pushd "$BUILD_DIR/monkey_island"
"$APPDIR"/AppRun -m pipenv --python "$APPDIR/AppRun" lock -r > "$requirements_island" || handle_error
popd
} }
@ -104,12 +112,18 @@ remove_python_appdir_artifacts() {
} }
build_package() { build_package() {
local version=$1 local commit_id=$2
local dist_dir=$2 local dist_dir=$3
log_message "Building AppImage" log_message "Building AppImage"
pushd "$APPIMAGE_DIR" || handle_error if [ -n "$1" ]; then
local version="v$1"
else
local version="$commit_id"
fi
pushd "$APPIMAGE_DIR"
ARCH="x86_64" linuxdeploy \ ARCH="x86_64" linuxdeploy \
--appdir "$APPIMAGE_DIR/squashfs-root" \ --appdir "$APPIMAGE_DIR/squashfs-root" \
--icon-file "$ICON_PATH" \ --icon-file "$ICON_PATH" \
@ -119,17 +133,11 @@ build_package() {
--output appimage --output appimage
dst_name="InfectionMonkey-$version.AppImage" dst_name="InfectionMonkey-$version.AppImage"
move_package_to_dist_dir "$dist_dir" "$dst_name" move_package_to_dist_dir $dist_dir $dst_name
popd || handle_error popd
} }
move_package_to_dist_dir() { move_package_to_dist_dir() {
mv Infection*Monkey*.AppImage "$1/$2" mv Infection*Monkey*.AppImage "$1/$2"
} }
cleanup() {
echo "Cleaning appimage build dirs"
rm -rf "$APPIMAGE_DIR/squashfs-root"
}

View File

@ -1,165 +0,0 @@
#!/bin/bash
set -e
SCRIPT_NAME="$(basename "${APPIMAGE}")"
SYSTEMD_UNIT_FILENAME="infection-monkey.service"
TMP_SYSTEMD_UNIT="${PWD}/${SYSTEMD_UNIT_FILENAME}"
SYSTEMD_DIR="/lib/systemd/system"
MONKEY_BIN="/opt/infection-monkey/bin"
APPIMAGE_NAME="InfectionMonkey.AppImage"
die() {
echo "$1" >&2
echo ""
echo_help
exit 1
}
echo_help() {
echo "Installs the Infection Monkey service to run on boot."
echo ""
echo "Usage:"
echo " ${SCRIPT_NAME} service --install --user <USERNAME>"
echo " ${SCRIPT_NAME} service --uninstall"
echo " ${SCRIPT_NAME} service -h|--help"
echo ""
echo "Options:"
echo " --install Install the Infection Monkey service"
echo " --user Configure the Infection Monkey service to run as a specific user"
echo " --uninstall Uninstall Infection Monkey service"
}
install_service() {
copy_appimage
install_systemd_unit "$1"
echo "The Infection Monkey service has been installed and will start on boot."
echo "Run 'systemctl start infection-monkey' to start the service now."
}
copy_appimage() {
sudo mkdir --mode=0755 -p "${MONKEY_BIN}"
if [ "${APPIMAGE}" != "${MONKEY_BIN}/${APPIMAGE_NAME}" ] ; then
umask 022
sudo cp "${APPIMAGE}" "${MONKEY_BIN}/${APPIMAGE_NAME}"
sudo chmod 755 "${MONKEY_BIN}/${APPIMAGE_NAME}"
fi
}
install_systemd_unit() {
umask 077
cat > "${TMP_SYSTEMD_UNIT}" << EOF
[Unit]
Description=Infection Monkey Runner
After=network.target
[Service]
User=$1
Type=simple
ExecStart="${MONKEY_BIN}/${APPIMAGE_NAME}"
[Install]
WantedBy=multi-user.target
EOF
sudo mv "${TMP_SYSTEMD_UNIT}" "${SYSTEMD_DIR}/${SYSTEMD_UNIT_FILENAME}"
sudo systemctl enable "${SYSTEMD_UNIT_FILENAME}" &>/dev/null
}
uninstall_service() {
if [ -f "${MONKEY_BIN}/${APPIMAGE_NAME}" ] ; then
sudo rm -f "${MONKEY_BIN}/${APPIMAGE_NAME}"
fi
if [ -f "${SYSTEMD_DIR}/${SYSTEMD_UNIT_FILENAME}" ] ; then
sudo systemctl stop "${SYSTEMD_UNIT_FILENAME}" 2>/dev/null
sudo systemctl disable "${SYSTEMD_UNIT_FILENAME}" &>/dev/null
sudo rm "${SYSTEMD_DIR}/${SYSTEMD_UNIT_FILENAME}"
sudo systemctl daemon-reload
fi
echo "The Infection Monkey service has been uninstalled"
}
exit_if_user_doesnt_exist() {
if ! user_exists "$1" ; then
die "Error: User '$1' does not exist."
fi
}
user_exists() {
id -u "$1" &>/dev/null
}
has_sudo() {
# 0 true, 1 false
sudo -nv > /dev/null 2>&1
return $?
}
exit_if_missing_argument() {
if [ -z "$2" ] || [ "${2:0:1}" == "-" ]; then
die "Error: Argument for parameter '$1' is missing."
fi
}
do_uninstall=false
do_install=false
username=""
while (( "$#" )); do
case "$1" in
--user)
exit_if_missing_argument "$1" "$2"
exit_if_user_doesnt_exist "$2"
username=$2
shift 2
;;
--install)
do_install=true
shift
;;
--uninstall)
do_uninstall=true
shift
;;
-h|--help)
echo_help
exit 0
;;
*)
die "Error: Unsupported parameter $1."
;;
esac
done
if ! has_sudo; then
die "Error: You need root permissions for some of this script operations. \
Run \`sudo -v\`, enter your password, and then re-run this script."
fi
if [ -z "${APPIMAGE}" ] ; then
die "Error: Missing 'APPIMAGE' environment variable. Try installing the Infection Monkey service through the AppImage"
fi
if $do_install && $do_uninstall ; then
die "Error: The --install and --uninstall flags are mutually exclusive."
fi
if $do_uninstall ; then
uninstall_service
exit 0
fi
if $do_install ; then
if [ -z "$username" ] ; then
die "Error: You must supply a username."
fi
install_service "$username"
exit 0
fi
die "Error:You must specify either the --install or --uninstall flag."

View File

@ -98,24 +98,13 @@ clone_monkey_repo() {
install_build_prereqs() { install_build_prereqs() {
sudo apt-get update sudo apt-get update
sudo apt-get upgrade -y -o Dpkg::Options::="--force-confold" sudo apt-get upgrade -y
# monkey island prereqs # monkey island prereqs
sudo apt-get install -y curl libcurl4 openssl git build-essential moreutils sudo apt-get install -y curl libcurl4 openssl git build-essential moreutils
install_nodejs install_nodejs
} }
format_version() {
local unformatted_version=$1
local commit_id=$2
if [ -n "$unformatted_version" ]; then
echo "v$monkey_version"
else
echo "$commit_id"
fi
}
agent_binary_dir="" agent_binary_dir=""
as_root=false as_root=false
branch="develop" branch="develop"
@ -207,24 +196,15 @@ fi
install_build_prereqs install_build_prereqs
install_package_specific_build_prereqs "$WORKSPACE" install_package_specific_build_prereqs "$WORKSPACE"
commit_id=$(get_commit_id "$monkey_repo")
is_release_build=false is_release_build=false
# Monkey version is empty on release build # Monkey version is empty on release build
if [ ! -z "$monkey_version" ]; then if [ ! -z "$monkey_version" ]; then
is_release_build=true is_release_build=true
echo -n "" > "$monkey_repo/monkey/common/BUILD"
else
echo $commit_id > "$monkey_repo/monkey/common/BUILD"
fi fi
setup_build_dir "$agent_binary_dir" "$monkey_repo" "$deployment_type" "$is_release_build" setup_build_dir "$agent_binary_dir" "$monkey_repo" "$deployment_type" "$is_release_build"
commit_id=$(get_commit_id "$monkey_repo")
monkey_version=$(format_version "$monkey_version" "$commit_id") build_package "$monkey_version" "$commit_id" "$DIST_DIR"
build_package "$monkey_version" "$DIST_DIR"
cleanup "$monkey_version"
log_message "Finished building package: $package" log_message "Finished building package: $package"
exit 0 exit 0

View File

@ -4,10 +4,9 @@ FROM bitnami/python:3.7 as builder
COPY ./monkey /monkey COPY ./monkey /monkey
WORKDIR /monkey WORKDIR /monkey
RUN virtualenv . RUN virtualenv .
RUN export CI=1
RUN . bin/activate && \ RUN . bin/activate && \
cd monkey_island && \ cd monkey_island && \
pip install pipenv==2022.7.4 && \ pip install pipenv && \
pipenv sync pipenv sync

View File

@ -1,5 +1,4 @@
DOCKER_DIR="$(realpath $(dirname $BASH_SOURCE[0]))" DOCKER_DIR="$(realpath $(dirname $BASH_SOURCE[0]))"
DOCKER_IMAGE_NAME="guardicore/monkey-island"
source "$DOCKER_DIR/../common.sh" source "$DOCKER_DIR/../common.sh"
@ -38,12 +37,20 @@ copy_server_config_to_build_dir() {
build_package() { build_package() {
local version=$1 local version=$1
local dist_dir=$2 local commit_id=$2
local dist_dir=$3
pushd ./docker pushd ./docker
if [ -n "$1" ]; then
version="v$version"
else
version="$commit_id"
fi
docker_image_name="guardicore/monkey-island:$version"
tar_name="$DOCKER_DIR/InfectionMonkey-docker-$version.tar" tar_name="$DOCKER_DIR/InfectionMonkey-docker-$version.tar"
build_docker_image_tar "$DOCKER_IMAGE_NAME:$version" "$tar_name" build_docker_image_tar "$docker_image_name" "$tar_name"
tgz_name="$DOCKER_DIR/InfectionMonkey-docker-$version.tgz" tgz_name="$DOCKER_DIR/InfectionMonkey-docker-$version.tgz"
build_docker_image_tgz "$tar_name" "$tgz_name" build_docker_image_tgz "$tar_name" "$tgz_name"
@ -68,11 +75,3 @@ build_docker_image_tgz() {
move_package_to_dist_dir() { move_package_to_dist_dir() {
mv "$1" "$2/" mv "$1" "$2/"
} }
cleanup() {
local tag=$1
echo "Cleaning docker images"
sudo docker rmi "$DOCKER_IMAGE_NAME:$tag"
sudo docker image prune --force
}

View File

@ -1,13 +0,0 @@
import json
data = {
'name' : 'myname',
'age' : 100,
}
# separators:是分隔符的意思参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符后面的空格都除去了.
# dumps 将python对象字典转换为json字符串
json_str = json.dumps(data, separators=(',', ':'))
print(type(json_str), json_str)
# loads 将json字符串转化为python对象字典
pyton_obj = json.loads(json_str)
print(type(pyton_obj), pyton_obj)

View File

@ -21,6 +21,7 @@ $WINDOWS_64_BINARY_PATH = "monkey-windows-64.exe"
$MONKEY_ISLAND_DIR = Join-Path "\monkey" -ChildPath "monkey_island" $MONKEY_ISLAND_DIR = Join-Path "\monkey" -ChildPath "monkey_island"
$MONKEY_DIR = Join-Path "\monkey" -ChildPath "infection_monkey" $MONKEY_DIR = Join-Path "\monkey" -ChildPath "infection_monkey"
$TEMP_PYTHON_INSTALLER = ".\python.exe" $TEMP_PYTHON_INSTALLER = ".\python.exe"
$TEMP_MONGODB_ZIP = ".\mongodb.zip"
$TEMP_OPEN_SSL_ZIP = ".\openssl.zip" $TEMP_OPEN_SSL_ZIP = ".\openssl.zip"
$TEMP_CPP_INSTALLER = "cpp.exe" $TEMP_CPP_INSTALLER = "cpp.exe"
$TEMP_NPM_INSTALLER = "node.msi" $TEMP_NPM_INSTALLER = "node.msi"
@ -28,6 +29,7 @@ $TEMP_UPX_ZIP = "upx.zip"
$UPX_FOLDER = "upx-3.96-win64" $UPX_FOLDER = "upx-3.96-win64"
# Other url's # Other url's
$MONGODB_URL = "https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2012plus-v4.2-latest.zip"
$OPEN_SSL_URL = "https://indy.fulgan.com/SSL/openssl-1.0.2u-x64_86-win64.zip" $OPEN_SSL_URL = "https://indy.fulgan.com/SSL/openssl-1.0.2u-x64_86-win64.zip"
$CPP_URL = "https://go.microsoft.com/fwlink/?LinkId=746572" $CPP_URL = "https://go.microsoft.com/fwlink/?LinkId=746572"
$NPM_URL = "https://nodejs.org/dist/v16.14.2/node-v16.14.2-x64.msi" $NPM_URL = "https://nodejs.org/dist/v16.14.2/node-v16.14.2-x64.msi"

View File

@ -205,7 +205,7 @@ pushd "$ISLAND_PATH/cc/ui" || handle_error
npm ci npm ci
log_message "Generating front end" log_message "Generating front end"
npm run dev npm run dist
popd || handle_error popd || handle_error
# Making dir for binaries # Making dir for binaries

View File

@ -163,8 +163,25 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
[Environment]::SetEnvironmentVariable("Path", $env:Path, "User") [Environment]::SetEnvironmentVariable("Path", $env:Path, "User")
} }
$install_mongo_script = (Join-Path -Path $monkey_home -ChildPath "$MONKEY_ISLAND_DIR\windows\install_mongo.ps1") # Download mongodb
Invoke-Expression "$install_mongo_script -binDir $binDir" if (!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "mongodb")))
{
"Downloading mongodb ..."
$webClient.DownloadFile($MONGODB_URL, $TEMP_MONGODB_ZIP)
"Unzipping mongodb"
Expand-Archive $TEMP_MONGODB_ZIP -DestinationPath $binDir
# Get unzipped folder's name
$mongodb_folder = Get-ChildItem -Path $binDir | Where-Object -FilterScript {
($_.Name -like "mongodb*")
} | Select-Object -ExpandProperty Name
# Move all files from extracted folder to mongodb folder
New-Item -ItemType directory -Path (Join-Path -Path $binDir -ChildPath "mongodb")
"Moving extracted files"
Move-Item -Path (Join-Path -Path $binDir -ChildPath $mongodb_folder | Join-Path -ChildPath "\bin\*") -Destination (Join-Path -Path $binDir -ChildPath "mongodb\")
"Removing zip file"
Remove-Item $TEMP_MONGODB_ZIP
Remove-Item (Join-Path -Path $binDir -ChildPath $mongodb_folder) -Recurse
}
# Download OpenSSL # Download OpenSSL
"Downloading OpenSSL ..." "Downloading OpenSSL ..."
@ -223,7 +240,7 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
"Updating npm" "Updating npm"
Push-Location -Path (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\cc\ui") Push-Location -Path (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\cc\ui")
& npm update & npm update
& npm run dev & npm run dist
Pop-Location Pop-Location
# Create infection_monkey/bin directory if not already present # Create infection_monkey/bin directory if not already present

View File

@ -0,0 +1,161 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
SYSTEMD_UNIT_FILENAME="infection-monkey.service"
SYSTEMD_DIR="/lib/systemd/system"
MONKEY_BIN="/opt/infection-monkey/bin"
APPIMAGE_NAME="InfectionMonkey.AppImage"
echo_help() {
echo "Installs the Infection Monkey service to run on boot."
echo ""
echo "Usage:"
echo " install-infection-monkey-service.sh --user <USERNAME> --appimage <PATH>"
echo " install-infection-monkey-service.sh --uninstall"
echo " install-infection-monkey-service.sh -h|--help"
echo ""
echo "Options:"
echo " --user User to run the service as"
echo " --appimage Path to AppImage"
echo " --uninstall Uninstall the Infection Monkey service"
}
install_service() {
move_appimage "$2"
cat > "${SCRIPT_DIR}/${SYSTEMD_UNIT_FILENAME}" << EOF
[Unit]
Description=Infection Monkey Runner
After=network.target
[Service]
User=$1
Type=simple
ExecStart="${MONKEY_BIN}/${APPIMAGE_NAME}"
[Install]
WantedBy=multi-user.target
EOF
umask 077
sudo mv "${SCRIPT_DIR}/${SYSTEMD_UNIT_FILENAME}" "${SYSTEMD_DIR}/${SYSTEMD_UNIT_FILENAME}"
sudo systemctl enable "${SYSTEMD_UNIT_FILENAME}" &>/dev/null
echo -e "The Infection Monkey service has been installed and will start on boot.\n\
Run 'systemctl start infection-monkey' to start the service now."
}
uninstall_service() {
if [ -f "${MONKEY_BIN}/${APPIMAGE_NAME}" ] ; then
sudo rm -f "${MONKEY_BIN}/${APPIMAGE_NAME}"
fi
if [ -f "${SYSTEMD_DIR}/${SYSTEMD_UNIT_FILENAME}" ] ; then
sudo systemctl stop "${SYSTEMD_UNIT_FILENAME}" 2>/dev/null
sudo systemctl disable "${SYSTEMD_UNIT_FILENAME}" &>/dev/null
sudo rm "${SYSTEMD_DIR}/${SYSTEMD_UNIT_FILENAME}"
sudo systemctl daemon-reload
fi
echo "The Infection Monkey service has been uninstalled"
}
move_appimage() {
sudo mkdir --mode=0755 -p "${MONKEY_BIN}"
if [ "$1" != "${MONKEY_BIN}/${APPIMAGE_NAME}" ] ; then
umask 022
sudo cp "$appimage_path" "${MONKEY_BIN}/${APPIMAGE_NAME}"
sudo chmod 755 "${MONKEY_BIN}/${APPIMAGE_NAME}"
fi
}
user_exists() {
id -u "$1" &>/dev/null
}
assert_parameter_supplied() {
if [ -z "$2" ] ; then
echo "Error: missing required parameter '$1'"
echo_help
exit 1
fi
}
has_sudo() {
# 0 true, 1 false
sudo -nv > /dev/null 2>&1
return $?
}
exit_if_missing_argument() {
if [ -z "$2" ] || [ "${2:0:1}" == "-" ]; then
echo "Error: Argument for parameter '$1' is missing" >&2
echo_help
exit 1
fi
}
do_uninstall=false
uname=""
appimage_path=""
while (( "$#" )); do
case "$1" in
--user)
exit_if_missing_argument "$1" "$2"
uname=$2
shift 2
;;
--appimage)
exit_if_missing_argument "$1" "$2"
appimage_path=$2
shift 2
;;
--uninstall)
do_uninstall=true
shift
;;
-h|--help)
echo_help
exit 0
;;
*)
echo "Error: Unsupported parameter $1" >&2
exit 1
;;
esac
done
if ! has_sudo; then
echo "Error: You need root permissions for some of this script operations. \
Run \`sudo -v\`, enter your password, and then re-run this script."
exit 1
fi
if $do_uninstall ; then
uninstall_service
exit 0
fi
assert_parameter_supplied "--user" "$uname"
assert_parameter_supplied "--appimage" "$appimage_path"
if ! user_exists "$uname" ; then
echo "Error: User '$uname' does not exist"
exit 1
fi
if [ ! -f "$appimage_path" ] ; then
if [ ! -f "${SCRIPT_DIR}/$appimage_path" ] ; then
echo "Error: AppImage '$appimage_path' does not exist"
exit 1
fi
appimage_path="${SCRIPT_DIR}/$appimage_path"
fi
install_service "$uname" "$appimage_path"

View File

@ -1,2 +1,2 @@
baseURL = "https://monkey-documentation.website-us-southeast-1.linodeobjects.com" baseURL = "https://www.guardicore.com/infectionmonkey/docs/"
canonifyURLs = false canonifyURLs = true

View File

@ -1,2 +1,2 @@
baseURL = "http://monkey-documentation-staging.website-us-southeast-1.linodeobjects.com" baseURL = "http://staging-infectionmonkey.temp312.kinsta.cloud/docs/"
canonifyURLs = false canonifyURLs = true

View File

@ -15,20 +15,18 @@ Below are some of the most common questions we receive about the Infection Monke
- [Is the Infection Monkey a malware/virus?](#is-the-infection-monkey-a-malwarevirus) - [Is the Infection Monkey a malware/virus?](#is-the-infection-monkey-a-malwarevirus)
- [Reset the Monkey Island password](#reset-the-monkey-island-password) - [Reset the Monkey Island password](#reset-the-monkey-island-password)
- [Should I run the Infection Monkey continuously?](#should-i-run-the-infection-monkey-continuously) - [Should I run the Infection Monkey continuously?](#should-i-run-the-infection-monkey-continuously)
- [Exactly what internet queries does the Infection Monkey perform?](#exactly-what-internet-queries-does-the-infection-monkey-perform) - [Which queries does the Infection Monkey perform to the internet exactly?](#which-queries-does-the-infection-monkey-perform-to-the-internet-exactly)
- [Logging and how to find logs](#logging-and-how-to-find-logs) - [Logging and how to find logs](#logging-and-how-to-find-logs)
- [Downloading logs](#downloading-logs)
- [Log locations](#log-locations)
- [Monkey Island server logs](#monkey-island-server-logs) - [Monkey Island server logs](#monkey-island-server-logs)
- [Infection Monkey agent logs](#infection-monkey-agent-logs) - [Infection Monkey agent logs](#infection-monkey-agent-logs)
- [Running the Infection Monkey in a production environment](#running-the-infection-monkey-in-a-production-environment) - [Running the Infection Monkey in a production environment](#running-the-infection-monkey-in-a-production-environment)
- [How much of a footprint does the Infection Monkey leave?](#how-much-of-a-footprint-does-the-infection-monkey-leave) - [How much of a footprint does the Infection Monkey leave?](#how-much-of-a-footprint-does-the-infection-monkey-leave)
- [What's the Infection Monkey Agent's impact on system resources usage?](#whats-the-infection-monkey-agents-impact-on-system-resources-usage) - [What's the Infection Monkey Agent's impact on system resources usage?](#whats-the-infection-monkeys-impact-on-system-resources-usage)
- [What are the system resource requirements for the Monkey Island?](#what-are-the-system-resource-requirements-for-the-monkey-island) - [What are the system resource requirements for the Monkey Island?](#what-are-the-system-resource-requirements-for-the-monkey-island)
- [Is it safe to use real passwords and usernames in the Infection Monkey's configuration?](#is-it-safe-to-use-real-passwords-and-usernames-in-the-infection-monkeys-configuration) - [Is it safe to use real passwords and usernames in the Infection Monkey's configuration?](#is-it-safe-to-use-real-passwords-and-usernames-in-the-infection-monkeys-configuration)
- [How do you store sensitive information on Monkey Island?](#how-do-you-store-sensitive-information-on-monkey-island) - [How do you store sensitive information on Monkey Island?](#how-do-you-store-sensitive-information-on-monkey-island)
- [How stable are the exploits used by the Infection Monkey? Will the Infection Monkey crash my systems with its exploits?](#how-stable-are-the-exploits-used-by-the-infection-monkey-will-the-infection-monkey-crash-my-systems-with-its-exploits) - [How stable are the exploitations used by the Infection Monkey? Will the Infection Monkey crash my systems with its exploits?](#how-stable-are-the-exploitations-used-by-the-infection-monkey-will-the-infection-monkey-crash-my-systems-with-its-exploits)
- [After I've set up Monkey Island, how can I execute the Infection Monkey agent?](#after-ive-set-up-monkey-island-how-can-i-execute-the-infection-monkey-agent) - [After I've set up Monkey Island, how can I execute the Infection Monkey?](#after-ive-set-up-monkey-island-how-can-i-execute-the-infection-monkey-agent)
- [How can I make the Infection Monkey agents propagate “deeper” into the network?](#how-can-i-make-the-infection-monkey-agent-propagate-deeper-into-the-network) - [How can I make the Infection Monkey agents propagate “deeper” into the network?](#how-can-i-make-the-infection-monkey-agent-propagate-deeper-into-the-network)
- [What if the report returns a blank screen?](#what-if-the-report-returns-a-blank-screen) - [What if the report returns a blank screen?](#what-if-the-report-returns-a-blank-screen)
- [Can I limit how the Infection Monkey propagates through my network?](#can-i-limit-how-the-infection-monkey-propagates-through-my-network) - [Can I limit how the Infection Monkey propagates through my network?](#can-i-limit-how-the-infection-monkey-propagates-through-my-network)
@ -36,7 +34,7 @@ Below are some of the most common questions we receive about the Infection Monke
## Where can I get the latest version of the Infection Monkey? ## Where can I get the latest version of the Infection Monkey?
For the latest **stable** release, visit [our downloads page](https://www.akamai.com/infectionmonkey#download). **This is the recommended and supported version**! For the latest **stable** release, visit [our downloads page](https://www.guardicore.com/infectionmonkey/#download). **This is the recommended and supported version**!
If you want to see what has changed between versions, refer to the [releases page on GitHub](https://github.com/guardicore/monkey/releases). For the latest development version, visit the [develop version on GitHub](https://github.com/guardicore/monkey/tree/develop). If you want to see what has changed between versions, refer to the [releases page on GitHub](https://github.com/guardicore/monkey/releases). For the latest development version, visit the [develop version on GitHub](https://github.com/guardicore/monkey/tree/develop).
@ -81,6 +79,7 @@ Monkey in the newly created folder.
## Reset the Monkey Island password ## Reset the Monkey Island password
{{% notice warning %}} {{% notice warning %}}
If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost. <br/><br/> If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost. <br/><br/>
However, you can save the Monkey's existing configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. However, you can save the Monkey's existing configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page.
@ -159,25 +158,8 @@ If internet access is available, the Infection Monkey will use the internet for
The Monkey performs queries out to the Internet on two separate occasions: The Monkey performs queries out to the Internet on two separate occasions:
1. The Infection Monkey agent checks if it has internet access by performing 1. The Infection Monkey agent checks if it has internet access by performing requests to pre-configured domains. By default, these domains are `monkey.guardicore.com` and `www.google.com`, which can be changed. The request doesn't include any extra information - it's a GET request with no extra parameters. Since the Infection Monkey is 100% open-source, you can find the domains in the configuration [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/config.py#L152) and the code that performs the internet check [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/network/info.py#L123). This **IS NOT** used for statistics collection.
requests to pre-configured domains. By default, these domains are 1. After installing the Monkey Island, it sends a request to check for updates on `updates.infectionmonkey.com`. The request doesn't include any PII other than the IP address of the request. It also includes the server's deployment type (e.g., Windows Server, Debian Package, AWS Marketplace) and the server's version (e.g., "1.6.3"), so we can check if we have an update available for this type of deployment. Since the Infection Monkey is 100% open-source, you can inspect the code that performs this [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/monkey_island/cc/services/version_update.py#L37). This **IS** used for statistics collection. However, due to this data's anonymous nature, we use this to get an aggregate assumption of how many deployments we see over a specific time period - it's not used for "personal" tracking.
`monkey.guardicore.com` and `www.google.com`, which can be changed. The
request doesn't include any extra information - it's a GET request with no
extra parameters. Since the Infection Monkey is 100% open-source, you can
find the domains in the configuration
[here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/config.py#L152)
and the code that performs the internet check
[here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/network/info.py#L123).
This **IS NOT** used for statistics collection.
1. After the Monkey Island starts it sends a GET request with current
deployment type to the update server to fetch the latest version and a
download link for it. This information is used by the Monkey Island to
suggest an update if one is available. No information gets collected during
this process.
1. After the Monkey Island starts it sends a GET request to the analytics
server with your deployment type and a version number. This information gets
collected on the analytics server. It is used to understand which deployment
types/versions are no longer used and can be deprecated.
## Logging and how to find logs ## Logging and how to find logs
@ -195,7 +177,7 @@ the Infection Map.
If the logs can't be downloaded through the UI for any reason, you can collect the log files If the logs can't be downloaded through the UI for any reason, you can collect the log files
directly from the machine where an Agent or Monkey Island ran. directly from the machine where an Agent or Monkey Island ran.
#### Monkey Island server logs #### Monkey Island server log
The Monkey Island's log file is located in the The Monkey Island's log file is located in the
[data directory]({{< ref "/reference/data_directory" >}}). [data directory]({{< ref "/reference/data_directory" >}}).
@ -203,7 +185,7 @@ The Monkey Island's log file is located in the
The log enables you to see which requests were requested from the server and extra logs from the backend logic. The log will contain entries like these: The log enables you to see which requests were requested from the server and extra logs from the backend logic. The log will contain entries like these:
```log ```log
2022-04-18 13:48:43,914 - pywsgi.py:1226 - write() - INFO - 192.168.56.1 - - [2022-04-18 13:48:43] "GET /api/agent-binaries/windows HTTP/1.1" 200 21470665 0.293586 2022-04-18 13:48:43,914 - pywsgi.py:1226 - write() - INFO - 192.168.56.1 - - [2022-04-18 13:48:43] "GET /api/agent/download/windows HTTP/1.1" 200 21470665 0.293586
2022-04-18 13:48:49,970 - pywsgi.py:1226 - write() - INFO - 192.168.56.1 - - [2022-04-18 13:48:49] "GET /api/island-mode HTTP/1.1" 200 128 0.003426 2022-04-18 13:48:49,970 - pywsgi.py:1226 - write() - INFO - 192.168.56.1 - - [2022-04-18 13:48:49] "GET /api/island-mode HTTP/1.1" 200 128 0.003426
2022-04-18 13:48:49,988 - report.py:355 - get_domain_issues() - INFO - Domain issues generated for reporting 2022-04-18 13:48:49,988 - report.py:355 - get_domain_issues() - INFO - Domain issues generated for reporting
``` ```

View File

@ -13,7 +13,7 @@ draft: false
The Infection Monkey is an open-source breach and attack simulation tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Infection Monkey is an open-source breach and attack simulation tool for testing a data center's resiliency to perimeter breaches and internal server infection.
Infection Monkey will help you validate existing security solutions and will provide a view of the internal network from an attacker's perspective. Infection Monkey will help you validate existing security solutions and will provide a view of the internal network from an attacker's perspective.
Infection Monkey is free and can be downloaded from [our homepage](https://www.akamai.com/infectionmonkey). Infection Monkey is free and can be downloaded from [our homepage](https://infectionmonkey.com/).
![Infection Monkey Documentation Hub Logo](/images/monkey-teacher.svg?height=400px "Infection Monkey Documentation Hub Logo") ![Infection Monkey Documentation Hub Logo](/images/monkey-teacher.svg?height=400px "Infection Monkey Documentation Hub Logo")
@ -40,7 +40,7 @@ A more in-depth description of reports generated can be found in the [reports do
## Getting Started ## Getting Started
If you haven't downloaded Infection Monkey yet you can do so [from our homepage](https://www.akamai.com/infectionmonkey#download). After downloading the Monkey, install it using one of our [setup guides]({{< ref "/setup" >}}), and read our [getting started guide]({{< ref "/usage/getting-started" >}}) for a quick-start on Monkey! If you haven't downloaded Infection Monkey yet you can do so [from our homepage](https://www.guardicore.com/infectionmonkey/#download). After downloading the Monkey, install it using one of our [setup guides]({{< ref "/setup" >}}), and read our [getting started guide]({{< ref "/usage/getting-started" >}}) for a quick-start on Monkey!
## Support and community ## Support and community

View File

@ -38,6 +38,6 @@ We always want to improve the core Infection Monkey code to make it smaller, fas
### Documentation 📚 ### Documentation 📚
Every project requires excellent documentation. The Infection Monkey is no different. Please feel free to open pull requests with suggestions, improvements or issues and ask us to document various parts of the Monkey. Every project requires excellent documentation. The Infection Monkey is no different. Please feel free to open pull requests with suggestions, improvements or issues and asking us to document various parts of the Monkey.
The Infection Monkey's documentation is stored in the `/docs/content` directory. The Infection Monkey's documentation is stored in the `/docs/content` directory.

View File

@ -14,7 +14,7 @@ The Infection Monkey has development tutorials that use [`swimm.io`](https://swi
First, [sign up for swimm's beta](https://swimm.io/sign-beta). `swimm` is free for open-source projects, but as they're still in beta you'll need to sign up in order to download it. First, [sign up for swimm's beta](https://swimm.io/sign-beta). `swimm` is free for open-source projects, but as they're still in beta you'll need to sign up in order to download it.
After you've downloaded and installed `swimm`, open a shell in the Infection Monkey repo folder and run: After you've downloaded and installed `swimm`, open a shell in the Infeciton Monkey repo folder and run:
```shell script ```shell script
swimm start swimm start

View File

@ -9,6 +9,6 @@ tags = ["reference"]
# Reference # Reference
Find detailed information about the Infection Monkey: Find detailed information about the Infection Monkey.
{{% children %}} {{% children %}}

View File

@ -1,18 +0,0 @@
---
title: "Agent propagation"
date: 2022-06-03T13:17:22+05:30
draft: false
pre: '<i class="fas fa-user-secret"></i> '
weight: 2
tags: ["agent", "propagation", "reference"]
---
## How does the Infection Monkey Agent propagate to a new machine?
The agent propagates using remote code execution vulnerabilities. Once the
agent has achieved remote code execution on the victim, it executes commands
that are similar to the ones described in [manual run
page.](../../usage/running-manually/)
On Windows targets, the agent is copied to `C:\Windows\temp\monkey64.exe`. On
Linux targets, it is copied to `/tmp/monkey`.

View File

@ -23,7 +23,7 @@ The location of the data directory is set in the `data_dir` field in the
`server_config.json` file. `server_config.json` file.
1. [Create a custom server_config.json file](../server_configuration) and set the `data_dir` field. Its 1. [Create a custom server_config.json file](../server_configuration) and set the `data_dir` field. Its
contents will look like this: contents will look like:
```json ```json
{ {

View File

@ -7,4 +7,4 @@ tags: ["exploit", "windows"]
### Description ### Description
For this exploit, the Infection Monkey will try to brute force into an MsSQL server and use an insecure configuration to execute commands on the server. For this exploit, the Infection Monkey will try to brute force into a MsSQL server and use an insecure configuration to execute commands on the server.

View File

@ -22,7 +22,8 @@ The PowerShell exploiter can be run from both Linux and Windows attackers. On
Windows attackers, the exploiter has the ability to use the cached username Windows attackers, the exploiter has the ability to use the cached username
and/or password from the current user. On both Linux and Windows attackers, the and/or password from the current user. On both Linux and Windows attackers, the
exploiter uses all combinations of the [user-configured usernames and exploiter uses all combinations of the [user-configured usernames and
passwords]({{< ref "/usage/configuration/basic-credentials" >}}), as well as LM or NT hashes that have been collected. Different combinations of passwords]({{< ref "/usage/configuration/basic-credentials" >}}), as well as
and LM or NT hashes that have been collected. Different combinations of
credentials are attempted in the following order: credentials are attempted in the following order:
1. **Cached username and password (Windows attacker only)** - The exploiter will 1. **Cached username and password (Windows attacker only)** - The exploiter will

View File

@ -21,10 +21,10 @@ is, therefore, **not** enabled by default.
During successful exploitation, the Zerologon exploiter: During successful exploitation, the Zerologon exploiter:
* Will temporarily change the target domain controller's password. * will temporarily change the target domain controller's password.
* May break the target domain controller's communication with other systems in the network, affecting functionality. * may break the target domain controller's communication with other systems in the network, affecting functionality.
* May change the administrator's password. * may change the administrator's password.
* Will *attempt* to revert all changes. * will *attempt* to revert all changes.
While the Zerologon exploiter is usually successful in reverting its changes While the Zerologon exploiter is usually successful in reverting its changes
and restoring the original passwords, it sometimes fails. Restoring passwords and restoring the original passwords, it sometimes fails. Restoring passwords
@ -58,17 +58,17 @@ to regain access to the system.
#### Use Reset-ComputerMachinePassword #### Use Reset-ComputerMachinePassword
If you are able to log in as the administrator, you can use the If you are able to login as the administrator, you can use the
[Reset-ComputerMachinePassword](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/reset-computermachinepassword?view=powershell-5.1) [Reset-ComputerMachinePassword](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/reset-computermachinepassword?view=powershell-5.1)
powershell command to restore the domain controller's password. powershell command to restore the domain controller's password.
#### Try a Zerologon password restoration tool #### Try a zerologon password restoration tool
If all other approaches fail, you can try the tools and steps found If all other approaches fail, you can try the tools and steps found
[here](https://github.com/risksense/zerologon). [here](https://github.com/risksense/zerologon).
### Note ### Notes
* The Infection Monkey exploiter implementation is based on implementations by [@dirkjanm](https://github.com/dirkjanm/CVE-2020-1472/) and [@risksense](https://github.com/risksense/zerologon). * The Infection Monkey exploiter implementation is based on implementations by [@dirkjanm](https://github.com/dirkjanm/CVE-2020-1472/) and [@risksense](https://github.com/risksense/zerologon).

View File

@ -9,7 +9,7 @@ tags = ["reference", "exploit"]
# Exploiters # Exploiters
The Infection Monkey uses various remote code execution (RCE) exploiters. To our best knowledge, most of these pose no risk to performance or services on victim machines. This documentation serves as a quick introduction to the exploiters currently implemented and the vulnerabilities they use: The Infection Monkey uses various remote code execution (RCE) exploiters. To our best knowledge, most of these pose no risk to performance or services on victim machines. This documentation serves as a quick introduction to the exploiters currently implemented and the vulnerabilities they use.
{{% children %}} {{% children %}}

View File

@ -8,6 +8,6 @@ pre = "<i class='fas fa-scroll'></i> "
# Infection Monkey's Reports # Infection Monkey's Reports
The Infection Monkey offers four reports: The Infection Monkey offers three reports:
{{% children description=true style="p"%}} {{% children description=true style="p"%}}

View File

@ -18,7 +18,7 @@ Watch the overview video:
## How to use the report ## How to use the report
The MITRE ATT&CK report is centered around the ATT&CK matrix: The MITRE ATT&CK report is centred around the ATT&CK matrix:
![MITRE Report](/images/usage/reports/mitre-report-0.png "MITRE Report") ![MITRE Report](/images/usage/reports/mitre-report-0.png "MITRE Report")

View File

@ -27,7 +27,7 @@ This diagram provides you with a quick glance at how your organization scores on
![Zero Trust Report summary](/images/usage/reports/ztreport1.png "Zero Trust Report summary") ![Zero Trust Report summary](/images/usage/reports/ztreport1.png "Zero Trust Report summary")
## Test results ## Test Results
This section shows how your network fared against each of the tests the Infection Monkey ran. The tests are ordered by Zero Trust pillar, so you can quickly navigate to the category you want to prioritize. This section shows how your network fared against each of the tests the Infection Monkey ran. The tests are ordered by Zero Trust pillar, so you can quickly navigate to the category you want to prioritize.

View File

@ -46,20 +46,6 @@ do, see the [FAQ]({{< ref
>}}) for more information. >}}) for more information.
{{% /notice %}} {{% /notice %}}
## Running the Infection Monkey as a service on boot
The Infection Monkey can be installed as a service and run on boot by running the AppImage package
with the following parameters. This requires root permissions, so run `sudo -v` and enter your
password before running the script, if required.
```bash
./InfectionMonkey-v1.13.0.AppImage service --install --user <USERNAME>
```
To uninstall it, run:
```bash
./InfectionMonkey-v1.13.0.AppImage service --uninstall
```
## Configuring the server ## Configuring the server
You can configure the server by creating You can configure the server by creating

View File

@ -10,4 +10,4 @@ pre = '<i class="fas fa-users-cog"></i> '
If you're new to the Infection Monkey, check out our [Getting Started](getting-started) page. If you're new to the Infection Monkey, check out our [Getting Started](getting-started) page.
If you haven't downloaded the Infection Monkey yet, {{% button href="https://www.akamai.com/infectionmonkey#download" icon="fas fa-download" %}}Get Infection Monkey here{{% /button %}}! If you haven't downloaded the Infection Monkey yet, {{% button href="https://www.guardicore.com/infectionmonkey/#download" icon="fas fa-download" %}}Get Infection Monkey here{{% /button %}}!

View File

@ -8,7 +8,5 @@ description: "Configure settings related to the Monkey's network activity."
Here you can control multiple important settings, such as: Here you can control multiple important settings, such as:
* Network propagation depth - How many hops from the base machine will the Infection Monkey spread? * Network propagation depth - How many hops from the base machine will the Infection Monkey spread?
* Scan Agent's networks - Should the Infection Monkey attempt to attack any machine in its subnet? * Local network scan - Should the Infection Monkey attempt to attack any machine in its subnet?
_Be careful when using this option. If a machine is connected to a public network, then the agent will scan the public network!_
* Scanner IP/subnet list - Which specific IP ranges should the Infection Monkey should try to attack? * Scanner IP/subnet list - Which specific IP ranges should the Infection Monkey should try to attack?

View File

@ -6,7 +6,7 @@ weight: 100
pre: "<i class='fas fa-certificate'></i> " pre: "<i class='fas fa-certificate'></i> "
--- ---
The official distribution of Infection Monkey is compiled and supplied by Guardicore ([download from our official site here](https://www.akamai.com/infectionmonkey#download)). The team signs all software packages to certify that a particular Infection Monkey package is a valid and unaltered Infection Monkey release. Before installing Monkey, you should validate the package using the SHA-256 checksum. The official distribution of Infection Monkey is compiled and supplied by Guardicore ([download from our official site here](https://www.guardicore.com/infectionmonkey/#download)). The team signs all software packages to certify that a particular Infection Monkey package is a valid and unaltered Infection Monkey release. Before installing Monkey, you should validate the package using the SHA-256 checksum.
## How to get SHA-256 checksum ## How to get SHA-256 checksum

View File

@ -1,5 +1,5 @@
--- ---
title: "Running the agent on AWS EC2 instances" title: "Running the monkey on AWS EC2 instances"
date: 2020-06-28T10:44:05+03:00 date: 2020-06-28T10:44:05+03:00
draft: false draft: false
description: "Use AWS SSM to execute Infection Monkey on your AWS instances." description: "Use AWS SSM to execute Infection Monkey on your AWS instances."
@ -28,7 +28,7 @@ In order for the Infection Monkey to successfully view your instances, you'll ne
#### Creating a custom IAM role #### Creating a custom IAM role
Go to the [AWS IAM roles dashboard](https://console.aws.amazon.com/iam/home?#/roles) and create a new IAM role for EC2. The role will need to have some specific permissions (see Appendix A), but you can just create a role with the `AmazonEC2RoleforSSM`, `AWSSecurityHubFullAccess` and `AmazonSSMFullAccess` pre-made permissions. In the end it should look something like this: Go to the [AWS IAM roles dashboard](https://console.aws.amazon.com/iam/home?#/roles) and create a new IAM role for EC2. The role will need to have some specific permissions (see Appendix A), but you can just create a role with the `AmazonEC2RoleforSSM`, `AWSSecurityHubFullAccess` and `AmazonSSMFullAccess` pre-made permissions. In the end it should like something like this:
![Creating a custom IAM role](/images/usage/integrations/monkey-island-aws-screenshot-3.png "Creating a custom IAM role") ![Creating a custom IAM role](/images/usage/integrations/monkey-island-aws-screenshot-3.png "Creating a custom IAM role")
@ -50,10 +50,6 @@ If your EC2 instances don't have the _SSM agent_ installed, they will not be abl
See [Amazon's documentation about working with SSM agents](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) for more details on how to check if you have an SSM agent and how to manually install one if you don't yet have it. See [Amazon's documentation about working with SSM agents](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) for more details on how to check if you have an SSM agent and how to manually install one if you don't yet have it.
### Firewall rules
Make sure that all machines that will run the Monkey agent can access the Island(port 5000).
## Usage ## Usage
### Running the Infection Monkey ### Running the Infection Monkey
@ -72,7 +68,7 @@ After you click on **Run on AWS machine of your choice** you can choose one of t
## Notes ## Notes
- The machines that can use IAM roles and be listed MUST be internet connected (or you can set up a proxy for IAM). This is standard AWS practice and you can read about it (and about how to set up the required proxy machines) in the AWS IAM documentation. - The machines which can use IAM roles and be listed MUST be internet connected (or you can set up a proxy for IAM). This is standard AWS practice and you can read about it (and about how to set up the required proxy machines) in the AWS IAM documentation.
- You can view the Infection Monkey in [the AWS marketplace](https://aws.amazon.com/marketplace/pp/B07B3J7K6D). - You can view the Infection Monkey in [the AWS marketplace](https://aws.amazon.com/marketplace/pp/B07B3J7K6D).
### Appendix A: Specific policy permissions required ### Appendix A: Specific policy permissions required

View File

@ -1,33 +0,0 @@
---
title: "Running Manually"
date: 2022-06-09T14:47:40+03:00
draft: false
weight: 2
pre: "<i class='fas fa-terminal'></i> "
tags: ["usage"]
---
## Generating manual run command
A command to run the agent manually can be generated by the Island Server UI by
going to the "Run Monkey" -> "Manual" page.
### Downloading the agent manually
Agent binaries can be downloaded by sending a `GET` request to
`https://[IP]:5000/api/agent/download/[OS]`, where `[IP]` stands for the IP
address of the Island server and `[OS]` is either `windows` or `linux`.
### Running the agent binary
The agent binary must be started with either the `m0nk3y` or `dr0pp3r` flag.
The `m0nk3y` flag is the standard way to run the agent.
The `dr0pp3r` flag invokes the agent dropper. The dropper will move the agent
binary to a location provided with the `-l` flag. Then, it will start the agent
with the `m0nk3y` flag as a new process. Finally, the dropper will shut itself
down. This flag is useful if you want to detach the agent from an exploited
service or parent process. It alleviates the need for the parent process to
wait until the agent finishes.

View File

@ -9,16 +9,16 @@ weight: 5
## Overview ## Overview
Numerous attack techniques (from phishing to dumpster diving) might result in a credential leak, Numerous attack techniques (from phishing to dumpster diving) might result in a credential leak,
which can be **extremely costly** as demonstrated in our report [IResponse to IEncrypt](https://web.archive.org/web/20210117224801/https://www.guardicore.com/2019/04/iresponse-to-iencrypt/). which can be **extremely costly** as demonstrated in our report [IResponse to IEncrypt](https://www.guardicore.com/2019/04/iresponse-to-iencrypt/).
The Infection Monkey can help you assess the impact of stolen credentials by automatically searching The Infection Monkey can help you assess the impact of stolen credentials by automatically searching
where bad actors can reuse these credentials in your network. where bad actors can reuse these credentials in your network.
## Configuration ## Configuration
- **Propagation -> Credentials** After setting up the Monkey Island, add your users' **real** credentials - **Exploits -> Credentials** After setting up the Monkey Island, add your users' **real** credentials
(usernames and passwords) here. Don't worry; this sensitive data is not accessible, distributed or used in any way other than being sent to the Infection Monkey agents. You can easily eliminate it by resetting the configuration of your Monkey Island. (usernames and passwords) here. Don't worry; this sensitive data is not accessible, distributed or used in any way other than being sent to the Infection Monkey agents. You can easily eliminate it by resetting the configuration of your Monkey Island.
- **Propagation -> Credentials -> SSH key pairs list** When enabled, the Infection Monkey automatically gathers SSH keys on the current system. - **Internal -> Exploits -> SSH keypair list** When enabled, the Infection Monkey automatically gathers SSH keys on the current system.
For this to work, the Monkey Island or initial agent needs to access SSH key files. For this to work, the Monkey Island or initial agent needs to access SSH key files.
To make sure SSH keys were gathered successfully, refresh the page and check this configuration value after you run the Infection Monkey To make sure SSH keys were gathered successfully, refresh the page and check this configuration value after you run the Infection Monkey
(content of keys will not be displayed, it will appear as `<Object>`). (content of keys will not be displayed, it will appear as `<Object>`).

View File

@ -8,21 +8,24 @@ weight: 3
## Overview ## Overview
From the [Hex-Men campaign](https://web.archive.org/web/20210115171355/https://www.guardicore.com/2017/12/beware-the-hex-men/) that hit From the [Hex-Men campaign](https://www.guardicore.com/2017/12/beware-the-hex-men/) that hit
internet-facing DB servers to a [cryptomining operation that attacks WordPress sites](https://web.archive.org/web/20210115185135/https://www.guardicore.com/2018/06/operation-prowli-traffic-manipulation-cryptocurrency-mining-2/) or any other malicious campaign attackers are now trying to go deeper into your network. internet-facing DB servers to a [cryptomining operation that attacks WordPress sites](https://www.guardicore.com/2018/06/operation-prowli-traffic-manipulation-cryptocurrency-mining-2/) or any other malicious campaign attackers are now trying to go deeper into your network.
Infection Monkey will help you assess the impact of a future breach by attempting to propagate within your internal network using service vulnerabilities, brute-forcing and other safe exploiters. Infection Monkey will help you assess the impact of a future breach by attempting to propagate within your internal network using service vulnerabilities, brute-forcing and other safe exploiters.
## Configuration ## Configuration
- **Propagation -> Exploiters** Here you can review the exploits the Infection Monkey will be using. By default all - **Exploits -> Exploits** Here you can review the exploits the Infection Monkey will be using. By default all
safe exploiters are selected. safe exploiters are selected.
- **Propagation -> Credentials** This configuration value will be used for brute-forcing. The Infection Monkey uses the most popular default passwords and usernames, but feel free to adjust it according to the default passwords common in your network. Keep in mind a longer list means longer scanning times. - **Exploits -> Credentials** This configuration value will be used for brute-forcing. The Infection Monkey uses the most popular default passwords and usernames, but feel free to adjust it according to the default passwords common in your network. Keep in mind a longer list means longer scanning times.
- **Propagation -> Network analysis -> Network** Make sure to properly configure the scope of the scan. You can select **Scan Agent's networks** - **Network -> Scope** Make sure to properly configure the scope of the scan. You can select **Local network scan**
and allow Monkey to propagate until maximum **Scan depth**(hop count) is reached, or you can fine tune it by providing and allow Monkey to propagate until maximum **Scan depth**(hop count) is reached, or you can fine tune it by providing
specific network ranges in **Scan target list**. Scanning a local network is more realistic, but providing specific specific network ranges in **Scan target list**. Scanning a local network is more realistic, but providing specific
targets will make the scanning process substantially faster. targets will make the scanning process substantially faster.
- **(Optional) Propagation -> Network Analysis -> TCP scanner** Here you can add custom ports your organization is using. - **(Optional) Internal -> Network -> TCP scanner** Here you can add custom ports your organization is using.
- **(Optional) Monkey -> Post-Breach Actions** If you only want to test propagation in the network, you can turn off
all post-breach actions. These actions simulate an attacker's behavior after getting access to a new system but in no
way helps the Infection Monkey exploit new machines.
![Exploiter selector](/images/usage/use-cases/network-breach.PNG "Exploiter selector") ![Exploiter selector](/images/usage/use-cases/network-breach.PNG "Exploiter selector")

View File

@ -10,24 +10,25 @@ weight: 4
Segmentation is a method of creating secure zones in data centers and cloud deployments. It allows organizations to isolate workloads from one another and secure them individually, typically using policies. A useful way to test your company's segmentation effectiveness is to ensure that your network segments are properly separated (e.g., your development environment is isolated from your production environment and your applications are isolated from one another). Segmentation is a method of creating secure zones in data centers and cloud deployments. It allows organizations to isolate workloads from one another and secure them individually, typically using policies. A useful way to test your company's segmentation effectiveness is to ensure that your network segments are properly separated (e.g., your development environment is isolated from your production environment and your applications are isolated from one another).
[Segmentation is key](https://www.akamai.com/products/akamai-segmentation/use-cases) to protecting your network. It can reduce the network's attack surface and minimize the damage caused during a breach. [Segmentation is key](https://www.guardicore.com/use-cases/micro-segmentation/) to protecting your network. It can reduce the network's attack surface and minimize the damage caused during a breach.
You can use the Infection Monkey's cross-segment traffic feature to verify that your network segmentation configuration is adequate. This way, you can ensure that, even if a bad actor breaches your defenses, they can't move laterally between segments. You can use the Infection Monkey's cross-segment traffic feature to verify that your network segmentation configuration is adequate. This way, you can ensure that, even if a bad actor breaches your defenses, they can't move laterally between segments.
## Configuration ## Configuration
- **Propagation -> Network analysis -> Network segmentation testing** This configuration setting allows you to define - **Network -> Network analysis -> Network segmentation testing** This configuration setting allows you to define
subnets that should be segregated from each other. If any of the provided networks can reach each other, you'll see it subnets that should be segregated from each other. If any of the provided networks can reach each other, you'll see it
in the security report. in the security report.
- **(Optional) Propagation -> Network analysis -> Network** You can disable **Scan Agent's networks** and leave all other options at the default setting if you only want to test for network segmentation without any lateral movement. - **(Optional) Network -> Scope** You can disable **Local network scan** and leave all other options at the default setting if you only want to test for network segmentation without any lateral movement.
- **(Optional) Monkey -> Post-Breach Actions** If you only want to test segmentation in the network, you can turn off all post-breach actions. These actions simulate an attacker's behavior after getting access to a new system, so they might trigger your defense solutions and interrupt the segmentation test.
## Suggested run mode ## Suggested run mode
Execute The Infection Monkey on machines in different subnetworks using the “Manual” run option. Execute The Infection Monkey on machines in different subnetworks using the “Manual” run option.
Note that if the Infection Monkey can't communicate to the Monkey Island, it will Note that if the Infection Monkey can't communicate to the Monkey Island, it will
not be able to send scan results, so make sure all machines can reach the Monkey Island. not be able to send scan results, so make sure all machines can reach the the Monkey Island.
![How to configure network segmentation testing](/images/usage/scenarios/segmentation-config.png "How to configure network segmentation testing") ![How to configure network segmentation testing](/images/usage/scenarios/segmentation-config.png "How to configure network segmentation testing")

View File

@ -9,26 +9,37 @@ weight: 100
## Overview ## Overview
This page provides additional information about configuring the Infection Monkey, tips and tricks and creative usage scenarios. This page provides additional information about configuring the Infection Monkey, tips and tricks and creative usage scenarios.
## Custom behaviour
If you want the Infection Monkey to run a specific script or tool after it breaches a machine, you can configure it in
**Configuration -> Monkey -> Post-breach**. Input commands you want to execute in the corresponding fields.
You can also upload files and call them through the commands you entered.
## Accelerate the test ## Accelerate the test
To improve scanning speed you could **specify a subnet instead of scanning all of the local network**. To improve scanning speed you could **specify a subnet instead of scanning all of the local network**.
The following configuration values also have an impact on scanning speed: The following configuration values also have an impact on scanning speed:
- **Propagation -> Credentials** - The more usernames and passwords you input, the longer it will take the Infection Monkey to scan machines that have - **Credentials** - The more usernames and passwords you input, the longer it will take the Infection Monkey to scan machines that have
remote access services. The Infection Monkey agents try to stay elusive and leave a low impact, and thus brute-forcing takes longer than with loud conventional tools. remote access services. The Infection Monkey agents try to stay elusive and leave a low impact, and thus brute-forcing takes longer than with loud conventional tools.
- **Propagation -> Network analysis -> Network** - Scanning large networks with a lot of propagations can become unwieldy. Instead, try to scan your - **Network scope** - Scanning large networks with a lot of propagations can become unwieldy. Instead, try to scan your
networks bit by bit with multiple runs. networks bit by bit with multiple runs.
- **Propagation -> Network analysis -> TCP scanner** - Here you can trim down the list of ports the Infection Monkey tries to scan, improving performance. - **Post-breach actions** - If you only care about propagation, you can disable most of these.
- **Internal -> TCP scanner** - Here you can trim down the list of ports the Infection Monkey tries to scan, improving performance.
## Combining different scenarios ## Combining different scenarios
The Infection Monkey is not limited to the scenarios mentioned in this section. Once you get the hang of configuring it, you might come up with your own use case or test all of the suggested scenarios at the same time! Whatever you do, the Infection Monkey's Security, ATT&CK and Zero Trust reports will be waiting for you with your results! The Infection Monkey is not limited to the scenarios mentioned in this section. Once you get the hang of configuring it, you might come up with your own use case or test all of the suggested scenarios at the same time! Whatever you do, the Infection Monkey's Security, ATT&CK and Zero Trust reports will be waiting for you with your results!
## Persistent scanning
Use **Monkey -> Persistent** scanning configuration section to either run periodic scans or increase the reliability of exploitations by running consecutive scans with the Infection Monkey.
## Credentials ## Credentials
Every network has its old "skeleton keys" that it should have long discarded. Configuring the Infection Monkey with old and stale passwords will enable you to ensure they were really discarded. Every network has its old "skeleton keys" that it should have long discarded. Configuring the Infection Monkey with old and stale passwords will enable you to ensure they were really discarded.
To add the old passwords, go to the Monkey Island's **Exploit password list** under **Propagation -> Credentials** and use the "+" button to add the old passwords to the configuration. For example, here we added a few extra passwords (and a username as well) to the configuration: To add the old passwords, go to the Monkey Island's **Exploit password list** under **Basic - Credentials** and use the "+" button to add the old passwords to the configuration. For example, here we added a few extra passwords (and a username as well) to the configuration:
![Exploit password and user lists](/images/usage/scenarios/user-password-lists.png "Exploit password and user lists") ![Exploit password and user lists](/images/usage/scenarios/user-password-lists.png "Exploit password and user lists")

View File

@ -13,9 +13,9 @@ Want to assess your progress in achieving a Zero Trust network? The Infection Mo
## Configuration ## Configuration
- **Propagation -> Credentials** This configuration value will be used for brute-forcing. The Infection Monkey uses the most popular default passwords and usernames, but feel free to adjust it according to the default passwords common in your network. Keep in mind a longer list means longer scanning times. - **Exploits -> Credentials** This configuration value will be used for brute-forcing. The Infection Monkey uses the most popular default passwords and usernames, but feel free to adjust it according to the default passwords common in your network. Keep in mind a longer list means longer scanning times.
- **Propagation -> Network analysis -> Network** Disable “Scan Agent's networks” and instead provide specific network ranges in the “Scan target list.” - **Network -> Scope** Disable “Local network scan” and instead provide specific network ranges in the “Scan target list.”
- **Propagation -> Network analysis -> Network segmentation testing** This configuration setting allows you to define - **Network -> Network analysis -> Network segmentation testing** This configuration setting allows you to define
subnets that should be segregated from each other. subnets that should be segregated from each other.
In general, other configuration value defaults should be good enough, but feel free to see the “Other” section for tips and tricks about more features and in-depth configuration parameters you can use. In general, other configuration value defaults should be good enough, but feel free to see the “Other” section for tips and tricks about more features and in-depth configuration parameters you can use.

View File

@ -37,25 +37,19 @@ To ensure minimum interference and easy recoverability, the ransomware
simulation will only encrypt files contained in a user-specified directory. If simulation will only encrypt files contained in a user-specified directory. If
no directory is specified, no files will be encrypted. no directory is specified, no files will be encrypted.
Infection Monkey appends the `.m0nk3y` file extension to files that it
encrypts. You may optionally provide a custom file extension for Infection
Monkey to use instead. You can even provide no file extension, but take
caution: you'll no longer be able to tell if the file has been encrypted based
on the filename alone!
![Ransomware configuration](/images/usage/scenarios/ransomware-config.png "Ransomware configuration") ![Ransomware configuration](/images/usage/scenarios/ransomware-config.png "Ransomware configuration")
### How are the files encrypted? ### How are the files encrypted?
Files are "encrypted" in place with a simple bit flip. Encrypted files are Files are "encrypted" in place with a simple bit flip. Encrypted files are
renamed to have a file extension (`.m0nk3y` by default) appended to their renamed to have `.m0nk3y` appended to their names. This is a safe way to
names. This is a safe way to simulate encryption since it is easy to "decrypt" simulate encryption since it is easy to "decrypt" your files. You can simply
your files. You can simply perform a bit flip on the files again and rename perform a bit flip on the files again and rename them to remove the appended
them to remove the appended `.m0nk3y` extension. `.m0nk3y` extension.
Flipping a file's bits is sufficient to simulate the encryption behavior of Flipping a file's bits is sufficient to simulate the encryption behavior of
ransomware, as the data in your files has been manipulated (leaving them ransomware, as the data in your files has been manipulated (leaving them
temporarily unusable). Files are then renamed with a new extension appended, temporarily unusuable). Files are then renamed with a new extension appended,
which is similar to the way that many ransomwares behave. As this is a which is similar to the way that many ransomwares behave. As this is a
simulation, your simulation, your
security solutions should be triggered to notify you or prevent these changes security solutions should be triggered to notify you or prevent these changes

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 KiB

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -1 +1,2 @@
logs/ logs/
/blackbox/tests/performance/telemetry_sample

View File

@ -16,6 +16,8 @@ Either run pytest from `/monkey` directory or set `PYTHONPATH` environment varia
Blackbox tests have following parameters: Blackbox tests have following parameters:
- `--island=IP` Sets island's IP - `--island=IP` Sets island's IP
- `--no-gcp` (Optional) Use for no interaction with the cloud (local test). - `--no-gcp` (Optional) Use for no interaction with the cloud (local test).
- `--quick-performance-tests` (Optional) If enabled performance tests won't reset island and won't send telemetries,
instead will just test performance of endpoints in already present island state.
Example run command: Example run command:
@ -24,3 +26,26 @@ Example run command:
#### Running in PyCharm #### Running in PyCharm
Configure a PyTest configuration with the additional arguments `-s --island=35.207.152.72:5000`, 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
**Before running performance test make sure browser is not sending requests to island!**
To run telemetry performance test follow these steps:
0. Set no password protection on the island.
Make sure the island parameter is an IP address(not localhost) as the name resolution will increase the time for requests.
1. Gather monkey telemetries.
1. Enable "Export monkey telemetries" in Configuration -> Internal -> Tests if you don't have
exported telemetries already.
2. Run monkey and wait until infection is done.
3. All telemetries are gathered in `monkey/telem_sample`. If not, restart the island process.
2. Run telemetry performance test.
1. Move directory `monkey/telem_sample` to `envs/monkey_zoo/blackbox/tests/performance/telemetry_sample`
2. (Optional) Use `envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py` to multiply
telemetries gathered.
1. Run `sample_multiplier.py` script with working directory set to `monkey\envs\monkey_zoo\blackbox`
2. Pass integer to indicate the multiplier. For example running `telem_parser.py 4` will replicate
telemetries 4 times.
3. If you're using pycharm check "Emulate terminal in output console" on debug/run configuration.
3. Add a `--run-performance-tests` flag to blackbox scripts to run performance tests as part of BlackBox tests.
You can run a single test separately by adding `-k 'test_telem_performance'` option.

View File

@ -1,35 +1,24 @@
from ipaddress import IPv4Address
from typing import Collection, Iterable
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
class CommunicationAnalyzer(Analyzer): class CommunicationAnalyzer(Analyzer):
def __init__(self, island_client: MonkeyIslandClient, machine_ips: Iterable[str]): def __init__(self, island_client, machine_ips):
self.island_client = island_client self.island_client = island_client
self.machine_ips = machine_ips self.machine_ips = machine_ips
self.log = AnalyzerLog(self.__class__.__name__) self.log = AnalyzerLog(self.__class__.__name__)
def analyze_test_results(self): def analyze_test_results(self):
self.log.clear() self.log.clear()
all_agents_communicated = True all_monkeys_communicated = True
agent_ips = self._get_agent_ips()
for machine_ip in self.machine_ips: for machine_ip in self.machine_ips:
if self._agent_communicated_back(machine_ip, agent_ips): if not self.did_monkey_communicate_back(machine_ip):
self.log.add_entry("Agent from {} communicated back".format(machine_ip)) self.log.add_entry("Monkey from {} didn't communicate back".format(machine_ip))
all_monkeys_communicated = False
else: else:
self.log.add_entry("Agent from {} didn't communicate back".format(machine_ip)) self.log.add_entry("Monkey from {} communicated back".format(machine_ip))
all_agents_communicated = False return all_monkeys_communicated
return all_agents_communicated def did_monkey_communicate_back(self, machine_ip):
query = {"ip_addresses": {"$elemMatch": {"$eq": machine_ip}}}
def _get_agent_ips(self) -> Collection[IPv4Address]: return len(self.island_client.find_monkeys_in_db(query)) > 0
agents = self.island_client.get_agents()
machines = self.island_client.get_machines()
return {i.ip for a in agents for i in machines[a.machine_id].network_interfaces}
def _agent_communicated_back(self, machine_ip: str, agent_ips: Collection[IPv4Address]) -> bool:
return IPv4Address(machine_ip) in agent_ips

View File

@ -0,0 +1,50 @@
import logging
from datetime import timedelta
from typing import Dict
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
LOGGER = logging.getLogger(__name__)
class PerformanceAnalyzer(Analyzer):
def __init__(
self, performance_test_config: PerformanceTestConfig, endpoint_timings: Dict[str, timedelta]
):
self.performance_test_config = performance_test_config
self.endpoint_timings = endpoint_timings
def analyze_test_results(self):
# Calculate total time and check each endpoint
single_page_time_less_then_max = True
total_time = timedelta()
for endpoint, elapsed in self.endpoint_timings.items():
total_time += elapsed
if elapsed > self.performance_test_config.max_allowed_single_page_time:
single_page_time_less_then_max = False
total_time_less_then_max = total_time < self.performance_test_config.max_allowed_total_time
PerformanceAnalyzer.log_slowest_endpoints(self.endpoint_timings)
LOGGER.info(f"Total time is {str(total_time)}")
performance_is_good_enough = total_time_less_then_max and single_page_time_less_then_max
if self.performance_test_config.break_on_timeout and not performance_is_good_enough:
LOGGER.warning(
"Calling breakpoint - pausing to enable investigation of island. "
"Type 'c' to continue once you're done "
"investigating. Type 'p timings' and 'p total_time' to see performance information."
)
breakpoint()
return performance_is_good_enough
@staticmethod
def log_slowest_endpoints(endpoint_timings, max_endpoints_to_display=100):
slow_endpoint_list = list(endpoint_timings.items())
slow_endpoint_list.sort(key=lambda x: x[1], reverse=True)
slow_endpoint_list = slow_endpoint_list[:max_endpoints_to_display]
for endpoint in slow_endpoint_list:
LOGGER.info(f"{endpoint[0]} took {str(endpoint[1])}")

View File

@ -1,7 +1,9 @@
from pprint import pformat from pprint import pformat
from typing import List from typing import List
from common.credentials import Credentials, LMHash, NTHash, Username import dpath.util
from common.config_value_paths import LM_HASH_LIST_PATH, NTLM_HASH_LIST_PATH, USER_LIST_PATH
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
@ -27,22 +29,17 @@ class ZerologonAnalyzer(Analyzer):
return is_creds_gathered and is_creds_restored return is_creds_gathered and is_creds_restored
def _analyze_credential_gathering(self) -> bool: def _analyze_credential_gathering(self) -> bool:
propagation_credentials = self.island_client.get_propagation_credentials() config = self.island_client.get_config()
credentials_on_island = ZerologonAnalyzer._get_relevant_credentials(propagation_credentials) credentials_on_island = ZerologonAnalyzer._get_relevant_credentials(config)
return self._is_all_credentials_in_list(credentials_on_island) return self._is_all_credentials_in_list(credentials_on_island)
@staticmethod @staticmethod
def _get_relevant_credentials(propagation_credentials: Credentials) -> List[str]: def _get_relevant_credentials(config: dict):
credentials_on_island = set() credentials_on_island = []
for credentials in propagation_credentials: credentials_on_island.extend(dpath.util.get(config["configuration"], USER_LIST_PATH))
if isinstance(credentials.identity, Username): credentials_on_island.extend(dpath.util.get(config["configuration"], NTLM_HASH_LIST_PATH))
credentials_on_island.update([credentials.identity.username]) credentials_on_island.extend(dpath.util.get(config["configuration"], LM_HASH_LIST_PATH))
if isinstance(credentials.secret, NTHash): return credentials_on_island
credentials_on_island.update([credentials.secret.nt_hash.get_secret_value()])
if isinstance(credentials.secret, LMHash):
credentials_on_island.update([credentials.secret.lm_hash.get_secret_value()])
return list(credentials_on_island)
def _is_all_credentials_in_list(self, all_creds: List[str]) -> bool: 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] credentials_missing = [cred for cred in self.expected_credentials if cred not in all_creds]

View File

@ -0,0 +1,15 @@
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
# Disables a lot of config values not required for a specific feature test
class BaseTemplate(ConfigTemplate):
config_values = {
"basic.exploiters.exploiter_classes": [],
"basic_network.scope.local_network_scan": False,
"basic_network.scope.depth": 1,
"internal.classes.finger_classes": ["HTTPFinger"],
"internal.monkey.system_info.system_info_collector_classes": [],
"monkey.post_breach.post_breach_actions": [],
"internal.general.keep_tunnel_open_time": 0,
}

View File

@ -0,0 +1,8 @@
from abc import ABC, abstractmethod
class ConfigTemplate(ABC):
@property
@abstractmethod
def config_values(self) -> dict:
pass

View File

@ -0,0 +1,42 @@
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 Depth1A(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
# Tests:
# Hadoop (10.2.2.2, 10.2.2.3)
# Log4shell (10.2.3.55, 10.2.3.56, 10.2.3.49, 10.2.3.50, 10.2.3.51, 10.2.3.52)
# MSSQL (10.2.2.16)
# SMB mimikatz password stealing and brute force (10.2.2.14 and 10.2.2.15)
config_values.update(
{
"basic.exploiters.exploiter_classes": [
"HadoopExploiter",
"Log4ShellExploiter",
"MSSQLExploiter",
"SmbExploiter",
"SSHExploiter",
],
"basic_network.scope.subnet_scan_list": [
"10.2.2.2",
"10.2.2.3",
"10.2.3.55",
"10.2.3.56",
"10.2.3.49",
"10.2.3.50",
"10.2.3.51",
"10.2.3.52",
"10.2.2.16",
"10.2.2.14",
"10.2.2.15",
],
"basic.credentials.exploit_password_list": ["Ivrrw5zEzs", "Xk8VDTsC"],
"basic.credentials.exploit_user_list": ["m0nk3y"],
"monkey.system_info.system_info_collector_classes": [
"MimikatzCollector",
],
}
)

View File

@ -0,0 +1,23 @@
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 Depth2A(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
# SSH password and key brute-force, key stealing (10.2.2.11, 10.2.2.12)
config_values.update(
{
"basic.exploiters.exploiter_classes": [
"SSHExploiter",
],
"basic_network.scope.subnet_scan_list": [
"10.2.2.11",
"10.2.2.12",
],
"basic_network.scope.depth": 2,
"basic.credentials.exploit_password_list": ["^NgDvY59~8"],
"basic.credentials.exploit_user_list": ["m0nk3y"],
}
)

View File

@ -0,0 +1,48 @@
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 Depth3A(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
# Tests:
# Powershell (10.2.3.45, 10.2.3.46, 10.2.3.47, 10.2.3.48)
# Tunneling (SSH brute force) (10.2.2.9, 10.2.1.10, 10.2.0.12, 10.2.0.11)
# WMI pass the hash (10.2.2.15)
config_values.update(
{
"basic.exploiters.exploiter_classes": [
"PowerShellExploiter",
"SSHExploiter",
"WmiExploiter",
],
"basic_network.scope.subnet_scan_list": [
"10.2.2.9",
"10.2.3.45",
"10.2.3.46",
"10.2.3.47",
"10.2.3.48",
"10.2.1.10",
"10.2.0.12",
"10.2.0.11",
"10.2.2.15",
],
"basic.credentials.exploit_password_list": [
"Passw0rd!",
"3Q=(Ge(+&w]*",
"`))jU7L(w}",
"t67TC5ZDmz",
],
"basic_network.scope.depth": 3,
"internal.general.keep_tunnel_open_time": 20,
"basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.exploits.exploit_ntlm_hash_list": [
"d0f0132b308a0c4e5d1029cc06f48692",
"5da0889ea2081aa79f6852294cba4a5e",
"50c9987a6bf1ac59398df9f911122c9b",
],
}
)

View File

@ -0,0 +1,18 @@
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 Hadoop(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["HadoopExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.2", "10.2.2.3"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [8088],
}
)

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 Log4jLogstash(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["Log4ShellExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.3.55", "10.2.3.56"],
}
)

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 Log4jSolr(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["Log4ShellExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.3.49", "10.2.3.50"],
}
)

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 Log4jTomcat(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["Log4ShellExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.3.51", "10.2.3.52"],
}
)

View File

@ -0,0 +1,25 @@
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 Mssql(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["MSSQLExploiter"],
"internal.classes.finger_classes": [],
"basic_network.scope.subnet_scan_list": ["10.2.2.16"],
"basic.credentials.exploit_password_list": [
"Password1!",
"Xk8VDTsC",
"password",
"12345678",
],
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [3389],
}
)

View File

@ -0,0 +1,60 @@
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
class Performance(ConfigTemplate):
config_values = {
"basic.credentials.exploit_password_list": [
"Xk8VDTsC",
"^NgDvY59~8",
"Ivrrw5zEzs",
"3Q=(Ge(+&w]*",
"`))jU7L(w}",
"t67TC5ZDmz",
],
"basic.credentials.exploit_user_list": ["m0nk3y"],
"basic.exploiters.exploiter_classes": [
"SmbExploiter",
"WmiExploiter",
"SSHExploiter",
"HadoopExploiter",
"MSSQLExploiter",
"PowerShellExploiter",
"ZerologonExploiter",
"Log4ShellExploiter",
],
"basic_network.network_analysis.inaccessible_subnets": [
"10.2.2.0/30",
"10.2.2.8/30",
"10.2.2.21/32",
"10.2.2.19/32",
"10.2.2.18/32",
"10.2.2.17/32",
],
"basic_network.scope.subnet_scan_list": [
"10.2.2.2",
"10.2.2.3",
"10.2.2.4",
"10.2.2.5",
"10.2.2.8",
"10.2.2.9",
"10.2.1.10",
"10.2.0.11",
"10.2.0.12",
"10.2.2.11",
"10.2.2.12",
"10.2.2.14",
"10.2.2.15",
"10.2.2.16",
"10.2.2.18",
"10.2.2.19",
"10.2.2.20",
"10.2.2.21",
"10.2.2.25",
"10.2.3.55",
"10.2.3.56",
"10.2.3.49",
"10.2.3.50",
"10.2.3.51",
"10.2.3.52",
],
}

View File

@ -0,0 +1,29 @@
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 PowerShell(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["PowerShellExploiter"],
"basic_network.scope.subnet_scan_list": [
"10.2.3.45",
"10.2.3.46",
"10.2.3.47",
"10.2.3.48",
],
"basic.credentials.exploit_password_list": ["Passw0rd!"],
"basic_network.scope.depth": 2,
"basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"],
"internal.classes.finger_classes": [],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [5985, 5986],
"internal.exploits.exploit_ntlm_hash_list": [
"d0f0132b308a0c4e5d1029cc06f48692",
],
}
)

View File

@ -0,0 +1,21 @@
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 PowerShellCredentialsReuse(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["PowerShellExploiter"],
"basic_network.scope.subnet_scan_list": [
"10.2.3.46",
],
"basic_network.scope.depth": 2,
"internal.classes.finger_classes": [],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [5985, 5986],
}
)

View File

@ -0,0 +1,23 @@
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 SmbMimikatz(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["SmbExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.14", "10.2.2.15"],
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
"internal.classes.finger_classes": ["SMBFinger", "HTTPFinger"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [445],
"monkey.system_info.system_info_collector_classes": [
"MimikatzCollector",
],
}
)

View File

@ -0,0 +1,24 @@
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 SmbPth(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["SmbExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.15"],
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
"internal.classes.finger_classes": ["SMBFinger", "HTTPFinger"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [445],
"internal.classes.exploits.exploit_ntlm_hash_list": [
"5da0889ea2081aa79f6852294cba4a5e",
"50c9987a6bf1ac59398df9f911122c9b",
],
}
)

View File

@ -0,0 +1,21 @@
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(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["SSHExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.11", "10.2.2.12"],
"basic.credentials.exploit_password_list": ["Password1!", "12345678", "^NgDvY59~8"],
"basic_network.scope.depth": 2,
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
"internal.classes.finger_classes": ["SSHFinger"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [22],
}
)

View File

@ -0,0 +1,35 @@
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(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["SmbExploiter", "WmiExploiter", "SSHExploiter"],
"basic_network.scope.subnet_scan_list": [
"10.2.2.9",
"10.2.1.10",
"10.2.0.12",
"10.2.0.11",
],
"basic_network.scope.depth": 3,
"internal.general.keep_tunnel_open_time": 20,
"basic.credentials.exploit_password_list": [
"Password1!",
"3Q=(Ge(+&w]*",
"`))jU7L(w}",
"t67TC5ZDmz",
"12345678",
],
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
"internal.classes.finger_classes": [
"SSHFinger",
"HTTPFinger",
"SMBFinger",
],
}
)

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 WmiMimikatz(ConfigTemplate):
config_values = copy(BaseTemplate.config_values)
config_values.update(
{
"basic.exploiters.exploiter_classes": ["WmiExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.14", "10.2.2.15"],
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [135],
"monkey.system_info.system_info_collector_classes": [
"MimikatzCollector",
],
}
)

View File

@ -0,0 +1,24 @@
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": ["HTTPFinger"],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [135],
"internal.exploits.exploit_ntlm_hash_list": [
"5da0889ea2081aa79f6852294cba4a5e",
"50c9987a6bf1ac59398df9f911122c9b",
],
}
)

View File

@ -0,0 +1,20 @@
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", "SmbExploiter"],
"basic_network.scope.subnet_scan_list": ["10.2.2.25"],
# Empty list to make sure ZeroLogon adds "Administrator" username
"basic.credentials.exploit_user_list": [],
"internal.network.tcp_scanner.HTTP_PORTS": [],
"internal.network.tcp_scanner.tcp_target_ports": [135, 445],
}
)

View File

@ -1,9 +1,5 @@
from typing import Collection, Dict, Mapping, Set
import pytest import pytest
from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_SINGLE_TEST_LIST
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption( parser.addoption(
@ -18,6 +14,25 @@ def pytest_addoption(parser):
default=False, default=False,
help="Use for no interaction with the cloud.", help="Use for no interaction with the cloud.",
) )
parser.addoption(
"--quick-performance-tests",
action="store_true",
default=False,
help="If enabled performance tests won't reset island and won't send telemetries, "
"instead will just test performance of already present island state.",
)
parser.addoption(
"--run-performance-tests",
action="store_true",
default=False,
help="If enabled performance tests will be run.",
)
parser.addoption(
"--skip-powershell-reuse",
action="store_true",
default=False,
help="Use to run PowerShell credentials reuse test.",
)
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
@ -31,14 +46,22 @@ def no_gcp(request):
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def gcp_machines_to_start(request: pytest.FixtureRequest) -> Mapping[str, Collection[str]]: def quick_performance_tests(request):
machines_to_start: Dict[str, Set[str]] = {} return request.config.getoption("--quick-performance-tests")
enabled_tests = (test.name for test in request.node.items)
machines_for_enabled_tests = (GCP_SINGLE_TEST_LIST[test] for test in enabled_tests)
for machine_dict in machines_for_enabled_tests: def pytest_runtest_setup(item):
for zone, machines in machine_dict.items(): if "run_performance_tests" in item.keywords and not item.config.getoption(
machines_to_start.setdefault(zone, set()).update(machines) "--run-performance-tests"
):
pytest.skip(
"Skipping performance test because " "--run-performance-tests flag isn't specified."
)
return machines_to_start if "skip_powershell_reuse" in item.keywords and item.config.getoption(
"--skip-powershell-reuse"
):
pytest.skip(
"Skipping powershell credentials reuse test because "
"--skip-powershell-cached flag isn't specified."
)

View File

@ -11,18 +11,13 @@ GCP_TEST_MACHINE_LIST = {
"tunneling-10", "tunneling-10",
"tunneling-11", "tunneling-11",
"tunneling-12", "tunneling-12",
"tunneling-13",
"zerologon-25", "zerologon-25",
], ],
"europe-west1-b": [ "europe-west1-b": [
"powershell-3-44",
"powershell-3-45", "powershell-3-45",
"powershell-3-46", "powershell-3-46",
"powershell-3-47", "powershell-3-47",
"powershell-3-48", "powershell-3-48",
"credentials-reuse-14",
"credentials-reuse-15",
"credentials-reuse-16",
"log4j-logstash-55", "log4j-logstash-55",
"log4j-logstash-56", "log4j-logstash-56",
"log4j-solr-49", "log4j-solr-49",
@ -31,84 +26,3 @@ GCP_TEST_MACHINE_LIST = {
"log4j-tomcat-52", "log4j-tomcat-52",
], ],
} }
DEPTH_2_A = {
"europe-west3-a": [
"sshkeys-11",
"sshkeys-12",
],
"europe-west1-b": [
"powershell-3-46",
"powershell-3-44",
],
}
DEPTH_1_A = {
"europe-west3-a": ["hadoop-2", "hadoop-3", "mssql-16", "mimikatz-14", "mimikatz-15"],
"europe-west1-b": [
"log4j-logstash-55",
"log4j-logstash-56",
"log4j-solr-49",
"log4j-solr-50",
"log4j-tomcat-51",
"log4j-tomcat-52",
],
}
DEPTH_3_A = {
"europe-west3-a": [
"tunneling-9",
"tunneling-10",
"tunneling-11",
"mimikatz-15",
],
"europe-west1-b": [
"powershell-3-45",
"powershell-3-47",
"powershell-3-48",
],
}
DEPTH_4_A = {
"europe-west3-a": [
"tunneling-9",
"tunneling-10",
"tunneling-12",
"tunneling-13",
],
}
ZEROLOGON = {
"europe-west3-a": [
"zerologon-25",
],
}
CREDENTIALS_REUSE_SSH_KEY = {
"europe-west1-b": [
"credentials-reuse-14",
"credentials-reuse-15",
"credentials-reuse-16",
],
}
WMI_AND_MIMIKATZ = {
"europe-west3-a": [
"mimikatz-14",
"mimikatz-15",
]
}
SMB_PTH = {"europe-west3-a": ["mimikatz-15"]}
GCP_SINGLE_TEST_LIST = {
"test_depth_2_a": DEPTH_2_A,
"test_depth_1_a": DEPTH_1_A,
"test_depth_3_a": DEPTH_3_A,
"test_depth_4_a": DEPTH_4_A,
"test_zerologon_exploiter": ZEROLOGON,
"test_credentials_reuse_ssh_key": CREDENTIALS_REUSE_SSH_KEY,
"test_wmi_and_mimikatz_exploiters": WMI_AND_MIMIKATZ,
"test_smb_pth": SMB_PTH,
}

View File

@ -0,0 +1,29 @@
import json
import dpath.util
from typing_extensions import Type
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
class IslandConfigParser:
@staticmethod
def get_raw_config(
config_template: Type[ConfigTemplate], island_client: MonkeyIslandClient
) -> str:
response = island_client.get_config()
config = IslandConfigParser.apply_template_to_config(
config_template, response["configuration"]
)
return json.dumps(config)
@staticmethod
def apply_template_to_config(config_template: Type[ConfigTemplate], config: dict) -> dict:
for path, value in config_template.config_values.items():
dpath.util.set(config, path, value, ".")
return config
@staticmethod
def get_ips_of_targets(raw_config):
return dpath.util.get(json.loads(raw_config), "basic_network.scope.subnet_scan_list", ".")

View File

@ -1,21 +1,16 @@
import json import json
import logging import logging
import time import time
from typing import List, Mapping, Sequence, Union from typing import Union
from bson import json_util from bson import json_util
from common.credentials import Credentials
from common.types import AgentID, MachineID
from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests
from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration
from monkey_island.cc.models import Agent, Machine
SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5 SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5
GET_AGENTS_ENDPOINT = "api/agents" MONKEY_TEST_ENDPOINT = "api/test/monkey"
GET_LOG_ENDPOINT = "api/agent-logs"
GET_MACHINES_ENDPOINT = "api/machines"
TELEMETRY_TEST_ENDPOINT = "api/test/telemetry" TELEMETRY_TEST_ENDPOINT = "api/test/telemetry"
LOG_TEST_ENDPOINT = "api/test/log"
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -31,54 +26,16 @@ class MonkeyIslandClient(object):
def get_api_status(self): def get_api_status(self):
return self.requests.get("api") return self.requests.get("api")
def get_propagation_credentials(self) -> Sequence[Credentials]: def get_config(self):
response = self.requests.get("api/propagation-credentials") return json.loads(self.requests.get("api/configuration/island").content)
return [Credentials(**credentials) for credentials in response.json()]
@avoid_race_condition @avoid_race_condition
def import_config(self, test_configuration: TestConfiguration): def import_config(self, config_contents):
self._set_island_mode() _ = self.requests.post("api/configuration/island", data=config_contents)
self._import_config(test_configuration)
self._import_credentials(test_configuration.propagation_credentials)
@avoid_race_condition
def _set_island_mode(self):
if self.requests.put_json("api/island/mode", json="advanced").ok:
LOGGER.info("Setting island mode to Custom.")
else:
LOGGER.error("Failed to set island mode")
assert False
@avoid_race_condition
def _import_config(self, test_configuration: TestConfiguration):
response = self.requests.put_json(
"api/agent-configuration",
json=test_configuration.agent_configuration.dict(simplify=True),
)
if response.ok:
LOGGER.info("Configuration is imported.")
else:
LOGGER.error(f"Failed to import config: {response}")
assert False
@avoid_race_condition
def _import_credentials(self, propagation_credentials: List[Credentials]):
serialized_propagation_credentials = [
credentials.dict(simplify=True) for credentials in propagation_credentials
]
response = self.requests.put_json(
"/api/propagation-credentials/configured-credentials",
json=serialized_propagation_credentials,
)
if response.ok:
LOGGER.info("Credentials are imported.")
else:
LOGGER.error(f"Failed to import credentials: {response}")
assert False
@avoid_race_condition @avoid_race_condition
def run_monkey_local(self): def run_monkey_local(self):
response = self.requests.post_json("api/local-monkey", json={"action": "run"}) response = self.requests.post_json("api/local-monkey", data={"action": "run"})
if MonkeyIslandClient.monkey_ran_successfully(response): if MonkeyIslandClient.monkey_ran_successfully(response):
LOGGER.info("Running the monkey.") LOGGER.info("Running the monkey.")
else: else:
@ -91,9 +48,8 @@ class MonkeyIslandClient(object):
@avoid_race_condition @avoid_race_condition
def kill_all_monkeys(self): def kill_all_monkeys(self):
# TODO change this request, because monkey-control resource got removed
response = self.requests.post_json( response = self.requests.post_json(
"api/agent-signals/terminate-all-agents", json={"terminate_time": time.time()} "api/monkey-control/stop-all-agents", data={"kill_time": time.time()}
) )
if response.ok: if response.ok:
LOGGER.info("Killing all monkeys after the test.") LOGGER.info("Killing all monkeys after the test.")
@ -104,39 +60,24 @@ class MonkeyIslandClient(object):
assert False assert False
@avoid_race_condition @avoid_race_condition
def reset_island(self): def reset_env(self):
self._reset_agent_configuration() if self.requests.get("api", {"action": "reset"}).ok:
self._reset_simulation_data() LOGGER.info("Resetting environment after the test.")
self._reset_credentials()
self._reset_island_mode()
def _reset_agent_configuration(self):
if self.requests.post("api/reset-agent-configuration", data=None).ok:
LOGGER.info("Resetting agent-configuration after the test.")
else: else:
LOGGER.error("Failed to reset agent configuration.") LOGGER.error("Failed to reset the environment.")
assert False assert False
def _reset_simulation_data(self): @avoid_race_condition
if self.requests.post("api/clear-simulation-data", data=None).ok: def set_scenario(self, scenario):
LOGGER.info("Clearing simulation data.") self.requests.post_json("api/island-mode", {"mode": scenario})
else:
LOGGER.error("Failed to clear simulation data")
assert False
def _reset_credentials(self): def find_monkeys_in_db(self, query):
if self.requests.put_json("api/propagation-credentials/configured-credentials", json=[]).ok: if query is None:
LOGGER.info("Resseting configured credentials after the test.") raise TypeError
else: response = self.requests.get(
LOGGER.error("Failed to reset configured credentials") MONKEY_TEST_ENDPOINT, MonkeyIslandClient.form_find_query_for_request(query)
assert False )
return MonkeyIslandClient.get_test_query_results(response)
def _reset_island_mode(self):
if self.requests.put_json("api/island/mode", json="unset").ok:
LOGGER.info("Resetting island mode after the test.")
else:
LOGGER.error("Failed to reset island mode")
assert False
def find_telems_in_db(self, query: dict): def find_telems_in_db(self, query: dict):
if query is None: if query is None:
@ -146,21 +87,17 @@ class MonkeyIslandClient(object):
) )
return MonkeyIslandClient.get_test_query_results(response) return MonkeyIslandClient.get_test_query_results(response)
def get_agents(self) -> Sequence[Agent]: def get_all_monkeys_from_db(self):
response = self.requests.get(GET_AGENTS_ENDPOINT) response = self.requests.get(
MONKEY_TEST_ENDPOINT, MonkeyIslandClient.form_find_query_for_request(None)
)
return MonkeyIslandClient.get_test_query_results(response)
return [Agent(**a) for a in response.json()] def find_log_in_db(self, query):
response = self.requests.get(
def get_machines(self) -> Mapping[MachineID, Machine]: LOG_TEST_ENDPOINT, MonkeyIslandClient.form_find_query_for_request(query)
response = self.requests.get(GET_MACHINES_ENDPOINT) )
machines = (Machine(**m) for m in response.json()) return MonkeyIslandClient.get_test_query_results(response)
return {m.id: m for m in machines}
def get_agent_log(self, agent_id: AgentID) -> str:
response = self.requests.get(f"{GET_LOG_ENDPOINT}/{agent_id}")
return response.json()
@staticmethod @staticmethod
def form_find_query_for_request(query: Union[dict, None]) -> dict: def form_find_query_for_request(query: Union[dict, None]) -> dict:
@ -171,5 +108,15 @@ class MonkeyIslandClient(object):
return json.loads(response.content)["results"] return json.loads(response.content)["results"]
def is_all_monkeys_dead(self): def is_all_monkeys_dead(self):
agents = self.get_agents() query = {"dead": False}
return all((a.stop_time is not None for a in agents)) return len(self.find_monkeys_in_db(query)) == 0
def clear_caches(self):
"""
Tries to clear caches.
:raises: If error (by error code), raises the error
:return: The response
"""
response = self.requests.get("api/test/clear-caches")
response.raise_for_status()
return response

View File

@ -1,9 +1,12 @@
import functools import functools
import logging import logging
from datetime import timedelta
from typing import Dict from typing import Dict
import requests import requests
from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod
ISLAND_USERNAME = "test" ISLAND_USERNAME = "test"
ISLAND_PASSWORD = "test" ISLAND_PASSWORD = "test"
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -22,6 +25,28 @@ class MonkeyIslandRequests(object):
def __init__(self, server_address): def __init__(self, server_address):
self.addr = "https://{IP}/".format(IP=server_address) self.addr = "https://{IP}/".format(IP=server_address)
self.token = self.try_get_jwt_from_server() self.token = self.try_get_jwt_from_server()
self.supported_request_methods = {
SupportedRequestMethod.GET: self.get,
SupportedRequestMethod.POST: self.post,
SupportedRequestMethod.PATCH: self.patch,
SupportedRequestMethod.DELETE: self.delete,
}
def get_request_time(self, url, method: SupportedRequestMethod, data=None):
response = self.send_request_by_method(url, method, data)
if response.ok:
LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120].strip()}")
return response.elapsed
else:
LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}")
# instead of raising for status, mark failed responses as maxtime
return timedelta.max
def send_request_by_method(self, url, method=SupportedRequestMethod.GET, data=None):
if data:
return self.supported_request_methods[method](url, data)
else:
return self.supported_request_methods[method](url)
def try_get_jwt_from_server(self): def try_get_jwt_from_server(self):
try: try:
@ -39,7 +64,7 @@ class MonkeyIslandRequests(object):
def get_jwt_from_server(self): def get_jwt_from_server(self):
resp = requests.post( # noqa: DUO123 resp = requests.post( # noqa: DUO123
self.addr + "api/authenticate", self.addr + "api/auth",
json={"username": ISLAND_USERNAME, "password": ISLAND_PASSWORD}, json={"username": ISLAND_USERNAME, "password": ISLAND_PASSWORD},
verify=False, verify=False,
) )
@ -49,7 +74,7 @@ class MonkeyIslandRequests(object):
def try_set_island_to_credentials(self): def try_set_island_to_credentials(self):
resp = requests.post( # noqa: DUO123 resp = requests.post( # noqa: DUO123
self.addr + "api/register", self.addr + "api/registration",
json={"username": ISLAND_USERNAME, "password": ISLAND_PASSWORD}, json={"username": ISLAND_USERNAME, "password": ISLAND_PASSWORD},
verify=False, verify=False,
) )
@ -83,21 +108,9 @@ class MonkeyIslandRequests(object):
) )
@_Decorators.refresh_jwt_token @_Decorators.refresh_jwt_token
def put(self, url, data): def post_json(self, url, data: Dict):
return requests.put( # noqa: DUO123
self.addr + url, data=data, headers=self.get_jwt_header(), verify=False
)
@_Decorators.refresh_jwt_token
def put_json(self, url, json: Dict):
return requests.put( # noqa: DUO123
self.addr + url, json=json, headers=self.get_jwt_header(), verify=False
)
@_Decorators.refresh_jwt_token
def post_json(self, url, json: Dict):
return requests.post( # noqa: DUO123 return requests.post( # noqa: DUO123
self.addr + url, json=json, headers=self.get_jwt_header(), verify=False self.addr + url, json=data, headers=self.get_jwt_header(), verify=False
) )
@_Decorators.refresh_jwt_token @_Decorators.refresh_jwt_token

View File

@ -0,0 +1,8 @@
from enum import Enum
class SupportedRequestMethod(Enum):
GET = "GET"
POST = "POST"
PATCH = "PATCH"
DELETE = "DELETE"

View File

@ -1,7 +0,0 @@
from typing import Iterable
from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration
def get_target_ips(test_configuration: TestConfiguration) -> Iterable[str]:
return test_configuration.agent_configuration.propagation.network_scan.targets.subnets

View File

@ -0,0 +1,38 @@
import logging
import os
from bson import ObjectId
LOGGER = logging.getLogger(__name__)
class MonkeyLog(object):
def __init__(self, monkey, log_dir_path):
self.monkey = monkey
self.log_dir_path = log_dir_path
def download_log(self, island_client):
log = island_client.find_log_in_db({"monkey_id": ObjectId(self.monkey["id"])})
if not log:
LOGGER.error("Log for monkey {} not found".format(self.monkey["ip_addresses"][0]))
return False
else:
self.write_log_to_file(log)
return True
def write_log_to_file(self, log):
with open(self.get_log_path_for_monkey(self.monkey), "w") as log_file:
log_file.write(MonkeyLog.parse_log(log))
@staticmethod
def parse_log(log):
log = log.strip('"')
log = log.replace("\\n", "\n ")
return log
@staticmethod
def get_filename_for_monkey_log(monkey):
return "{}.txt".format(monkey["ip_addresses"][0])
def get_log_path_for_monkey(self, monkey):
return os.path.join(self.log_dir_path, MonkeyLog.get_filename_for_monkey_log(monkey))

View File

@ -1,65 +1,25 @@
import logging import logging
from pathlib import Path
from threading import Thread
from typing import List, Mapping
from common.types import MachineID from envs.monkey_zoo.blackbox.log_handlers.monkey_log import MonkeyLog
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
from monkey_island.cc.models import Agent, Machine
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
class MonkeyLogsDownloader(object): class MonkeyLogsDownloader(object):
def __init__(self, island_client: MonkeyIslandClient, log_dir_path: str): def __init__(self, island_client, log_dir_path):
self.island_client = island_client self.island_client = island_client
self.log_dir_path = Path(log_dir_path) self.log_dir_path = log_dir_path
self.monkey_log_paths: List[Path] = [] self.monkey_log_paths = []
def download_monkey_logs(self): def download_monkey_logs(self):
try: LOGGER.info("Downloading each monkey log.")
LOGGER.info("Downloading each monkey log.") all_monkeys = self.island_client.get_all_monkeys_from_db()
for monkey in all_monkeys:
downloaded_log_path = self._download_monkey_log(monkey)
if downloaded_log_path:
self.monkey_log_paths.append(downloaded_log_path)
agents = self.island_client.get_agents() def _download_monkey_log(self, monkey):
machines = self.island_client.get_machines() log_handler = MonkeyLog(monkey, self.log_dir_path)
download_successful = log_handler.download_log(self.island_client)
download_threads: List[Thread] = [] return log_handler.get_log_path_for_monkey(monkey) if download_successful else None
# TODO: Does downloading logs concurrently still improve performance after resolving
# https://github.com/guardicore/monkey/issues/2383?
for agent in agents:
t = Thread(target=self._download_log, args=(agent, machines), daemon=True)
t.start()
download_threads.append(t)
for thread in download_threads:
thread.join()
except Exception as err:
LOGGER.exception(err)
def _download_log(self, agent: Agent, machines: Mapping[MachineID, Machine]):
log_file_path = self._get_log_file_path(agent, machines)
log_contents = self.island_client.get_agent_log(agent.id)
MonkeyLogsDownloader._write_log_to_file(log_file_path, log_contents)
self.monkey_log_paths.append(log_file_path)
def _get_log_file_path(self, agent: Agent, machines: Mapping[MachineID, Machine]) -> Path:
try:
machine_ip = machines[agent.machine_id].network_interfaces[0].ip
except IndexError:
LOGGER.error(f"Machine with ID {agent.machine_id} has no network interfaces")
machine_ip = "UNKNOWN"
start_time = agent.start_time.strftime("%Y-%m-%d_%H-%M-%S")
return self.log_dir_path / f"agent_{start_time}_{machine_ip}.log"
@staticmethod
def _write_log_to_file(log_file_path: Path, log_contents: str):
LOGGER.debug(f"Writing {len(log_contents)} bytes to {log_file_path}")
with open(log_file_path, "w") as f:
f.write(log_contents)

View File

@ -10,8 +10,6 @@ LOGGER = logging.getLogger(__name__)
class TestLogsHandler(object): class TestLogsHandler(object):
__test__ = False
def __init__(self, test_name, island_client, log_dir_path): def __init__(self, test_name, island_client, log_dir_path):
self.test_name = test_name self.test_name = test_name
self.island_client = island_client self.island_client = island_client

View File

@ -3,29 +3,31 @@ import os
from time import sleep from time import sleep
import pytest import pytest
from typing_extensions import Type
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer 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.analyzers.zerologon_analyzer import ZerologonAnalyzer
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
from envs.monkey_zoo.blackbox.island_client.test_configuration_parser import get_target_ips from envs.monkey_zoo.blackbox.config_templates.grouped.depth_1_a import Depth1A
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler from envs.monkey_zoo.blackbox.config_templates.grouped.depth_2_a import Depth2A
from envs.monkey_zoo.blackbox.test_configurations import ( from envs.monkey_zoo.blackbox.config_templates.grouped.depth_3_a import Depth3A
credentials_reuse_ssh_key_test_configuration, from envs.monkey_zoo.blackbox.config_templates.single_tests.powershell_credentials_reuse import (
depth_1_a_test_configuration, PowerShellCredentialsReuse,
depth_2_a_test_configuration,
depth_3_a_test_configuration,
depth_4_a_test_configuration,
smb_pth_test_configuration,
wmi_mimikatz_test_configuration,
zerologon_test_configuration,
) )
from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration from envs.monkey_zoo.blackbox.config_templates.single_tests.smb_pth import SmbPth
from envs.monkey_zoo.blackbox.config_templates.single_tests.wmi_mimikatz import WmiMimikatz
from envs.monkey_zoo.blackbox.config_templates.single_tests.zerologon import Zerologon
from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_TEST_MACHINE_LIST
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.log_handlers.test_logs_handler import TestLogsHandler
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import ( from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
initialize_gcp_client, initialize_gcp_client,
start_machines, start_machines,
stop_machines, stop_machines,
) )
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
DEFAULT_TIMEOUT_SECONDS = 2 * 60 + 30 DEFAULT_TIMEOUT_SECONDS = 2 * 60 + 30
MACHINE_BOOTUP_WAIT_SECONDS = 30 MACHINE_BOOTUP_WAIT_SECONDS = 30
@ -35,20 +37,18 @@ LOGGER = logging.getLogger(__name__)
@pytest.fixture(autouse=True, scope="session") @pytest.fixture(autouse=True, scope="session")
def GCPHandler(request, no_gcp, gcp_machines_to_start): def GCPHandler(request, no_gcp):
if not no_gcp: if not no_gcp:
LOGGER.info(f"MACHINES TO START: {gcp_machines_to_start}")
try: try:
initialize_gcp_client() initialize_gcp_client()
start_machines(gcp_machines_to_start) start_machines(GCP_TEST_MACHINE_LIST)
except Exception as e: except Exception as e:
LOGGER.error("GCP Handler failed to initialize: %s." % e) LOGGER.error("GCP Handler failed to initialize: %s." % e)
pytest.exit("Encountered an error while starting GCP machines. Stopping the tests.") pytest.exit("Encountered an error while starting GCP machines. Stopping the tests.")
wait_machine_bootup() wait_machine_bootup()
def fin(): def fin():
stop_machines(gcp_machines_to_start) stop_machines(GCP_TEST_MACHINE_LIST)
request.addfinalizer(fin) request.addfinalizer(fin)
@ -64,7 +64,7 @@ def wait_machine_bootup():
@pytest.fixture(scope="class") @pytest.fixture(scope="class")
def island_client(island): def island_client(island, quick_performance_tests):
client_established = False client_established = False
try: try:
island_client_object = MonkeyIslandClient(island) island_client_object = MonkeyIslandClient(island)
@ -74,6 +74,9 @@ def island_client(island):
finally: finally:
if not client_established: if not client_established:
pytest.exit("BB tests couldn't establish communication to the island.") pytest.exit("BB tests couldn't establish communication to the island.")
if not quick_performance_tests:
island_client_object.reset_env()
island_client_object.set_scenario(IslandModeEnum.ADVANCED.value)
yield island_client_object yield island_client_object
@ -83,13 +86,13 @@ class TestMonkeyBlackbox:
@staticmethod @staticmethod
def run_exploitation_test( def run_exploitation_test(
island_client: MonkeyIslandClient, island_client: MonkeyIslandClient,
test_configuration: TestConfiguration, config_template: Type[ConfigTemplate],
test_name: str, test_name: str,
timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS,
): ):
raw_config = IslandConfigParser.get_raw_config(config_template, island_client)
analyzer = CommunicationAnalyzer( analyzer = CommunicationAnalyzer(
island_client, island_client, IslandConfigParser.get_ips_of_targets(raw_config)
get_target_ips(test_configuration),
) )
log_handler = TestLogsHandler( log_handler = TestLogsHandler(
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path() test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
@ -97,7 +100,7 @@ class TestMonkeyBlackbox:
ExploitationTest( ExploitationTest(
name=test_name, name=test_name,
island_client=island_client, island_client=island_client,
test_configuration=test_configuration, raw_config=raw_config,
analyzers=[analyzer], analyzers=[analyzer],
timeout=timeout_in_seconds, timeout=timeout_in_seconds,
log_handler=log_handler, log_handler=log_handler,
@ -110,23 +113,21 @@ class TestMonkeyBlackbox:
# If test_depth_1_a() is run first, some test will fail because machines are not yet fully # If test_depth_1_a() is run first, some test will fail because machines are not yet fully
# booted. Running test_depth_2_a() first gives slow VMs extra time to boot. # booted. Running test_depth_2_a() first gives slow VMs extra time to boot.
def test_depth_2_a(self, island_client): def test_depth_2_a(self, island_client):
TestMonkeyBlackbox.run_exploitation_test( TestMonkeyBlackbox.run_exploitation_test(island_client, Depth2A, "Depth2A test suite")
island_client, depth_2_a_test_configuration, "Depth2A test suite"
)
def test_depth_1_a(self, island_client): def test_depth_1_a(self, island_client):
TestMonkeyBlackbox.run_exploitation_test( TestMonkeyBlackbox.run_exploitation_test(island_client, Depth1A, "Depth1A test suite")
island_client, depth_1_a_test_configuration, "Depth1A test suite"
)
def test_depth_3_a(self, island_client): def test_depth_3_a(self, island_client):
TestMonkeyBlackbox.run_exploitation_test( TestMonkeyBlackbox.run_exploitation_test(island_client, Depth3A, "Depth3A test suite")
island_client, depth_3_a_test_configuration, "Depth3A test suite"
)
def test_depth_4_a(self, island_client): # Not grouped because can only be ran on windows
@pytest.mark.skip_powershell_reuse
def test_powershell_exploiter_credentials_reuse(self, island_client):
TestMonkeyBlackbox.run_exploitation_test( TestMonkeyBlackbox.run_exploitation_test(
island_client, depth_4_a_test_configuration, "Depth4A test suite" island_client,
PowerShellCredentialsReuse,
"PowerShell_Remoting_exploiter_credentials_reuse",
) )
# Not grouped because it's slow # Not grouped because it's slow
@ -137,10 +138,10 @@ class TestMonkeyBlackbox:
"aad3b435b51404eeaad3b435b51404ee", "aad3b435b51404eeaad3b435b51404ee",
"2864b62ea4496934a5d6e86f50b834a5", "2864b62ea4496934a5d6e86f50b834a5",
] ]
raw_config = IslandConfigParser.get_raw_config(Zerologon, island_client)
zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds) zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds)
communication_analyzer = CommunicationAnalyzer( communication_analyzer = CommunicationAnalyzer(
island_client, island_client, IslandConfigParser.get_ips_of_targets(raw_config)
get_target_ips(zerologon_test_configuration),
) )
log_handler = TestLogsHandler( log_handler = TestLogsHandler(
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path() test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
@ -148,26 +149,19 @@ class TestMonkeyBlackbox:
ExploitationTest( ExploitationTest(
name=test_name, name=test_name,
island_client=island_client, island_client=island_client,
test_configuration=zerologon_test_configuration, raw_config=raw_config,
analyzers=[zero_logon_analyzer, communication_analyzer], analyzers=[zero_logon_analyzer, communication_analyzer],
timeout=DEFAULT_TIMEOUT_SECONDS + 30, timeout=DEFAULT_TIMEOUT_SECONDS + 30,
log_handler=log_handler, log_handler=log_handler,
).run() ).run()
def test_credentials_reuse_ssh_key(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, credentials_reuse_ssh_key_test_configuration, "Credentials_Reuse_SSH_Key"
)
# Not grouped because conflicts with SMB. # Not grouped because conflicts with SMB.
# Consider grouping when more depth 1 exploiters collide with group depth_1_a # Consider grouping when more depth 1 exploiters collide with group depth_1_a
def test_wmi_and_mimikatz_exploiters(self, island_client): def test_wmi_and_mimikatz_exploiters(self, island_client):
TestMonkeyBlackbox.run_exploitation_test( TestMonkeyBlackbox.run_exploitation_test(
island_client, wmi_mimikatz_test_configuration, "WMI_exploiter,_mimikatz" island_client, WmiMimikatz, "WMI_exploiter,_mimikatz"
) )
# Not grouped because it's depth 1 but conflicts with SMB exploiter in group depth_1_a # Not grouped because it's depth 1 but conflicts with SMB exploiter in group depth_1_a
def test_smb_pth(self, island_client): def test_smb_pth(self, island_client):
TestMonkeyBlackbox.run_exploitation_test( TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH")
island_client, smb_pth_test_configuration, "SMB_PTH"
)

View File

@ -0,0 +1,281 @@
import logging
import os
from time import sleep
import pytest
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.config_templates.config_template import ConfigTemplate
from envs.monkey_zoo.blackbox.config_templates.single_tests.hadoop import Hadoop
from envs.monkey_zoo.blackbox.config_templates.single_tests.log4j_logstash import Log4jLogstash
from envs.monkey_zoo.blackbox.config_templates.single_tests.log4j_solr import Log4jSolr
from envs.monkey_zoo.blackbox.config_templates.single_tests.log4j_tomcat import Log4jTomcat
from envs.monkey_zoo.blackbox.config_templates.single_tests.mssql import Mssql
from envs.monkey_zoo.blackbox.config_templates.single_tests.performance import Performance
from envs.monkey_zoo.blackbox.config_templates.single_tests.powershell import PowerShell
from envs.monkey_zoo.blackbox.config_templates.single_tests.powershell_credentials_reuse import (
PowerShellCredentialsReuse,
)
from envs.monkey_zoo.blackbox.config_templates.single_tests.smb_mimikatz import SmbMimikatz
from envs.monkey_zoo.blackbox.config_templates.single_tests.smb_pth import SmbPth
from envs.monkey_zoo.blackbox.config_templates.single_tests.ssh import Ssh
from envs.monkey_zoo.blackbox.config_templates.single_tests.tunneling import Tunneling
from envs.monkey_zoo.blackbox.config_templates.single_tests.wmi_mimikatz import WmiMimikatz
from envs.monkey_zoo.blackbox.config_templates.single_tests.wmi_pth import WmiPth
from envs.monkey_zoo.blackbox.config_templates.single_tests.zerologon import Zerologon
from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_TEST_MACHINE_LIST
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.log_handlers.test_logs_handler import TestLogsHandler
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
from envs.monkey_zoo.blackbox.tests.performance.map_generation import MapGenerationTest
from envs.monkey_zoo.blackbox.tests.performance.map_generation_from_telemetries import (
MapGenerationFromTelemetryTest,
)
from envs.monkey_zoo.blackbox.tests.performance.report_generation import ReportGenerationTest
from envs.monkey_zoo.blackbox.tests.performance.report_generation_from_telemetries import (
ReportGenerationFromTelemetryTest,
)
from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test import (
TelemetryPerformanceTest,
)
from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
initialize_gcp_client,
start_machines,
stop_machines,
)
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
DEFAULT_TIMEOUT_SECONDS = 2 * 60
MACHINE_BOOTUP_WAIT_SECONDS = 30
LOG_DIR_PATH = "./logs"
logging.basicConfig(level=logging.INFO)
LOGGER = logging.getLogger(__name__)
@pytest.fixture(autouse=True, scope="session")
def GCPHandler(request, no_gcp):
if not no_gcp:
try:
initialize_gcp_client()
start_machines(GCP_TEST_MACHINE_LIST)
except Exception as e:
LOGGER.error("GCP Handler failed to initialize: %s." % e)
pytest.exit("Encountered an error while starting GCP machines. Stopping the tests.")
wait_machine_bootup()
def fin():
stop_machines(GCP_TEST_MACHINE_LIST)
request.addfinalizer(fin)
@pytest.fixture(autouse=True, scope="session")
def delete_logs():
LOGGER.info("Deleting monkey logs before new tests.")
TestLogsHandler.delete_log_folder_contents(TestMonkeyBlackbox.get_log_dir_path())
def wait_machine_bootup():
sleep(MACHINE_BOOTUP_WAIT_SECONDS)
@pytest.fixture(scope="class")
def island_client(island, quick_performance_tests):
client_established = False
try:
island_client_object = MonkeyIslandClient(island)
client_established = island_client_object.get_api_status()
except Exception:
logging.exception("Got an exception while trying to establish connection to the Island.")
finally:
if not client_established:
pytest.exit("BB tests couldn't establish communication to the island.")
if not quick_performance_tests:
island_client_object.reset_env()
island_client_object.set_scenario(IslandModeEnum.ADVANCED.value)
yield island_client_object
@pytest.mark.usefixtures("island_client")
# noinspection PyUnresolvedReferences
class TestMonkeyBlackbox:
@staticmethod
def run_exploitation_test(
island_client: MonkeyIslandClient,
config_template: Type[ConfigTemplate],
test_name: str,
timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS,
):
raw_config = IslandConfigParser.get_raw_config(config_template, island_client)
analyzer = CommunicationAnalyzer(
island_client, IslandConfigParser.get_ips_of_targets(raw_config)
)
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=timeout_in_seconds,
log_handler=log_handler,
).run()
@staticmethod
def run_performance_test(
performance_test_class,
island_client,
config_template,
timeout_in_seconds,
break_on_timeout=False,
):
raw_config = IslandConfigParser.get_raw_config(config_template, island_client)
log_handler = TestLogsHandler(
performance_test_class.TEST_NAME, island_client, TestMonkeyBlackbox.get_log_dir_path()
)
analyzers = [
CommunicationAnalyzer(island_client, IslandConfigParser.get_ips_of_targets(raw_config))
]
performance_test_class(
island_client=island_client,
raw_config=raw_config,
analyzers=analyzers,
timeout=timeout_in_seconds,
log_handler=log_handler,
break_on_timeout=break_on_timeout,
).run()
@staticmethod
def get_log_dir_path():
return os.path.abspath(LOG_DIR_PATH)
def test_ssh_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, Ssh, "SSH_exploiter_and_keys")
def test_hadoop_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, Hadoop, "Hadoop_exploiter", 6 * 60)
def test_mssql_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, Mssql, "MSSQL_exploiter")
def test_powershell_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, PowerShell, "PowerShell_Remoting_exploiter"
)
@pytest.mark.skip_powershell_reuse
def test_powershell_exploiter_credentials_reuse(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client,
PowerShellCredentialsReuse,
"PowerShell_Remoting_exploiter_credentials_reuse",
)
def test_smb_and_mimikatz_exploiters(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, SmbMimikatz, "SMB_exploiter_mimikatz"
)
def test_smb_pth(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH")
def test_log4j_solr_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, Log4jSolr, "Log4Shell_Solr_exploiter"
)
def test_log4j_tomcat_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, Log4jTomcat, "Log4Shell_tomcat_exploiter"
)
def test_log4j_logstash_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, Log4jLogstash, "Log4Shell_logstash_exploiter"
)
def test_tunneling(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, Tunneling, "Tunneling_exploiter", 3 * 60
)
def test_wmi_and_mimikatz_exploiters(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
island_client, WmiMimikatz, "WMI_exploiter,_mimikatz"
)
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)
zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds)
communication_analyzer = CommunicationAnalyzer(
island_client, IslandConfigParser.get_ips_of_targets(raw_config)
)
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=[zero_logon_analyzer, communication_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):
"""
This test includes the SSH + Hadoop + MSSQL machines all in one test
for a total of 8 machines including the Monkey Island.
Is has 2 analyzers - the regular one which checks all the Monkeys
and the Timing one which checks how long the report took to execute
"""
if not quick_performance_tests:
TestMonkeyBlackbox.run_performance_test(
ReportGenerationTest, island_client, Performance, timeout_in_seconds=10 * 60
)
else:
LOGGER.error("This test doesn't support 'quick_performance_tests' option.")
assert False
@pytest.mark.skip(
reason="Perfomance test that creates env from fake telemetries is faster, use that instead."
)
def test_map_generation_performance(self, island_client, quick_performance_tests):
if not quick_performance_tests:
TestMonkeyBlackbox.run_performance_test(
MapGenerationTest, island_client, "PERFORMANCE.conf", timeout_in_seconds=10 * 60
)
else:
LOGGER.error("This test doesn't support 'quick_performance_tests' option.")
assert False
@pytest.mark.run_performance_tests
def test_report_generation_from_fake_telemetries(self, island_client, quick_performance_tests):
ReportGenerationFromTelemetryTest(island_client, quick_performance_tests).run()
@pytest.mark.run_performance_tests
def test_map_generation_from_fake_telemetries(self, island_client, quick_performance_tests):
MapGenerationFromTelemetryTest(island_client, quick_performance_tests).run()
@pytest.mark.run_performance_tests
def test_telem_performance(self, island_client, quick_performance_tests):
TelemetryPerformanceTest(
island_client, quick_performance_tests
).test_telemetry_performance()

View File

@ -1,9 +0,0 @@
from .test_configuration import TestConfiguration
from .depth_1_a import depth_1_a_test_configuration
from .depth_2_a import depth_2_a_test_configuration
from .depth_3_a import depth_3_a_test_configuration
from .depth_4_a import depth_4_a_test_configuration
from .smb_pth import smb_pth_test_configuration
from .wmi_mimikatz import wmi_mimikatz_test_configuration
from .zerologon import zerologon_test_configuration
from .credentials_reuse_ssh_key import credentials_reuse_ssh_key_test_configuration

View File

@ -1,71 +0,0 @@
import dataclasses
from common.agent_configuration import AgentConfiguration, PluginConfiguration
from common.credentials import Credentials, Password, Username
from .noop import noop_test_configuration
from .utils import (
add_credential_collectors,
add_exploiters,
add_subnets,
add_tcp_ports,
replace_agent_configuration,
replace_propagation_credentials,
set_keep_tunnel_open_time,
set_maximum_depth,
)
# Tests:
# SSHCollector steals key from machine A(10.2.3.14),
# then B(10.2.4.15) exploits C(10.2.5.16) with that key
def _add_exploiters(agent_configuration: AgentConfiguration) -> AgentConfiguration:
brute_force = [
PluginConfiguration(name="SSHExploiter", options={}),
]
return add_exploiters(agent_configuration, brute_force=brute_force, vulnerability=[])
def _add_subnets(agent_configuration: AgentConfiguration) -> AgentConfiguration:
subnets = ["10.2.3.14", "10.2.4.15", "10.2.5.16"]
return add_subnets(agent_configuration, subnets)
def _add_credential_collectors(agent_configuration: AgentConfiguration) -> AgentConfiguration:
credential_collectors = [
PluginConfiguration(name="SSHCollector", options={}),
]
return add_credential_collectors(
agent_configuration, credential_collectors=credential_collectors
)
def _add_tcp_ports(agent_configuration: AgentConfiguration) -> AgentConfiguration:
ports = [22]
return add_tcp_ports(agent_configuration, ports)
test_agent_configuration = set_maximum_depth(noop_test_configuration.agent_configuration, 3)
test_agent_configuration = set_keep_tunnel_open_time(test_agent_configuration, 20)
test_agent_configuration = _add_exploiters(test_agent_configuration)
test_agent_configuration = _add_subnets(test_agent_configuration)
test_agent_configuration = _add_credential_collectors(test_agent_configuration)
test_agent_configuration = _add_tcp_ports(test_agent_configuration)
CREDENTIALS = (
Credentials(identity=Username(username="m0nk3y"), secret=None),
Credentials(identity=None, secret=Password(password="u26gbVQe")),
Credentials(identity=None, secret=Password(password="5BuYHeVl")),
)
credentials_reuse_ssh_key_test_configuration = dataclasses.replace(noop_test_configuration)
replace_agent_configuration(
test_configuration=credentials_reuse_ssh_key_test_configuration,
agent_configuration=test_agent_configuration,
)
replace_propagation_credentials(
test_configuration=credentials_reuse_ssh_key_test_configuration,
propagation_credentials=CREDENTIALS,
)

View File

@ -1,101 +0,0 @@
import dataclasses
from common.agent_configuration import AgentConfiguration, PluginConfiguration
from common.credentials import Credentials, Password, Username
from .noop import noop_test_configuration
from .utils import (
add_credential_collectors,
add_exploiters,
add_fingerprinters,
add_http_ports,
add_subnets,
add_tcp_ports,
replace_agent_configuration,
replace_propagation_credentials,
set_maximum_depth,
)
# Tests:
# Hadoop (10.2.2.2, 10.2.2.3)
# Log4shell (10.2.3.55, 10.2.3.56, 10.2.3.49, 10.2.3.50, 10.2.3.51, 10.2.3.52)
# MSSQL (10.2.2.16)
# SMB mimikatz password stealing and brute force (10.2.2.14 and 10.2.2.15)
def _add_exploiters(agent_configuration: AgentConfiguration) -> AgentConfiguration:
brute_force = [
PluginConfiguration(name="MSSQLExploiter", options={}),
PluginConfiguration(name="SmbExploiter", options={"smb_download_timeout": 30}),
PluginConfiguration(name="SSHExploiter", options={}),
]
vulnerability = [
PluginConfiguration(name="HadoopExploiter", options={}),
PluginConfiguration(name="Log4ShellExploiter", options={}),
]
return add_exploiters(agent_configuration, brute_force=brute_force, vulnerability=vulnerability)
def _add_fingerprinters(agent_configuration: AgentConfiguration) -> AgentConfiguration:
fingerprinters = [PluginConfiguration(name="http", options={})]
return add_fingerprinters(agent_configuration, fingerprinters)
def _add_subnets(agent_configuration: AgentConfiguration) -> AgentConfiguration:
subnets = [
"10.2.2.2",
"10.2.2.3",
"10.2.3.55",
"10.2.3.56",
"10.2.3.49",
"10.2.3.50",
"10.2.3.51",
"10.2.3.52",
"10.2.2.16",
"10.2.2.14",
"10.2.2.15",
]
return add_subnets(agent_configuration, subnets)
def _add_credential_collectors(agent_configuration: AgentConfiguration) -> AgentConfiguration:
return add_credential_collectors(
agent_configuration, [PluginConfiguration(name="MimikatzCollector", options={})]
)
HTTP_PORTS = [8080, 8983, 9600]
def _add_tcp_ports(agent_configuration: AgentConfiguration) -> AgentConfiguration:
ports = [22, 445] + HTTP_PORTS
return add_tcp_ports(agent_configuration, ports)
def _add_http_ports(agent_configuration: AgentConfiguration) -> AgentConfiguration:
return add_http_ports(agent_configuration, HTTP_PORTS)
test_agent_configuration = set_maximum_depth(noop_test_configuration.agent_configuration, 1)
test_agent_configuration = _add_exploiters(test_agent_configuration)
test_agent_configuration = _add_fingerprinters(test_agent_configuration)
test_agent_configuration = _add_subnets(test_agent_configuration)
test_agent_configuration = _add_tcp_ports(test_agent_configuration)
test_agent_configuration = _add_credential_collectors(test_agent_configuration)
test_agent_configuration = _add_http_ports(test_agent_configuration)
CREDENTIALS = (
Credentials(identity=Username(username="m0nk3y"), secret=None),
Credentials(identity=None, secret=Password(password="Ivrrw5zEzs")),
Credentials(identity=None, secret=Password(password="Xk8VDTsC")),
)
depth_1_a_test_configuration = dataclasses.replace(noop_test_configuration)
replace_agent_configuration(
test_configuration=depth_1_a_test_configuration, agent_configuration=test_agent_configuration
)
replace_propagation_credentials(
test_configuration=depth_1_a_test_configuration, propagation_credentials=CREDENTIALS
)

View File

@ -1,77 +0,0 @@
import dataclasses
from common.agent_configuration import AgentConfiguration, PluginConfiguration
from common.credentials import Credentials, Password, Username
from .noop import noop_test_configuration
from .utils import (
add_exploiters,
add_fingerprinters,
add_http_ports,
add_subnets,
add_tcp_ports,
replace_agent_configuration,
replace_propagation_credentials,
set_maximum_depth,
)
# Tests:
# SSH password and key brute-force, key stealing (10.2.2.11, 10.2.2.12)
# Powershell credential reuse (logging in without credentials
# to an identical user on another machine)(10.2.3.44, 10.2.3.46)
def _add_exploiters(agent_configuration: AgentConfiguration) -> AgentConfiguration:
brute_force = [
PluginConfiguration(name="SSHExploiter", options={}),
PluginConfiguration(name="PowerShellExploiter", options={}),
]
vulnerability = [
PluginConfiguration(name="Log4ShellExploiter", options={}),
]
return add_exploiters(agent_configuration, brute_force=brute_force, vulnerability=vulnerability)
def _add_subnets(agent_configuration: AgentConfiguration) -> AgentConfiguration:
subnets = [
"10.2.2.11",
"10.2.2.12",
"10.2.3.44",
"10.2.3.46",
]
return add_subnets(agent_configuration, subnets)
def _add_fingerprinters(agent_configuration: AgentConfiguration) -> AgentConfiguration:
fingerprinters = [PluginConfiguration(name="http", options={})]
return add_fingerprinters(agent_configuration, fingerprinters)
def _add_tcp_ports(agent_configuration: AgentConfiguration) -> AgentConfiguration:
ports = [22, 5985, 5986, 8080]
return add_tcp_ports(agent_configuration, ports)
def _add_http_ports(agent_configuration: AgentConfiguration) -> AgentConfiguration:
return add_http_ports(agent_configuration, [8080])
test_agent_configuration = set_maximum_depth(noop_test_configuration.agent_configuration, 2)
test_agent_configuration = _add_exploiters(test_agent_configuration)
test_agent_configuration = _add_subnets(test_agent_configuration)
test_agent_configuration = _add_fingerprinters(test_agent_configuration)
test_agent_configuration = _add_tcp_ports(test_agent_configuration)
test_agent_configuration = _add_http_ports(test_agent_configuration)
CREDENTIALS = (
Credentials(identity=Username(username="m0nk3y"), secret=None),
Credentials(identity=None, secret=Password(password="^NgDvY59~8")),
)
depth_2_a_test_configuration = dataclasses.replace(noop_test_configuration)
replace_agent_configuration(
test_configuration=depth_2_a_test_configuration, agent_configuration=test_agent_configuration
)
replace_propagation_credentials(
test_configuration=depth_2_a_test_configuration, propagation_credentials=CREDENTIALS
)

View File

@ -1,74 +0,0 @@
import dataclasses
from common.agent_configuration import AgentConfiguration, PluginConfiguration
from common.credentials import Credentials, NTHash, Password, Username
from .noop import noop_test_configuration
from .utils import (
add_exploiters,
add_subnets,
add_tcp_ports,
replace_agent_configuration,
replace_propagation_credentials,
set_keep_tunnel_open_time,
set_maximum_depth,
)
# Tests:
# Powershell (10.2.3.45, 10.2.3.46, 10.2.3.47, 10.2.3.48)
# Tunneling through grandparent agent (SSH brute force) (10.2.2.9, 10.2.1.10, 10.2.0.11)
# WMI pass the hash (10.2.2.15)
def _add_exploiters(agent_configuration: AgentConfiguration) -> AgentConfiguration:
brute_force = [
PluginConfiguration(name="PowerShellExploiter", options={}),
PluginConfiguration(name="SSHExploiter", options={}),
PluginConfiguration(name="WmiExploiter", options={"smb_download_timeout": 30}),
]
return add_exploiters(agent_configuration, brute_force=brute_force, vulnerability=[])
def _add_subnets(agent_configuration: AgentConfiguration) -> AgentConfiguration:
subnets = [
"10.2.2.9",
"10.2.3.45",
"10.2.3.47",
"10.2.3.48",
"10.2.1.10",
"10.2.0.11",
"10.2.2.15",
]
return add_subnets(agent_configuration, subnets)
def _add_tcp_ports(agent_configuration: AgentConfiguration) -> AgentConfiguration:
ports = [22, 135, 5985, 5986]
return add_tcp_ports(agent_configuration, ports)
test_agent_configuration = set_maximum_depth(noop_test_configuration.agent_configuration, 3)
test_agent_configuration = set_keep_tunnel_open_time(test_agent_configuration, 20)
test_agent_configuration = _add_exploiters(test_agent_configuration)
test_agent_configuration = _add_subnets(test_agent_configuration)
test_agent_configuration = _add_tcp_ports(test_agent_configuration)
CREDENTIALS = (
Credentials(identity=Username(username="m0nk3y"), secret=None),
Credentials(identity=Username(username="m0nk3y-user"), secret=None),
Credentials(identity=None, secret=Password(password="Passw0rd!")),
Credentials(identity=None, secret=Password(password="3Q=(Ge(+&w]*")),
Credentials(identity=None, secret=Password(password="`))jU7L(w}")),
Credentials(identity=None, secret=NTHash(nt_hash="d0f0132b308a0c4e5d1029cc06f48692")),
Credentials(identity=None, secret=NTHash(nt_hash="5da0889ea2081aa79f6852294cba4a5e")),
Credentials(identity=None, secret=NTHash(nt_hash="50c9987a6bf1ac59398df9f911122c9b")),
)
depth_3_a_test_configuration = dataclasses.replace(noop_test_configuration)
replace_agent_configuration(
test_configuration=depth_3_a_test_configuration, agent_configuration=test_agent_configuration
)
replace_propagation_credentials(
test_configuration=depth_3_a_test_configuration, propagation_credentials=CREDENTIALS
)

Some files were not shown because too many files have changed in this diff Show More