diff --git a/.github/attack-report.png b/.github/attack-report.png new file mode 100644 index 000000000..5ba45e9fb Binary files /dev/null and b/.github/attack-report.png differ diff --git a/.github/map-full.png b/.github/map-full.png index faa1b7832..92902a221 100644 Binary files a/.github/map-full.png and b/.github/map-full.png differ diff --git a/.github/security-report.png b/.github/security-report.png new file mode 100644 index 000000000..54a30b13f Binary files /dev/null and b/.github/security-report.png differ diff --git a/.github/zero-trust-report.png b/.github/zero-trust-report.png new file mode 100644 index 000000000..18a0ff703 Binary files /dev/null and b/.github/zero-trust-report.png differ diff --git a/.gitmodules b/.gitmodules index 92a84cb37..63b69ebab 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ + [submodule "monkey/monkey_island/cc/services/attack/attack_data"] path = monkey/monkey_island/cc/services/attack/attack_data - url = https://github.com/mitre/cti + url = https://github.com/guardicore/cti diff --git a/README.md b/README.md index 83fd878ad..bf9768459 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,6 @@ Welcome to the Infection Monkey! The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized Monkey Island server. - - - - The Infection Monkey is comprised of two parts: * **Monkey** - A tool which infects other machines and propagates to them. @@ -24,6 +20,20 @@ The Infection Monkey is comprised of two parts: To read more about the Monkey, visit [infectionmonkey.com](https://infectionmonkey.com). +## Screenshots + +### Map + + +### Security report + + +### Zero trust report + + +### ATT&CK report + + ## Main Features The Infection Monkey uses the following techniques and exploits to propagate to other machines. @@ -40,6 +50,8 @@ The Infection Monkey uses the following techniques and exploits to propagate to * Conficker * SambaCry * Elastic Search (CVE-2015-1427) + * Weblogic server + * and more ## Setup Check out the [Setup](https://github.com/guardicore/monkey/wiki/setup) page in the Wiki or a quick getting [started guide](https://www.guardicore.com/infectionmonkey/wt/). diff --git a/docker/Dockerfile b/docker/Dockerfile index 2d0d0b55b..aec69fd32 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL MAINTAINER="theonlydoo " -ARG RELEASE=1.6 +ARG RELEASE=1.8.0 ARG DEBIAN_FRONTEND=noninteractive EXPOSE 5000 diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index 82954b99b..1305e3946 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -129,7 +129,7 @@ class VSFTPDExploiter(HostExploiter): change_permission = str.encode(str(change_permission) + '\n') LOG.info("change_permission command is %s", change_permission) backdoor_socket.send(change_permission) - T1222Telem(ScanStatus.USED, change_permission, self.host).send() + T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send() # Run monkey on the machine parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1) @@ -143,7 +143,7 @@ class VSFTPDExploiter(HostExploiter): if backdoor_socket.send(run_monkey): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, run_monkey) - self.add_executed_cmd(run_monkey) + self.add_executed_cmd(run_monkey.decode()) return True else: return False diff --git a/monkey/infection_monkey/requirements.txt b/monkey/infection_monkey/requirements.txt index 7dd61cd19..858e5652b 100644 --- a/monkey/infection_monkey/requirements.txt +++ b/monkey/infection_monkey/requirements.txt @@ -9,7 +9,11 @@ git+https://github.com/guardicore/pyinstaller ecdsa netifaces ipaddress -wmi +# Locking WMI since version 1.5 introduced breaking change on Linux agent compilation. +# See breaking change here: https://github.com/tjguk/wmi/commit/dcf8e3eca79bb8c0101ffb83e25c066b0ba9e16d +# Causes pip to error with: +# Could not find a version that satisfies the requirement pywin32 (from wmi->-r /src/infection_monkey/requirements.txt (line 12)) (from versions: none) +wmi==1.4.9 pywin32 ; sys_platform == 'win32' pymssql<3.0 pyftpdlib diff --git a/monkey/infection_monkey/telemetry/base_telem.py b/monkey/infection_monkey/telemetry/base_telem.py index 2d59b9cbf..7617ab4e3 100644 --- a/monkey/infection_monkey/telemetry/base_telem.py +++ b/monkey/infection_monkey/telemetry/base_telem.py @@ -17,12 +17,16 @@ class BaseTelem(object, metaclass=abc.ABCMeta): def __init__(self): pass - def send(self): + def send(self, log_data=True): """ Sends telemetry to island """ data = self.get_data() - logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, json.dumps(data))) + if log_data: + data_to_log = json.dumps(data) + else: + data_to_log = 'redacted' + logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, data_to_log)) ControlClient.send_telemetry(self.telem_category, data) @property diff --git a/monkey/infection_monkey/telemetry/system_info_telem.py b/monkey/infection_monkey/telemetry/system_info_telem.py index a4b1c0bd0..69ee7beda 100644 --- a/monkey/infection_monkey/telemetry/system_info_telem.py +++ b/monkey/infection_monkey/telemetry/system_info_telem.py @@ -17,3 +17,6 @@ class SystemInfoTelem(BaseTelem): def get_data(self): return self.system_info + + def send(self, log_data=False): + super(SystemInfoTelem, self).send(log_data) diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 09f079c19..50e3bdd7c 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -31,7 +31,7 @@ from monkey_island.cc.bootloader_server import BootloaderHttpServer from monkey_island.cc.setup import setup -def main(should_setup_only): +def main(should_setup_only=False): logger.info("Starting bootloader server") mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url()) bootloader_server_thread = Thread(target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True) diff --git a/monkey/monkey_island/cc/services/attack/attack_data b/monkey/monkey_island/cc/services/attack/attack_data index c139e37bd..fb8942b1a 160000 --- a/monkey/monkey_island/cc/services/attack/attack_data +++ b/monkey/monkey_island/cc/services/attack/attack_data @@ -1 +1 @@ -Subproject commit c139e37bdc51acbc7d0488a5be48553caffdbbd7 +Subproject commit fb8942b1a10f4e734ed75542f2ccae7cbd72c46d diff --git a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py index ad4419be5..6390c600b 100644 --- a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py +++ b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py @@ -1,6 +1,6 @@ from typing import List, Dict -from stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern, v20 +from stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern class MitreApiInterface: @@ -32,14 +32,14 @@ class MitreApiInterface: return all_techniques @staticmethod - def get_stix2_external_reference_id(stix2_data: v20._DomainObject) -> str: + def get_stix2_external_reference_id(stix2_data) -> str: for reference in stix2_data['external_references']: if reference['source_name'] == "mitre-attack" and 'external_id' in reference: return reference['external_id'] return '' @staticmethod - def get_stix2_external_reference_url(stix2_data: v20._DomainObject) -> str: + def get_stix2_external_reference_url(stix2_data) -> str: for reference in stix2_data['external_references']: if 'url' in reference: return reference['url'] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 80dbe7518..bd4e07c24 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -5,7 +5,7 @@ from monkey_island.cc.database import mongo from common.utils.attack_utils import ScanStatus from monkey_island.cc.services.attack.attack_config import AttackConfig from common.utils.code_utils import abstractstatic -from cc.models.attack.attack_mitigations import AttackMitigations +from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/deb-package/DEBIAN_MONGO/control b/monkey/monkey_island/deb-package/DEBIAN_MONGO/control index a47371005..a7bc2373e 100644 --- a/monkey/monkey_island/deb-package/DEBIAN_MONGO/control +++ b/monkey/monkey_island/deb-package/DEBIAN_MONGO/control @@ -5,4 +5,4 @@ Homepage: https://www.infectionmonkey.com Priority: optional Version: 1.0 Description: Guardicore Infection Monkey Island installation package -Depends: openssl, python3-pip, python3-dev +Depends: openssl, python3.7-dev, python3.7-venv, python3-venv, build-essential diff --git a/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst b/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst index f79a71913..f12b31b73 100644 --- a/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst +++ b/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst @@ -1,20 +1,42 @@ #!/bin/bash +# See the "Depends" field of the control file for what packages this scripts depends on. +# Here are the explanations for the current deps: +# Dependency - Why is it required +## openssl - Server certificate generation +## python3.7-dev - Server runtime +## python3.7-venv - For creating virtual env to install all the server pip deps (don't want to pollute system python) +## python3-venv - python3.7-venv doesn't work without it since you need ensure-pip +## build-essential - for compiling python dependencies that don't come in a pre-compiled wheel, like `netifaces` + +echo "Installing Monkey Island (Infection Monkey server)..." + MONKEY_FOLDER=/var/monkey INSTALLATION_FOLDER=/var/monkey/monkey_island/installation PYTHON_FOLDER=/var/monkey/monkey_island/bin/python +PYTHON_VERSION=python3.7 # Prepare python virtualenv -pip3 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER -python3 -m virtualenv -p python3 ${PYTHON_FOLDER} +# This is using the apt package `python3.7-venv` which is listed in the `control` file as a dependency. +# See https://packages.debian.org/stable/python/python3.7-venv +echo "Using $(command -v $PYTHON_VERSION) as the base for virtualenv creation" +$PYTHON_VERSION -m venv ${PYTHON_FOLDER} +# shellcheck disable=SC1090 +source ${PYTHON_FOLDER}/bin/activate -# install pip requirements -${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER +echo "Installing Python dependencies using $(command -v python) and $(command -v pip)..." +# First, make sure that pip is updated +python -m pip install --upgrade pip +# Then install the dependecies from the pre-downloaded whl and tar.gz file +python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER + +deactivate # remove installation folder and unnecessary files rm -rf ${INSTALLATION_FOLDER} rm -f ${MONKEY_FOLDER}/monkey_island/requirements.txt +echo "Installing mongodb..." ${MONKEY_FOLDER}/monkey_island/install_mongo.sh ${MONKEY_FOLDER}/monkey_island/bin/mongodb if [ -d "/etc/systemd/network" ]; then @@ -25,11 +47,17 @@ if [ -d "/etc/systemd/network" ]; then systemctl enable monkey-island fi -${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/ +echo "Creating server certificate..." +${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/cc +echo "Starting services..." service monkey-island start service monkey-mongo start -echo Monkey Island installation ended +echo "" +echo "Monkey Island installation ended." +echo "The server should be accessible soon via https://:5000/" +echo "To check the Island's status, run 'sudo service monkey-island status'" +echo "" -exit 0 \ No newline at end of file +exit 0 diff --git a/monkey/monkey_island/linux/create_certificate.sh b/monkey/monkey_island/linux/create_certificate.sh index 7e306a822..985f607bc 100644 --- a/monkey/monkey_island/linux/create_certificate.sh +++ b/monkey/monkey_island/linux/create_certificate.sh @@ -2,8 +2,29 @@ server_root=${1:-"./cc"} +echo "Creating server cetificate. Server root: $server_root" +# We override the RANDFILE determined by default openssl.cnf, if it doesn't exist. +# This is a known issue with the current version of openssl on Ubuntu 18.04 - once they release +# a new version, we can delete this command. See +# https://github.com/openssl/openssl/commit/0f58220973a02248ca5c69db59e615378467b9c8#diff-8ce6aaad88b10ed2b3b4592fd5c8e03a +# for more details. +DEFAULT_RND_FILE_PATH=~/.rnd +CREATED_RND_FILE=false +if [ ! -f /tmp/foo.txt ]; then # If the file already exists, assume that the contents are fine, and don't change them. + echo "Creating rand seed file in $DEFAULT_RND_FILE_PATH" + dd bs=1024 count=2 "$DEFAULT_RND_FILE_PATH" + chmod 666 "$DEFAULT_RND_FILE_PATH" + CREATED_RND_FILE=true +fi +echo "Generating key in $server_root/server.key..." openssl genrsa -out "$server_root"/server.key 2048 +echo "Generating csr in $server_root/server.csr..." openssl req -new -key "$server_root"/server.key -out "$server_root"/server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com" -openssl x509 -req -days 366 -in "$server_root"/server.csr -signkey "$server_root"/server.key -out $server_root/server.crt +echo "Generating certificate in $server_root/server.crt..." +openssl x509 -req -days 366 -in "$server_root"/server.csr -signkey "$server_root"/server.key -out "$server_root"/server.crt +# Shove some new random data into the file to override the original seed we put in. +if [ "$CREATED_RND_FILE" = true ] ; then + dd bs=1024 count=2 "$DEFAULT_RND_FILE_PATH" +fi diff --git a/monkey/monkey_island/monkey_island.spec b/monkey/monkey_island/monkey_island.spec index ef6d9c2d3..59f95e34f 100644 --- a/monkey/monkey_island/monkey_island.spec +++ b/monkey/monkey_island/monkey_island.spec @@ -1,7 +1,7 @@ # -*- mode: python -*- import os import platform - +import sys __author__ = 'itay.mizeretz' @@ -9,15 +9,20 @@ block_cipher = None def main(): + # These data files and folders will be included in the bundle. + # The format of the tuples is (src, dest_dir). See https://pythonhosted.org/PyInstaller/spec-files.html#adding-data-files + added_datas = [ + ("../common/BUILD", "/common"), + ("../monkey_island/cc/services/attack/attack_data", "/monkey_island/cc/services/attack/attack_data") + ] + a = Analysis(['cc/main.py'], pathex=['..'], hiddenimports=get_hidden_imports(), - hookspath=None, + hookspath=[os.path.join(".", "pyinstaller_hooks")], runtime_hooks=None, binaries=None, - datas=[ - ("../common/BUILD", "/common") - ], + datas=added_datas, excludes=None, win_no_prefer_redirects=None, win_private_assemblies=None, @@ -36,8 +41,7 @@ def main(): name=get_monkey_filename(), debug=False, strip=get_exe_strip(), - upx=True, - upx_exclude=['vcruntime140.dll'], + upx=False, console=True, icon=get_exe_icon()) @@ -74,7 +78,7 @@ def get_linux_only_binaries(): def get_hidden_imports(): - return ['_cffi_backend', 'queue'] if is_windows() else ['_cffi_backend'] + return ['_cffi_backend', 'queue', 'pkg_resources.py2_warn'] if is_windows() else ['_cffi_backend'] def get_msvcr(): diff --git a/monkey/monkey_island/pyinstaller_hooks/__init__.py b/monkey/monkey_island/pyinstaller_hooks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py new file mode 100644 index 000000000..22a9c9774 --- /dev/null +++ b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py @@ -0,0 +1,7 @@ +# Workaround for packaging Monkey Island using PyInstaller. See https://github.com/oasis-open/cti-python-stix2/issues/218 + +import os +from PyInstaller.utils.hooks import get_module_file_attribute + +stix2_dir = os.path.dirname(get_module_file_attribute('stix2')) +datas = [(stix2_dir, 'stix2')] diff --git a/monkey/monkey_island/requirements.txt b/monkey/monkey_island/requirements.txt index cad53d1c8..b5baed7f4 100644 --- a/monkey/monkey_island/requirements.txt +++ b/monkey/monkey_island/requirements.txt @@ -1,5 +1,4 @@ pytest -bson python-dateutil tornado werkzeug