forked from p34709852/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
|
# exploiters config
|
||||||
###########################
|
###########################
|
||||||
|
|
||||||
skip_exploit_if_file_exist = True
|
skip_exploit_if_file_exist = False
|
||||||
|
|
||||||
ms08_067_exploit_attempts = 5
|
ms08_067_exploit_attempts = 5
|
||||||
ms08_067_remote_user_add = "Monkey_IUSER_SUPPORT"
|
ms08_067_remote_user_add = "Monkey_IUSER_SUPPORT"
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import requests
|
|
||||||
import platform
|
import platform
|
||||||
import monkeyfs
|
|
||||||
from network.info import local_ips, check_internet_access
|
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
from config import WormConfiguration, GUID
|
|
||||||
from transport.tcp import TcpProxy
|
import requests
|
||||||
from transport.http import HTTPConnectProxy
|
|
||||||
|
import monkeyfs
|
||||||
import tunnel
|
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'
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
@ -60,7 +62,7 @@ class ControlClient(object):
|
||||||
timeout=20)
|
timeout=20)
|
||||||
break
|
break
|
||||||
|
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
WormConfiguration.current_server = ""
|
WormConfiguration.current_server = ""
|
||||||
LOG.warn("Error connecting to control server %s: %s", server, exc)
|
LOG.warn("Error connecting to control server %s: %s", server, exc)
|
||||||
|
|
||||||
|
@ -89,7 +91,7 @@ class ControlClient(object):
|
||||||
headers={'content-type': 'application/json'},
|
headers={'content-type': 'application/json'},
|
||||||
verify=False,
|
verify=False,
|
||||||
proxies=ControlClient.proxies)
|
proxies=ControlClient.proxies)
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.warn("Error connecting to control server %s: %s",
|
LOG.warn("Error connecting to control server %s: %s",
|
||||||
WormConfiguration.current_server, exc)
|
WormConfiguration.current_server, exc)
|
||||||
return {}
|
return {}
|
||||||
|
@ -105,7 +107,7 @@ class ControlClient(object):
|
||||||
headers={'content-type': 'application/json'},
|
headers={'content-type': 'application/json'},
|
||||||
verify=False,
|
verify=False,
|
||||||
proxies=ControlClient.proxies)
|
proxies=ControlClient.proxies)
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.warn("Error connecting to control server %s: %s",
|
LOG.warn("Error connecting to control server %s: %s",
|
||||||
WormConfiguration.current_server, exc)
|
WormConfiguration.current_server, exc)
|
||||||
|
|
||||||
|
@ -118,7 +120,7 @@ class ControlClient(object):
|
||||||
verify=False,
|
verify=False,
|
||||||
proxies=ControlClient.proxies)
|
proxies=ControlClient.proxies)
|
||||||
|
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.warn("Error connecting to control server %s: %s",
|
LOG.warn("Error connecting to control server %s: %s",
|
||||||
WormConfiguration.current_server, exc)
|
WormConfiguration.current_server, exc)
|
||||||
return
|
return
|
||||||
|
@ -126,7 +128,7 @@ class ControlClient(object):
|
||||||
try:
|
try:
|
||||||
unknown_variables = WormConfiguration.from_dict(reply.json().get('config'))
|
unknown_variables = WormConfiguration.from_dict(reply.json().get('config'))
|
||||||
LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),))
|
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
|
# 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",
|
LOG.error("Error parsing JSON reply from control server %s (%s): %s",
|
||||||
WormConfiguration.current_server, reply._content, exc)
|
WormConfiguration.current_server, reply._content, exc)
|
||||||
|
@ -145,7 +147,7 @@ class ControlClient(object):
|
||||||
headers={'content-type': 'application/json'},
|
headers={'content-type': 'application/json'},
|
||||||
verify=False,
|
verify=False,
|
||||||
proxies=ControlClient.proxies)
|
proxies=ControlClient.proxies)
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc)
|
LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -215,7 +217,7 @@ class ControlClient(object):
|
||||||
if size == monkeyfs.getsize(dest_file):
|
if size == monkeyfs.getsize(dest_file):
|
||||||
return dest_file
|
return dest_file
|
||||||
|
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.warn("Error connecting to control server %s: %s",
|
LOG.warn("Error connecting to control server %s: %s",
|
||||||
WormConfiguration.current_server, exc)
|
WormConfiguration.current_server, exc)
|
||||||
|
|
||||||
|
@ -243,7 +245,7 @@ class ControlClient(object):
|
||||||
else:
|
else:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.warn("Error connecting to control server %s: %s",
|
LOG.warn("Error connecting to control server %s: %s",
|
||||||
WormConfiguration.current_server, exc)
|
WormConfiguration.current_server, exc)
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
|
import argparse
|
||||||
|
import ctypes
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import pprint
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import ctypes
|
|
||||||
import shutil
|
|
||||||
import pprint
|
|
||||||
import logging
|
|
||||||
import subprocess
|
|
||||||
import argparse
|
|
||||||
from ctypes import c_char_p
|
from ctypes import c_char_p
|
||||||
|
|
||||||
|
from config import WormConfiguration
|
||||||
from exploit.tools import build_monkey_commandline_explicitly
|
from exploit.tools import build_monkey_commandline_explicitly
|
||||||
from model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
|
from model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
|
||||||
from config import WormConfiguration
|
|
||||||
from system_info import SystemInfoCollector, OperatingSystem
|
from system_info import SystemInfoCollector, OperatingSystem
|
||||||
|
|
||||||
if "win32" == sys.platform:
|
if "win32" == sys.platform:
|
||||||
|
@ -19,6 +19,12 @@ if "win32" == sys.platform:
|
||||||
else:
|
else:
|
||||||
DETACHED_PROCESS = 0
|
DETACHED_PROCESS = 0
|
||||||
|
|
||||||
|
# Linux doesn't have WindowsError
|
||||||
|
try:
|
||||||
|
WindowsError
|
||||||
|
except NameError:
|
||||||
|
WindowsError = None
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -62,7 +68,7 @@ class MonkeyDrops(object):
|
||||||
self._config['source_path'], self._config['destination_path'])
|
self._config['source_path'], self._config['destination_path'])
|
||||||
|
|
||||||
file_moved = True
|
file_moved = True
|
||||||
except (WindowsError, IOError, OSError), exc:
|
except (WindowsError, IOError, OSError) as exc:
|
||||||
LOG.debug("Error moving source file '%s' into '%s': %s",
|
LOG.debug("Error moving source file '%s' into '%s': %s",
|
||||||
self._config['source_path'], self._config['destination_path'],
|
self._config['source_path'], self._config['destination_path'],
|
||||||
exc)
|
exc)
|
||||||
|
@ -75,7 +81,7 @@ class MonkeyDrops(object):
|
||||||
|
|
||||||
LOG.info("Copied source file '%s' into '%s'",
|
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), exc:
|
except (WindowsError, IOError, OSError) as exc:
|
||||||
LOG.error("Error copying source file '%s' into '%s': %s",
|
LOG.error("Error copying source file '%s' into '%s': %s",
|
||||||
self._config['source_path'], self._config['destination_path'],
|
self._config['source_path'], self._config['destination_path'],
|
||||||
exc)
|
exc)
|
||||||
|
@ -89,7 +95,7 @@ class MonkeyDrops(object):
|
||||||
dropper_date_reference_path = WormConfiguration.dropper_date_reference_path_linux
|
dropper_date_reference_path = WormConfiguration.dropper_date_reference_path_linux
|
||||||
try:
|
try:
|
||||||
ref_stat = os.stat(dropper_date_reference_path)
|
ref_stat = os.stat(dropper_date_reference_path)
|
||||||
except:
|
except OSError as exc:
|
||||||
LOG.warn("Cannot set reference date using '%s', file not found",
|
LOG.warn("Cannot set reference date using '%s', file not found",
|
||||||
dropper_date_reference_path)
|
dropper_date_reference_path)
|
||||||
else:
|
else:
|
||||||
|
@ -131,12 +137,12 @@ class MonkeyDrops(object):
|
||||||
# try removing the file first
|
# try removing the file first
|
||||||
try:
|
try:
|
||||||
os.remove(self._config['source_path'])
|
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)
|
LOG.debug("Error removing source file '%s': %s", self._config['source_path'], exc)
|
||||||
|
|
||||||
# mark the file for removal on next boot
|
# mark the file for removal on next boot
|
||||||
dropper_source_path_ctypes = c_char_p(self._config['source_path'])
|
dropper_source_path_ctypes = c_char_p(self._config['source_path'])
|
||||||
if 0 == ctypes.windll.kernel32.MoveFileExA( dropper_source_path_ctypes, None,
|
if 0 == ctypes.windll.kernel32.MoveFileExA(dropper_source_path_ctypes, None,
|
||||||
MOVEFILE_DELAY_UNTIL_REBOOT):
|
MOVEFILE_DELAY_UNTIL_REBOOT):
|
||||||
LOG.debug("Error marking source file '%s' for deletion on next boot (error %d)",
|
LOG.debug("Error marking source file '%s' for deletion on next boot (error %d)",
|
||||||
self._config['source_path'], ctypes.windll.kernel32.GetLastError())
|
self._config['source_path'], ctypes.windll.kernel32.GetLastError())
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
"self_delete_in_cleanup": true,
|
"self_delete_in_cleanup": true,
|
||||||
"serialize_config": false,
|
"serialize_config": false,
|
||||||
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}",
|
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}",
|
||||||
"skip_exploit_if_file_exist": true,
|
"skip_exploit_if_file_exist": false,
|
||||||
"exploit_user_list": [],
|
"exploit_user_list": [],
|
||||||
"exploit_password_list": [],
|
"exploit_password_list": [],
|
||||||
"exploit_lm_hash_list": [],
|
"exploit_lm_hash_list": [],
|
||||||
|
|
|
@ -53,6 +53,9 @@ class ElasticGroovyExploiter(HostExploiter):
|
||||||
LOG.info("Host: %s doesn't have ES open" % host.ip_addr)
|
LOG.info("Host: %s doesn't have ES open" % host.ip_addr)
|
||||||
return False
|
return False
|
||||||
major, minor, build = host.services[ES_SERVICE]['version'].split('.')
|
major, minor, build = host.services[ES_SERVICE]['version'].split('.')
|
||||||
|
major = int(major)
|
||||||
|
minor = int(minor)
|
||||||
|
build = int(build)
|
||||||
if major > 1:
|
if major > 1:
|
||||||
return False
|
return False
|
||||||
if major == 1 and minor > 4:
|
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 (
|
elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "6") and (
|
||||||
samba_version_parts[1] <= "3"):
|
samba_version_parts[1] <= "3"):
|
||||||
is_vulnerable = True
|
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" %
|
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)))
|
(host.ip_addr, smb_server_name, samba_version, repr(is_vulnerable)))
|
||||||
|
@ -304,7 +307,7 @@ class SambaCryExploiter(HostExploiter):
|
||||||
try:
|
try:
|
||||||
# the extra / on the beginning is required for the vulnerability
|
# the extra / on the beginning is required for the vulnerability
|
||||||
self.open_pipe(smb_client, "/" + module_path)
|
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.
|
# 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:
|
if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -64,7 +64,8 @@ class ShellShockExploiter(HostExploiter):
|
||||||
|
|
||||||
# we want to report all vulnerable URLs even if we didn't succeed
|
# we want to report all vulnerable URLs even if we didn't succeed
|
||||||
# let's overload this
|
# 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
|
# now try URLs until we install something on victim
|
||||||
for _, url, header, exploit in exploitable_urls:
|
for _, url, header, exploit in exploitable_urls:
|
||||||
|
|
|
@ -171,9 +171,11 @@ class ChaosMonkey(object):
|
||||||
ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
|
ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
|
||||||
'exploiter': exploiter.__class__.__name__})
|
'exploiter': exploiter.__class__.__name__})
|
||||||
|
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.error("Exception while attacking %s using %s: %s",
|
LOG.error("Exception while attacking %s using %s: %s",
|
||||||
machine, exploiter.__class__.__name__, exc)
|
machine, exploiter.__class__.__name__, exc)
|
||||||
|
ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
|
||||||
|
'exploiter': exploiter.__class__.__name__})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if successful_exploiter:
|
if successful_exploiter:
|
||||||
|
|
|
@ -6,6 +6,12 @@ from enum import IntEnum
|
||||||
|
|
||||||
from network.info import get_host_subnets
|
from network.info import get_host_subnets
|
||||||
|
|
||||||
|
# Linux doesn't have WindowsError
|
||||||
|
try:
|
||||||
|
WindowsError
|
||||||
|
except NameError:
|
||||||
|
WindowsError = None
|
||||||
|
|
||||||
__author__ = 'uri'
|
__author__ = 'uri'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import ctypes
|
import ctypes
|
||||||
import binascii
|
import binascii
|
||||||
import logging
|
import logging
|
||||||
|
import socket
|
||||||
|
|
||||||
__author__ = 'itay.mizeretz'
|
__author__ = 'itay.mizeretz'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MimikatzCollector:
|
class MimikatzCollector:
|
||||||
"""
|
"""
|
||||||
Password collection module for Windows using Mimikatz.
|
Password collection module for Windows using Mimikatz.
|
||||||
|
@ -24,7 +23,7 @@ class MimikatzCollector:
|
||||||
self._collect = collect_proto(("collect", self._dll))
|
self._collect = collect_proto(("collect", self._dll))
|
||||||
self._get = get_proto(("get", self._dll))
|
self._get = get_proto(("get", self._dll))
|
||||||
self._isInit = True
|
self._isInit = True
|
||||||
except StandardError as ex:
|
except StandardError:
|
||||||
LOG.exception("Error initializing mimikatz collector")
|
LOG.exception("Error initializing mimikatz collector")
|
||||||
|
|
||||||
def get_logon_info(self):
|
def get_logon_info(self):
|
||||||
|
@ -40,18 +39,28 @@ class MimikatzCollector:
|
||||||
entry_count = self._collect()
|
entry_count = self._collect()
|
||||||
|
|
||||||
logon_data_dictionary = {}
|
logon_data_dictionary = {}
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
|
||||||
for i in range(entry_count):
|
for i in range(entry_count):
|
||||||
entry = self._get()
|
entry = self._get()
|
||||||
username = str(entry.username)
|
username = entry.username.encode('utf-8').strip()
|
||||||
password = str(entry.password)
|
|
||||||
|
password = entry.password.encode('utf-8').strip()
|
||||||
lm_hash = binascii.hexlify(bytearray(entry.lm_hash))
|
lm_hash = binascii.hexlify(bytearray(entry.lm_hash))
|
||||||
ntlm_hash = binascii.hexlify(bytearray(entry.ntlm_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_lm = ("00000000000000000000000000000000" != lm_hash)
|
||||||
has_ntlm = ("00000000000000000000000000000000" != ntlm_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] = {}
|
logon_data_dictionary[username] = {}
|
||||||
if has_password:
|
if has_password:
|
||||||
logon_data_dictionary[username]["password"] = password
|
logon_data_dictionary[username]["password"] = password
|
||||||
|
@ -61,7 +70,7 @@ class MimikatzCollector:
|
||||||
logon_data_dictionary[username]["ntlm_hash"] = ntlm_hash
|
logon_data_dictionary[username]["ntlm_hash"] = ntlm_hash
|
||||||
|
|
||||||
return logon_data_dictionary
|
return logon_data_dictionary
|
||||||
except StandardError as ex:
|
except StandardError:
|
||||||
LOG.exception("Error getting logon info")
|
LOG.exception("Error getting logon info")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import sys
|
|
||||||
import ctypes
|
import ctypes
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
from config import WormConfiguration
|
from config import WormConfiguration
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
@ -28,7 +29,7 @@ class _SystemSingleton(object):
|
||||||
|
|
||||||
class WindowsSystemSingleton(_SystemSingleton):
|
class WindowsSystemSingleton(_SystemSingleton):
|
||||||
def __init__(self):
|
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
|
self._mutex_handle = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -84,7 +85,7 @@ class LinuxSystemSingleton(_SystemSingleton):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sock.bind('\0' + self._unix_sock_name)
|
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",
|
LOG.error("Cannot acquire system singleton %r, error code %d, error: %s",
|
||||||
self._unix_sock_name, e.args[0], e.args[1])
|
self._unix_sock_name, e.args[0], e.args[1])
|
||||||
return False
|
return False
|
||||||
|
@ -100,9 +101,12 @@ class LinuxSystemSingleton(_SystemSingleton):
|
||||||
self._sock_handle.close()
|
self._sock_handle.close()
|
||||||
self._sock_handle = None
|
self._sock_handle = None
|
||||||
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
import winerror
|
import winerror
|
||||||
|
|
||||||
SystemSingleton = WindowsSystemSingleton
|
SystemSingleton = WindowsSystemSingleton
|
||||||
else:
|
else:
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
SystemSingleton = LinuxSystemSingleton
|
SystemSingleton = LinuxSystemSingleton
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import urllib, BaseHTTPServer, threading, os.path
|
import BaseHTTPServer
|
||||||
import monkeyfs
|
import os.path
|
||||||
from logging import getLogger
|
|
||||||
from base import TransportProxyBase, update_last_serve_time
|
|
||||||
from urlparse import urlsplit
|
|
||||||
import select
|
import select
|
||||||
import socket
|
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'
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
@ -55,9 +59,9 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def send_head(self):
|
def send_head(self):
|
||||||
if self.path != '/'+urllib.quote(os.path.basename(self.filename)):
|
if self.path != '/' + urllib.quote(os.path.basename(self.filename)):
|
||||||
self.send_error (500, "")
|
self.send_error(500, "")
|
||||||
return
|
return None, 0, 0
|
||||||
f = None
|
f = None
|
||||||
try:
|
try:
|
||||||
f = monkeyfs.open(self.filename, 'rb')
|
f = monkeyfs.open(self.filename, 'rb')
|
||||||
|
@ -163,7 +167,7 @@ class HTTPServer(threading.Thread):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def report_download(dest=None):
|
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
|
self.downloads += 1
|
||||||
|
|
||||||
httpd = BaseHTTPServer.HTTPServer((self._local_ip, self._local_port), TempHandler)
|
httpd = BaseHTTPServer.HTTPServer((self._local_ip, self._local_port), TempHandler)
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
|
import logging
|
||||||
import socket
|
import socket
|
||||||
import struct
|
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
|
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'
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ def _check_tunnel(address, port, existing_sock=None):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sock.sendto("+", (address, MCAST_PORT))
|
sock.sendto("+", (address, MCAST_PORT))
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.debug("Caught exception in tunnel registration: %s", exc)
|
LOG.debug("Caught exception in tunnel registration: %s", exc)
|
||||||
|
|
||||||
if not existing_sock:
|
if not existing_sock:
|
||||||
|
@ -91,7 +92,7 @@ def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT):
|
||||||
sock.close()
|
sock.close()
|
||||||
return address, port
|
return address, port
|
||||||
|
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.debug("Caught exception in tunnel lookup: %s", exc)
|
LOG.debug("Caught exception in tunnel lookup: %s", exc)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -104,7 +105,7 @@ def quit_tunnel(address, timeout=DEFAULT_TIMEOUT):
|
||||||
sock.sendto("-", (address, MCAST_PORT))
|
sock.sendto("-", (address, MCAST_PORT))
|
||||||
sock.close()
|
sock.close()
|
||||||
LOG.debug("Success quitting tunnel")
|
LOG.debug("Success quitting tunnel")
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.debug("Exception quitting tunnel: %s", exc)
|
LOG.debug("Exception quitting tunnel: %s", exc)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,8 @@ class Monkey(flask_restful.Resource):
|
||||||
update['$set']['config_error'] = monkey_json['config_error']
|
update['$set']['config_error'] = monkey_json['config_error']
|
||||||
|
|
||||||
if 'tunnel' in monkey_json:
|
if 'tunnel' in monkey_json:
|
||||||
host = monkey_json['tunnel'].split(":")[-2].replace("//", "")
|
tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace("//", "")
|
||||||
tunnel_host_id = NodeService.get_monkey_by_ip(host)["_id"]
|
NodeService.set_monkey_tunnel(monkey["_id"], tunnel_host_ip)
|
||||||
NodeService.set_monkey_tunnel(monkey["_id"], tunnel_host_id)
|
|
||||||
|
|
||||||
return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False)
|
return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False)
|
||||||
|
|
||||||
|
@ -98,10 +97,9 @@ class Monkey(flask_restful.Resource):
|
||||||
else:
|
else:
|
||||||
monkey_json['parent'] = db_monkey.get('parent') + [parent_to_add]
|
monkey_json['parent'] = db_monkey.get('parent') + [parent_to_add]
|
||||||
|
|
||||||
tunnel_host_id = None
|
tunnel_host_ip = None
|
||||||
if 'tunnel' in monkey_json:
|
if 'tunnel' in monkey_json:
|
||||||
host = monkey_json['tunnel'].split(":")[-2].replace("//", "")
|
tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace("//", "")
|
||||||
tunnel_host_id = NodeService.get_monkey_by_ip(host)["_id"]
|
|
||||||
monkey_json.pop('tunnel')
|
monkey_json.pop('tunnel')
|
||||||
|
|
||||||
mongo.db.monkey.update({"guid": monkey_json["guid"]},
|
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"]
|
new_monkey_id = mongo.db.monkey.find_one({"guid": monkey_json["guid"]})["_id"]
|
||||||
|
|
||||||
if tunnel_host_id is not None:
|
if tunnel_host_ip is not None:
|
||||||
NodeService.set_monkey_tunnel(new_monkey_id, tunnel_host_id)
|
NodeService.set_monkey_tunnel(new_monkey_id, tunnel_host_ip)
|
||||||
|
|
||||||
existing_node = mongo.db.node.find_one({"ip_addresses": {"$in": monkey_json["ip_addresses"]}})
|
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):
|
def process_tunnel_telemetry(self, telemetry_json):
|
||||||
monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"]
|
monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"]
|
||||||
if telemetry_json['data']['proxy'] is not None:
|
if telemetry_json['data']['proxy'] is not None:
|
||||||
host = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "")
|
tunnel_host_ip = 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_ip)
|
||||||
NodeService.set_monkey_tunnel(monkey_id, tunnel_host_id)
|
|
||||||
else:
|
else:
|
||||||
NodeService.unset_all_monkey_tunnels(monkey_id)
|
NodeService.unset_all_monkey_tunnels(monkey_id)
|
||||||
|
|
||||||
|
|
|
@ -506,7 +506,7 @@ SCHEMA = {
|
||||||
"skip_exploit_if_file_exist": {
|
"skip_exploit_if_file_exist": {
|
||||||
"title": "Skip exploit if file exists",
|
"title": "Skip exploit if file exists",
|
||||||
"type": "boolean",
|
"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"
|
"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
|
@staticmethod
|
||||||
def edge_to_displayed_edge(edge):
|
def edge_to_displayed_edge(edge):
|
||||||
services = {}
|
services = []
|
||||||
os = {}
|
os = {}
|
||||||
exploits = []
|
exploits = []
|
||||||
if len(edge["scans"]) > 0:
|
if len(edge["scans"]) > 0:
|
||||||
|
@ -52,6 +52,7 @@ class EdgeService:
|
||||||
exploit_container["end_timestamp"] = new_exploit["timestamp"]
|
exploit_container["end_timestamp"] = new_exploit["timestamp"]
|
||||||
|
|
||||||
displayed_edge = EdgeService.edge_to_net_edge(edge)
|
displayed_edge = EdgeService.edge_to_net_edge(edge)
|
||||||
|
|
||||||
displayed_edge["ip_address"] = edge["ip_address"]
|
displayed_edge["ip_address"] = edge["ip_address"]
|
||||||
displayed_edge["services"] = services
|
displayed_edge["services"] = services
|
||||||
displayed_edge["os"] = os
|
displayed_edge["os"] = os
|
||||||
|
|
|
@ -148,7 +148,8 @@ class NodeService:
|
||||||
upsert=False)
|
upsert=False)
|
||||||
|
|
||||||
@staticmethod
|
@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)
|
NodeService.unset_all_monkey_tunnels(monkey_id)
|
||||||
mongo.db.monkey.update(
|
mongo.db.monkey.update(
|
||||||
{"_id": monkey_id},
|
{"_id": monkey_id},
|
||||||
|
@ -156,7 +157,7 @@ class NodeService:
|
||||||
upsert=False)
|
upsert=False)
|
||||||
tunnel_edge = EdgeService.get_or_create_edge(monkey_id, tunnel_host_id)
|
tunnel_edge = EdgeService.get_or_create_edge(monkey_id, tunnel_host_id)
|
||||||
mongo.db.edge.update({"_id": tunnel_edge["_id"]},
|
mongo.db.edge.update({"_id": tunnel_edge["_id"]},
|
||||||
{'$set': {'tunnel': True}},
|
{'$set': {'tunnel': True, 'ip_address': tunnel_host_ip}},
|
||||||
upsert=False)
|
upsert=False)
|
||||||
|
|
||||||
@staticmethod
|
@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',
|
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
|
||||||
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
|
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
|
||||||
|
|
||||||
|
let legend = require('../../images/map-legend.png');
|
||||||
|
|
||||||
let getGroupsOptions = () => {
|
let getGroupsOptions = () => {
|
||||||
let groupOptions = {};
|
let groupOptions = {};
|
||||||
for (let groupName of groupNames) {
|
for (let groupName of groupNames) {
|
||||||
|
@ -28,6 +30,7 @@ let options = {
|
||||||
improvedLayout: false
|
improvedLayout: false
|
||||||
},
|
},
|
||||||
edges: {
|
edges: {
|
||||||
|
width: 2,
|
||||||
smooth: {
|
smooth: {
|
||||||
type: 'curvedCW'
|
type: 'curvedCW'
|
||||||
}
|
}
|
||||||
|
@ -61,7 +64,7 @@ class MapPageComponent extends React.Component {
|
||||||
case 'exploited':
|
case 'exploited':
|
||||||
return '#c00';
|
return '#c00';
|
||||||
case 'tunnel':
|
case 'tunnel':
|
||||||
return '#aaa';
|
return '#0058aa';
|
||||||
case 'scan':
|
case 'scan':
|
||||||
return '#f90';
|
return '#f90';
|
||||||
case 'island':
|
case 'island':
|
||||||
|
@ -129,6 +132,9 @@ class MapPageComponent extends React.Component {
|
||||||
<Col xs={12}>
|
<Col xs={12}>
|
||||||
<h1 className="page-title">Infection Map</h1>
|
<h1 className="page-title">Infection Map</h1>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col xs={12}>
|
||||||
|
<img src={legend}/>
|
||||||
|
</Col>
|
||||||
<Col xs={8}>
|
<Col xs={8}>
|
||||||
<Graph graph={this.state.graph} options={options} events={this.events}/>
|
<Graph graph={this.state.graph} options={options} events={this.events}/>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -134,13 +134,18 @@ class RunMonkeyPageComponent extends React.Component {
|
||||||
Run on C&C Server
|
Run on C&C Server
|
||||||
{ this.renderIconByState(this.state.runningOnIslandState) }
|
{ this.renderIconByState(this.state.runningOnIslandState) }
|
||||||
</button>
|
</button>
|
||||||
<a
|
{
|
||||||
|
// TODO: implement button functionality
|
||||||
|
/*
|
||||||
|
<button
|
||||||
className="btn btn-default"
|
className="btn btn-default"
|
||||||
disabled={this.state.runningOnClientState !== 'not_running'}
|
disabled={this.state.runningOnClientState !== 'not_running'}
|
||||||
style={{'marginLeft': '1em'}}>
|
style={{'marginLeft': '1em'}}>
|
||||||
Download and run locally
|
Download and run locally
|
||||||
{ this.renderIconByState(this.state.runningOnClientState) }
|
{ this.renderIconByState(this.state.runningOnClientState) }
|
||||||
</a>
|
</button>
|
||||||
|
*/
|
||||||
|
}
|
||||||
</p>
|
</p>
|
||||||
<div className="run-monkey-snippets" style={{'marginBottom': '3em'}}>
|
<div className="run-monkey-snippets" style={{'marginBottom': '3em'}}>
|
||||||
<p>
|
<p>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Loading…
Reference in New Issue