diff --git a/chaos_monkey/exploit/rdpgrinder.py b/chaos_monkey/exploit/rdpgrinder.py index c0cc6aa27..6d2bbde18 100644 --- a/chaos_monkey/exploit/rdpgrinder.py +++ b/chaos_monkey/exploit/rdpgrinder.py @@ -292,7 +292,7 @@ class RdpExploiter(HostExploiter): LOG.info("Trying RDP logging into victim %r with user %s and password '%s'", host, self._config.psexec_user, password) - LOG.info("RDP logged in to %r", host) + LOG.info("RDP connected to %r", host) client_factory = CMDClientFactory(self._config.psexec_user, password, "", command) diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py index 9fab2d715..b6cee68ac 100644 --- a/chaos_monkey/exploit/tools.py +++ b/chaos_monkey/exploit/tools.py @@ -1,4 +1,7 @@ import os +import sys +import socket +import struct import ntpath import pprint import logging @@ -8,7 +11,7 @@ import monkeyfs from difflib import get_close_matches from network import local_ips from transport import HTTPServer -from network.info import get_free_tcp_port +from network.info import get_free_tcp_port, get_routes from network.firewall import app as firewall from impacket.dcerpc.v5 import transport, srvs from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError @@ -369,7 +372,7 @@ class HTTPTools(object): local_port = get_free_tcp_port() if not local_ip: - local_ip = get_close_matches(host.ip_addr, local_ips())[0] + local_ip = get_interface_to_target(host.ip_addr) if not firewall.listen_allowed(): return None, None @@ -381,6 +384,39 @@ class HTTPTools(object): return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd +def get_interface_to_target(dst): + if sys.platform == "win32": + try: + import dnet + intf = dnet.intf() + inte = intf.get_dst(dnet.addr(dst)) + return str(inte['addr']).split("/")[0] + except ImportError: + # dnet lib is not installed + return get_close_matches(dst, local_ips())[0] + else: + # based on scapy implementation + + def atol(x): + ip = socket.inet_aton(x) + return struct.unpack("!I", ip)[0] + + routes = get_routes() + dst = atol(dst) + pathes = [] + for d, m, gw, i, a in routes: + aa = atol(a) + if aa == dst: + pathes.append((0xffffffffL, ("lo", a, "0.0.0.0"))) + if (dst & m) == (d & m): + pathes.append((m, (i, a, gw))) + if not pathes: + return None + pathes.sort() + ret = pathes[-1][1] + return ret[1] + + def get_target_monkey(host): from control import ControlClient import platform diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py index d5e507846..59b40a089 100644 --- a/chaos_monkey/network/info.py +++ b/chaos_monkey/network/info.py @@ -30,9 +30,11 @@ if sys.platform == "win32": network_adapters.append((add["addr"], add["netmask"])) return network_adapters -else: - import fcntl + def get_routes(): + raise NotImplementedError() +else: + from fcntl import ioctl def get_host_subnets(only_ips=False): """Get the list of Linux network adapters.""" @@ -46,7 +48,7 @@ else: offset2 = 32 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) names = array.array('B', '\0' * max_bytes) - outbytes = struct.unpack('iL', fcntl.ioctl( + outbytes = struct.unpack('iL', ioctl( sock.fileno(), 0x8912, struct.pack('iL', max_bytes, names.buffer_info()[0])))[0] @@ -54,13 +56,13 @@ else: for n_cnt in xrange(0, outbytes, offset2)] network_adapters = [] for adapter_name in adapter_names: - ip_address = socket.inet_ntoa(fcntl.ioctl( + ip_address = socket.inet_ntoa(ioctl( sock.fileno(), 0x8915, struct.pack('256s', adapter_name))[20:24]) if ip_address.startswith('127'): continue - subnet_mask = socket.inet_ntoa(fcntl.ioctl( + subnet_mask = socket.inet_ntoa(ioctl( sock.fileno(), 0x891b, struct.pack('256s', adapter_name))[20:24]) @@ -72,10 +74,56 @@ else: return network_adapters - def local_ips(): return get_host_subnets(only_ips=True) + def get_routes(): # based on scapy implementation for route parsing + LOOPBACK_NAME = "lo" + SIOCGIFADDR = 0x8915 # get PA address + SIOCGIFNETMASK = 0x891b # get network PA mask + RTF_UP = 0x0001 # Route usable + RTF_REJECT = 0x0200 + + try: + f = open("/proc/net/route", "r") + except IOError: + return [] + routes = [] + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", LOOPBACK_NAME)) + addrfamily = struct.unpack("h", ifreq[16:18])[0] + if addrfamily == socket.AF_INET: + ifreq2 = ioctl(s, SIOCGIFNETMASK, struct.pack("16s16x", LOOPBACK_NAME)) + msk = socket.ntohl(struct.unpack("I", ifreq2[20:24])[0]) + dst = socket.ntohl(struct.unpack("I", ifreq[20:24])[0]) & msk + ifaddr = socket.inet_ntoa(ifreq[20:24]) + routes.append((dst, msk, "0.0.0.0", LOOPBACK_NAME, ifaddr)) + + for l in f.readlines()[1:]: + iff, dst, gw, flags, x, x, x, msk, x, x, x = l.split() + flags = int(flags, 16) + if flags & RTF_UP == 0: + continue + if flags & RTF_REJECT: + continue + try: + ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", iff)) + except IOError: # interface is present in routing tables but does not have any assigned IP + ifaddr = "0.0.0.0" + else: + addrfamily = struct.unpack("h", ifreq[16:18])[0] + if addrfamily == socket.AF_INET: + ifaddr = socket.inet_ntoa(ifreq[20:24]) + else: + continue + routes.append((socket.htonl(long(dst, 16)) & 0xffffffffL, + socket.htonl(long(msk, 16)) & 0xffffffffL, + socket.inet_ntoa(struct.pack("I", long(gw, 16))), + iff, ifaddr)) + + f.close() + return routes + def get_free_tcp_port(min_range=1000, max_range=65535): start_range = min(1, min_range) diff --git a/chaos_monkey/readme.txt b/chaos_monkey/readme.txt index 21becbdc2..d25742016 100644 --- a/chaos_monkey/readme.txt +++ b/chaos_monkey/readme.txt @@ -32,7 +32,11 @@ Windows: type > C:\Python27\Lib\site-packages\zope\__init__.py 7. Download and extract UPX binary to [source-path]\monkey\chaos_monkey\bin\upx.exe: http://upx.sourceforge.net/download/upx391w.zip -8. Run [source-path]\monkey\chaos_monkey\build_windows.bat to build, output is in dist\monkey.exe +8. (Optional) For some exploits to work better, install 'dnet' python library. You'll need to compile it for your OS + or use a precompiled setup that can be found at: + 32bit: https://github.com/Kondziowy/scapy_win64/raw/master/win32/dnet-1.12.win32-py2.7.exe + 64bit: https://github.com/Kondziowy/scapy_win64/raw/master/win64/dnet-1.12.win-amd64-py2.7.exe +9. Run [source-path]\monkey\chaos_monkey\build_windows.bat to build, output is in dist\monkey.exe Linux (Tested on Ubuntu 12.04): 1. Run: