# MIT License # Copyright (c) 2017 Balazs Bucsay # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # For the Windows support credit goes to: Thomas Watteyne @thomaswatteyne # https://openwsn.atlassian.net/wiki/spaces/OW/pages/5373971/tun+tap+in+Windows import sys if "interface.py" in sys.argv[0]: print("[-] Instead of poking around just try: python xfltreat.py --help") sys.exit(-1) import socket import struct import time import os import subprocess import array import common class Interface(): orig_default_gw = None def __init__(self): OSFP_table = { # OS type : [__init__(), tun_alloc(), set_ip_address(), # set_mtu(), close_tunnel(), check_default_route(), # set_default_route(), set_intermediate_route(), restore_routes()] common.OS_LINUX : [self.lin_init, self.lin_tun_alloc, self.lin_set_ip_address, self.lin_set_mtu, self.lin_close_tunnel, self.lin_check_default_route, self.lin_set_default_route, self.lin_set_intermediate_route, self.lin_restore_routes, self.lin_set_split_route, self.lin_del_split_route], common.OS_MACOSX : [self.mac_init, self.mac_tun_alloc, self.mac_set_ip_address, self.mac_set_mtu, self.mac_close_tunnel, self.mac_check_default_route, self.mac_set_default_route, self.mac_set_intermediate_route, self.mac_restore_routes, self.mac_set_split_route, self.mac_del_split_route], common.OS_WINDOWS : [self.win_init, self.win_tun_alloc, self.win_set_ip_address, self.win_set_mtu, self.win_close_tunnel, self.win_check_default_route, self.win_set_default_route, self.win_set_intermediate_route, self.win_restore_routes, self.win_set_split_route, self.win_del_split_route], common.OS_FREEBSD : [self.freebsd_init, self.freebsd_tun_alloc, self.freebsd_set_ip_address, self.freebsd_set_mtu, self.freebsd_close_tunnel, self.freebsd_check_default_route, self.freebsd_set_default_route, self.freebsd_set_intermediate_route, self.freebsd_restore_routes, self.freebsd_set_split_route, self.freebsd_del_split_route], } os_type = common.get_os_type() if not (os_type in OSFP_table): common.internal_print("Your operating system is not supported yet. (interface.py)", -1) sys.exit(-1) # calling OS specific __init__() OSFP_table[os_type][0]() # replacing placeholders with OS specific calls # allocating tunnel, clonde device and name it self.tun_alloc = OSFP_table[os_type][1] # setting IP address + netmask on the interface self.set_ip_address = OSFP_table[os_type][2] # setting MTU on the interface self.set_mtu = OSFP_table[os_type][3] # closing tunnel file descriptor self.close_tunnel = OSFP_table[os_type][4] # check if more than one or no default route is present self.check_default_route = OSFP_table[os_type][5] # automatic routing set up. # check for multiple default routes, if there are then print error message # - save default route address # - delete default route # - add default route, route all packets into the XFLTReaT interface # - last route: server IP address routed over the original default route self.set_default_route = OSFP_table[os_type][6] # setting up intermediate route # when the module needs an intermediate hop (DNS server, Proxy server) # then all encapsulated packet should be sent to the intermediate server # instead of the XFLTReaT server self.set_intermediate_route = OSFP_table[os_type][7] # restoring default route self.restore_routes = OSFP_table[os_type][8] # set split routes self.set_split_route = OSFP_table[os_type][9] # del split routes self.del_split_route = OSFP_table[os_type][10] # LINUX ######################################################### IFF_TUN = 0x0001 IFF_TAP = 0x0002 IFF_NO_PI = 0x1000 LINUX_CLONEDEV = "/dev/net/tun" IOCTL_LINUX_TUNSETIFF = 0x400454ca IOCTL_LINUX_SIOCGIFFLAGS = 0x8913 IOCTL_LINUX_SIOCSIFFLAGS = 0x8914 IOCTL_LINUX_SIOCSIFADDR = 0x8916 IOCTL_LINUX_SIOCSIFNETMASK = 0x891C IOCTL_LINUX_SIOCSIFMTU = 0x8922 IOCTL_LINUX_IFF_UP = 0x1 IOCTL_MACOSX_SIOCSIFADDR = 0x8020690c IOCTL_MACOSX_SIOCSIFNETMASK = 0x80206916 IOCTL_MACOSX_SIOCSIFMTU = 0x80206934 IOCTL_MACOSX_SIOCSIFFLAGS = 0x80206910 IOCTL_MACOSX_SIOCAIFADDR = 0x8040691A IOCTL_FREEBSD_SIOCIFCREATE2 = 0xc020697c IOCTL_FREEBSD_SIOCIFDESTROY = 0x80206979 IOCTL_FREEBSD_SIOCSIFNAME = 0x80206928 IOCTL_FREEBSD_SIOCAIFADDR = 0x8044692b IOCTL_FREEBSD_SIOCGIFFLAGS = 0xc0206911 IOCTL_FREEBSD_SIOCSIFFLAGS = 0x80206910 IOCTL_FREEBSD_SIOCSIFMTU = 0x80206934 IOCTL_FREEBSD_IFF_UP = 0x1 IOCTL_FREEBSD_FIODGNAME = 0x80106678 # __init__() def lin_init(self): global fcntl import fcntl def lin_tun_alloc(self, dev, flags): try: tun = os.open(Interface.LINUX_CLONEDEV, os.O_RDWR|os.O_NONBLOCK, 0) ifr = struct.pack('16sH', dev, flags) fcntl.ioctl(tun, self.IOCTL_LINUX_TUNSETIFF, ifr) except IOError: common.internal_print("Error: Cannot create tunnel. Is {0} in use?".format(dev), -1) sys.exit(-1) return tun def lin_set_ip_address(self, dev, ip, serverip, netmask): sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: # set IP ifr = struct.pack('<16sH2s4s8s', dev, socket.AF_INET, "\x00"*2, socket.inet_aton(ip), "\x00"*8) fcntl.ioctl(sockfd, self.IOCTL_LINUX_SIOCSIFADDR, ifr) # get flags ifr = struct.pack('<16sh', dev, 0) flags = struct.unpack('<16sh', fcntl.ioctl(sockfd, self.IOCTL_LINUX_SIOCSIFFLAGS, ifr))[1] # set new flags flags = flags | self.IOCTL_LINUX_IFF_UP ifr = struct.pack('<16sh', dev, flags) # iface up fcntl.ioctl(sockfd, self.IOCTL_LINUX_SIOCSIFFLAGS, ifr) except Exception as e: common.internal_print("Something went wrong with setting up the interface.", -1) print(e) sys.exit(-1) # adding new route for forwarding packets properly. integer_ip = struct.unpack(">I", socket.inet_pton(socket.AF_INET, serverip))[0] rangeip = socket.inet_ntop(socket.AF_INET, struct.pack(">I", integer_ip & ((2**int(netmask))-1)<<32-int(netmask))) integer_netmask = struct.pack(">I", ((2**int(netmask))-1)<<32-int(netmask)) netmask = socket.inet_ntoa(integer_netmask) ps = subprocess.Popen(["route", "add", "-net", rangeip, "netmask", netmask, "dev", dev], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding client route: {0}".format(stderr), -1) sys.exit(-1) return def lin_set_mtu(self, dev, mtu): s = socket.socket(type=socket.SOCK_DGRAM) try: ifr = struct.pack('<16sH', dev, mtu) + '\x00'*14 fcntl.ioctl(s, self.IOCTL_LINUX_SIOCSIFMTU, ifr) except Exception as e: common.internal_print("Cannot set MTU ({0}) on interface".format(mtu), -1) sys.exit(-1) return def lin_close_tunnel(self, tun): try: os.close(tun) except: pass return def lin_check_default_route(self): # get default gateway(s) ps = subprocess.Popen(["route", "-n"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr != "": common.internal_print("Route error: {0}".format(stderr), -1) sys.exit(-1) lines = stdout.split("\n") default_route_number = 0 for line in lines: if line[0:7] == "0.0.0.0": default_route_number += 1 if default_route_number < 1: common.internal_print("No default route. Please set up your routing before executing the tool", -1) sys.exit(-1) if default_route_number > 1: common.internal_print("More than one default route. This should be reviewed before executing the tool.", -1) sys.exit(-1) return def lin_set_default_route(self, serverip, clientip, ip): self.check_default_route() # get default gateway ps = subprocess.Popen(["route", "-n"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() lines = stdout.split("\n") default_route_number = 0 for line in lines: if line[0:7] == "0.0.0.0": default_route_line = line[7:] i = 0 while default_route_line[i:i+1] == " ": i += 1 default_route_line = default_route_line[i:] # original default route saved self.orig_default_gw = default_route_line[0:default_route_line.find(" ")] # adding static route to the VPN server via original route ps = subprocess.Popen(["route", "add", "-host", serverip, "gw", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) # delete original default route ps = subprocess.Popen(["route", "delete", "default"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: deleting default route: {0}".format(stderr), -1) sys.exit(-1) # new default route set via VPN server ps = subprocess.Popen(["route", "add", "default", "gw", ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: adding new default route: {0}".format(stderr), -1) sys.exit(-1) return def lin_set_intermediate_route(self, serverip, proxyip): common.internal_print("Changing route table for intermediate hop") ps = subprocess.Popen(["route", "delete", serverip, "gw", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: delete old route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "-host", proxyip, "gw", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) return def lin_restore_routes(self, serverip, clientip, ip): common.internal_print("Restoring default route") ps = subprocess.Popen(["route", "delete", serverip, "gw", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: delete old route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "default", "gw", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) return def lin_set_split_route(self, scope, ip): for entry in scope: ps = subprocess.Popen(["route", "add", "-net", "{0}/{1}".format(entry[0], entry[2]), "gw", ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: add split route: {0}".format(stderr), -1) sys.exit(-1) return def lin_del_split_route(self, scope, ip): return # MAC OS (X) #################################################### MACOS_UTUN_CONTROL_NAME = "com.apple.net.utun_control" MACOS_PF_SYSTEM = 32 MACOS_AF_SYSTEM = 32 MACOS_SYSPROTO_CONTROL = 2 MACOS_AF_SYS_CONTROL = 2 MACOS_UTUN_OPT_IFNAME = 2 MACOS_MAX_KCTL_NAME = 96 MACOS_CTLIOCGINFO = 0xc0644e03 MACOS_temp = None # __init__() def mac_init(self): global fcntl import fcntl def mac_tun_alloc(self, dev, flags): ''' # before utun, tun/tap driver had to be used. utun support was # added to MacOS 10.7+ so there is no need for tun/tap ext. if common.get_os_release() == '13.4.0': #TODO loop to look for an interface that is not busy for i in range(0, 16): self.iface_name = "tun{0}".format(i) try: tun = os.open("/dev/"+self.iface_name, os.O_EXCL|os.O_RDWR, 0) print tun except Exception as exception: if exception.args[0] == 16: continue else: print exception sys.exit(-1) break else: ''' # MacOS utun support # direct calls to libc are needed, because otherwise it could not # done. import ctypes import ctypes.util self.iface_name = "\x00"*10 libc_name = ctypes.util.find_library('c') libc = ctypes.CDLL(libc_name, use_errno=True) # special socket to poke MacOS(X)' soul s = socket.socket(self.MACOS_PF_SYSTEM, socket.SOCK_DGRAM, self.MACOS_SYSPROTO_CONTROL) # magic to make utun alive info = struct.pack("<I{0}s".format(self.MACOS_MAX_KCTL_NAME), 0, self.MACOS_UTUN_CONTROL_NAME) ctl_id = struct.unpack("<I{0}s".format(self.MACOS_MAX_KCTL_NAME), fcntl.ioctl(s, self.MACOS_CTLIOCGINFO, info))[0] # setting up the address, because the python lib does not # support this type of address type... # setting the interface number to 0 to let the kernel allocate addr = struct.pack("<BBHIIIIIIII", 32, self.MACOS_AF_SYSTEM, self.MACOS_AF_SYS_CONTROL, ctl_id, 0, 0, 0, 0, 0, 0, 0) err = libc.connect(s.fileno(), addr, 32) if err < 0: err = ctypes.get_errno() raise OSError(err, os.strerror(err)) # get interface name into the self.iface_name err = libc.getsockopt(s.fileno(), self.MACOS_SYSPROTO_CONTROL, self.MACOS_UTUN_OPT_IFNAME, ctypes.c_char_p(self.iface_name), ctypes.byref(ctypes.c_int(10))) if err < 0: err = ctypes.get_errno() raise OSError(err, os.strerror(err)) # setting flags on interface/fd fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK) fcntl.fcntl(s, fcntl.F_SETFD, fcntl.FD_CLOEXEC) # saving the socket, otherwise it will be destroyed. with the iface self.MACOS_temp = s return s.fileno() def mac_set_ip_address(self, dev, ip, serverip, netmask): ifr = struct.pack('<16sBBHIIIBBHIIIBBHIII', self.iface_name, 16, socket.AF_INET, 0, struct.unpack('<L', socket.inet_pton(socket.AF_INET, ip))[0], 0, 0, 16, socket.AF_INET, 0, struct.unpack('<L', socket.inet_pton(socket.AF_INET, serverip))[0], 0, 0, 16, 0, 0, struct.unpack('<L', socket.inet_pton(socket.AF_INET, "255.255.255.255"))[0], 0, 0) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) fcntl.ioctl(sock, self.IOCTL_MACOSX_SIOCAIFADDR, ifr) except Exception as e: common.internal_print("Something went wrong with setting up the interface.", -1) print(e) sys.exit(-1) # adding new route for forwarding packets properly. integer_ip = struct.unpack(">I", socket.inet_pton(socket.AF_INET, serverip))[0] rangeip = socket.inet_ntop(socket.AF_INET, struct.pack(">I", integer_ip & ((2**int(netmask))-1)<<32-int(netmask))) ps = subprocess.Popen(["route", "add", "-net", rangeip+"/"+netmask, serverip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding client route: {0}".format(stderr), -1) sys.exit(-1) return def mac_set_mtu(self, dev, mtu): s = socket.socket(type=socket.SOCK_DGRAM) try: ifr = struct.pack('<16sH', self.iface_name, 1350)+'\x00'*14 fcntl.ioctl(s, self.IOCTL_MACOSX_SIOCSIFMTU, ifr) except Exception as e: common.internal_print("Cannot set MTU ({0}) on interface".format(mtu), -1) sys.exit(-1) return def mac_close_tunnel(self, tun): try: os.close(tun) except: pass return def mac_check_default_route(self): # get default gateway ps = subprocess.Popen(["route", "-n", "get", "default"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() # is there a default gateway entry? if "not in table" in stderr: common.internal_print("No default route. Please set up your routing before executing the tool", -1) sys.exit(-1) # check for multiple default routes # is this even possible on MacOS(X)? return def mac_set_default_route(self, serverip, clientip, ip): # https://developer.apple.com/documentation/kernel/rt_msghdr?language=objc # s = socket(PF_ROUTE, SOCK_RAW, 0) # not sure which is the better way, calling external tools like # 'route' or implementing the messaging... # get default gateway ps = subprocess.Popen(["route", "-n", "get", "default"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() # is there a default gateway entry? if "not in table" in stderr: common.internal_print("No default route. Please set up your routing before executing the tool", -1) sys.exit(-1) self.orig_default_gw = stdout.split("gateway: ")[1].split("\n")[0] # is it an ipv4 address? if not common.is_ipv4(self.orig_default_gw): common.internal_print("Default gateway is not an IPv4 address.", -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "-net", serverip, self.orig_default_gw, "255.255.255.255"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "delete", "default"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: deleting default route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "default", ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: adding new default route: {0}".format(stderr), -1) sys.exit(-1) ''' # keeping this, in case I can test with tun, not utun ps = subprocess.Popen(["route", "add", "-net", clientip, serverip, "255.255.255.255"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding new route: {0}".format(stderr), -1) sys.exit(-1) ''' return def mac_set_intermediate_route(self, serverip, proxyip): common.internal_print("Changing route table for intermediate hop") ps = subprocess.Popen(["route", "delete", serverip, self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: delete old route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "-net", proxyip, self.orig_default_gw, "255.255.255.255"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) return def mac_restore_routes(self, serverip, clientip, ip): common.internal_print("Restoring default route") ps = subprocess.Popen(["route", "delete", serverip, self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: delete old route: {0}".format(stderr), -1) sys.exit(-1) ''' # keeping this, in case I can test with tun, not utun ps = subprocess.Popen(["route", "delete", clientip, serverip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: delete old server route: {0}".format(stderr), -1) sys.exit(-1) ''' ps = subprocess.Popen(["route", "delete", "default"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "not in table" in stderr: common.internal_print("Error: deleting default route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "default", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) return def mac_set_split_route(self, scope, ip): for entry in scope: ps = subprocess.Popen(["route", "add", "{0}/{1}".format(entry[0], entry[2]), ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: adding new split route: {0}".format(stderr), -1) sys.exit(-1) return def mac_del_split_route(self, scope, ip): return # WINDOWS ####################################################### WINDOWS_ADAPTER_KEY = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" TUNTAP_COMPONENT_ID = "tap0901" def win_init(self): global registry, win32file import _winreg as registry import win32file return # get GUID of tap device from registry (Windows) def WIN_get_device_guid(self): try: regkey = registry.OpenKey(registry.HKEY_LOCAL_MACHINE, self.WINDOWS_ADAPTER_KEY) for i in xrange(10000): key_name = registry.EnumKey(regkey, i) try: regsubkey = registry.OpenKey(regkey, key_name) component_id = registry.QueryValueEx(regsubkey, "ComponentId")[0] if component_id == self.TUNTAP_COMPONENT_ID: return registry.QueryValueEx(regsubkey, 'NetCfgInstanceId')[0] except WindowsError as e: pass continue except Exception as e: pass return None return None def WIN_get_subinterface_name(self): IFACE_NAME_KEY = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\" NetCfgInstanceId = self.WIN_get_device_guid() try: regkey = registry.OpenKey(registry.HKEY_LOCAL_MACHINE, IFACE_NAME_KEY+NetCfgInstanceId+"\\Connection\\") iface_name = registry.QueryValueEx(regkey, "Name")[0] except WindowsError as e: common.internal_print("Cannot get interface name. Registry key cannot be found: {0}".format(e), -1) sys.exit(-1) return iface_name def WIN_get_interface_index(self): iface_name = self.WIN_get_subinterface_name() ps = subprocess.Popen(["netsh", "interface", "ipv4", "show", "interfaces"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr != "": common.internal_print("Show interfaces. netsh failed: {0}".format(stdout), -1) sys.exit(-1) for line in stdout.split("\n"): if iface_name in line: i = 0 while line[i:i+1] == " ": i += 1 return int(line[i:].split(" ")[0]) return -1 def WIN_CTL_CODE(self, device_type, function, method, access): return (device_type << 16) | (access << 14) | (function << 2) | method; def WIN_TAP_CONTROL_CODE(self, request, method): return self.WIN_CTL_CODE(34, request, method, 0) def win_tun_alloc(self, dev, flags): TAP_IOCTL_SET_MEDIA_STATUS = self.WIN_TAP_CONTROL_CODE(6, 0) import pywintypes guid = self.WIN_get_device_guid() if not guid: common.internal_print("Please install OpenVPN's Windows TAP driver (NDIS 6) to use XFLTReaT\r\nhttps://openvpn.net/index.php/open-source/downloads.html", -1) sys.exit(-1) # create a win32file for manipulating the TUN/TAP interface try: self.wintun = win32file.CreateFile("\\\\.\\Global\\{0}.tap".format(guid), win32file.GENERIC_READ | win32file.GENERIC_WRITE, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, None, win32file.OPEN_EXISTING, win32file.FILE_ATTRIBUTE_SYSTEM | win32file.FILE_FLAG_NO_BUFFERING | win32file.FILE_FLAG_OVERLAPPED, None) except pywintypes.error as e: if e.args[0] == 31: # A device attached to the system is not functioning. common.internal_print("The TUN device is already in use. Maybe another XFLTReaT is running.", -1) sys.exit(-1) # have Windows consider the interface now connected win32file.DeviceIoControl(self.wintun, TAP_IOCTL_SET_MEDIA_STATUS, '\x01\x00\x00\x00', 1, None) return self.wintun def win_set_ip_address(self, dev, ip, serverip, netmask): TAP_WIN_IOCTL_CONFIG_TUN = self.WIN_TAP_CONTROL_CODE(10, 0) TAP_WIN_IOCTL_CONFIG_DHCP_MASQ = self.WIN_TAP_CONTROL_CODE(7, 0) integer_ip = struct.unpack(">I", socket.inet_aton(ip))[0] integer_network = struct.pack(">I", integer_ip & ((2**int(netmask))-1)<<32-int(netmask)) integer_netmask = struct.pack(">I", ((2**int(netmask))-1)<<32-int(netmask)) settings = socket.inet_aton(ip) + integer_network + integer_netmask win32file.DeviceIoControl(self.wintun, TAP_WIN_IOCTL_CONFIG_TUN, settings, 1, None) lease = '\x10\x0e\x00\x00' settings = socket.inet_aton(ip) + integer_netmask + socket.inet_aton(ip) + lease iface_name = self.WIN_get_subinterface_name() integer_netmask = struct.pack(">I", ((2**int(netmask))-1)<<32-int(netmask)) netmask = socket.inet_ntoa(integer_netmask) # server mode if serverip == ip: ps = subprocess.Popen(["netsh", "interface", "ipv4", "set", "address", "name={0}".format(iface_name), "source=static", "address={0}".format(ip), "mask={0}".format(netmask)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr != "": common.internal_print("Cannot set IP. netsh failed: {0}".format(stdout), -1) sys.exit(-1) # client mode else: ps = subprocess.Popen(["netsh", "interface", "ipv4", "set", "address", "name={0}".format(iface_name), "source=static", "address={0}".format(ip), "mask={0}".format(netmask),"gateway={0}".format(serverip)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr != "": common.internal_print("Cannot set IP. netsh failed: {0}".format(stdout), -1) sys.exit(-1) return def win_set_mtu(self, dev, mtu): iface_name = self.WIN_get_subinterface_name() ps = subprocess.Popen(["netsh", "interface", "ipv4", "set", "subinterface", iface_name, "mtu={0}".format(mtu), "store=active"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if "Ok." not in stdout: common.internal_print("Cannot set MTU. netsh failed: {0}".format(stdout), -1) sys.exit(-1) return def win_close_tunnel(self, tun): try: win32file.CloseHandle(self.wintun) except: pass return def win_check_default_route(self): # get default gateway ps = subprocess.Popen(["route", "-4", "PRINT", "0.0.0.0"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Checking default route failed: {0}".format(stderr), -1) sys.exit(-1) # count default routes default_routes = 0 for line in stdout[0:stdout.find("Persistent Routes:")].split("\n"): if "0.0.0.0" in line: default_routes += 1 if not default_routes: common.internal_print("No default route. Please set up your routing before executing the tool", -1) sys.exit(-1) return def win_set_default_route(self, serverip, clientip, ip): self.win_check_default_route() # get default gateway lines ps = subprocess.Popen(["route", "-4", "PRINT", "0.0.0.0"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Get default route failed: {0}".format(stderr), -1) sys.exit(-1) # parse and get default gw - no persistent routes for line in stdout[0:stdout.find("Persistent Routes:")].split("\n"): if "0.0.0.0" in line: elements = line.split(" ") while "" in elements: elements.remove("") # save original default route if elements[2] != ip: self.orig_default_gw = elements[2] break ps = subprocess.Popen(["route", "DELETE", "0.0.0.0", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Delete default route failed: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "ADD", serverip, "MASK", "255.255.255.255", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Add route to server failed: {0}".format(stderr), -1) sys.exit(-1) return def win_set_intermediate_route(self, serverip, proxyip): common.internal_print("Changing route table for intermediate hop") # delete original default route ps = subprocess.Popen(["route", "DELETE", serverip, self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Delete server route failed: {0}".format(stderr), -1) sys.exit(-1) # add intermediate route ps = subprocess.Popen(["route", "ADD", proxyip, "MASK", "255.255.255.255", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Add intermediate route failed: {0}".format(stderr), -1) sys.exit(-1) return def win_restore_routes(self, serverip, clientip, ip): common.internal_print("Restoring default route") ps = subprocess.Popen(["route", "DELETE", serverip, self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Delete server route failed: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "-p", "DELETE", "0.0.0.0", ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Delete default route failed: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "ADD", "0.0.0.0", "MASK", "0.0.0.0", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Add original default route failed: {0}".format(stderr), -1) sys.exit(-1) return def win_set_split_route(self, scope, ip): iface_idx = self.WIN_get_interface_index() ps = subprocess.Popen(["route", "-p", "DELETE", "0.0.0.0", ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Delete default route failed: {0}".format(stderr), -1) sys.exit(-1) for entry in scope: ps = subprocess.Popen(["route", "ADD", entry[0], "MASK", entry[1], ip, "IF", "{0}".format(iface_idx)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Add split route to server failed: {0}".format(stderr), -1) sys.exit(-1) return def win_del_split_route(self, scope, ip): for entry in scope: ps = subprocess.Popen(["route", "DELETE", entry[0], ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Delete split route to server failed: {0}".format(stderr), -1) sys.exit(-1) return # FreeBSD ####################################################### # __init__() def freebsd_init(self): global fcntl import fcntl # @sghctoma for the win. Thanks for the help to support FreeBSD. def freebsd_tun_alloc(self, dev, flags): try: sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ifr = struct.pack('<16si', 'tun', 0) self.iface_name = fcntl.ioctl(sockfd, self.IOCTL_FREEBSD_SIOCIFCREATE2, ifr) self.iface_name = self.iface_name.rstrip("\x00") buff = array.array('c', dev+"\x00") caddr_t, _ = buff.buffer_info() ifr = struct.pack('16sP', self.iface_name, caddr_t); fcntl.ioctl(sockfd, self.IOCTL_FREEBSD_SIOCSIFNAME, ifr) tun = os.open("/dev/"+self.iface_name, os.O_RDWR | os.O_NONBLOCK) self.iface_name = dev except IOError as e: print e common.internal_print("Error: Cannot create tunnel. Is {0} in use?".format(dev), -1) sys.exit(-1) return tun def freebsd_set_ip_address(self, dev, ip, serverip, netmask): sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: #set ip, serverip and netmask ifaliasreq = struct.pack('<16sBBHI8sBBHI8sBBHI8sI', self.iface_name, 16, socket.AF_INET, 0, struct.unpack('<I', socket.inet_aton(ip))[0], '\x00'*8, 16, socket.AF_INET, 0, struct.unpack('<I', socket.inet_aton(serverip))[0], '\x00'*8, 16, socket.AF_INET, 0, struct.unpack('<I', socket.inet_aton('255.255.255.255'))[0], '\x00'*8, 0) fcntl.ioctl(sockfd, self.IOCTL_FREEBSD_SIOCAIFADDR, ifaliasreq) # get flags ifr = struct.pack('<16sh', self.iface_name, 0) flags = struct.unpack('<16sh', fcntl.ioctl(sockfd, self.IOCTL_FREEBSD_SIOCGIFFLAGS, ifr))[1] # set new flags flags = flags | self.IOCTL_FREEBSD_IFF_UP ifr = struct.pack('<16sh', self.iface_name, flags) # iface up fcntl.ioctl(sockfd, self.IOCTL_FREEBSD_SIOCSIFFLAGS, ifr) except Exception as e: common.internal_print("Something went wrong with setting up the interface.", -1) print(e) sys.exit(-1) # adding new route for forwarding packets properly. integer_ip = struct.unpack(">I", socket.inet_pton(socket.AF_INET, serverip))[0] rangeip = socket.inet_ntop(socket.AF_INET, struct.pack(">I", integer_ip & ((2**int(netmask))-1)<<32-int(netmask))) ps = subprocess.Popen(["route", "add", "-net", rangeip+"/"+netmask, serverip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding client route: {0}".format(stderr), -1) sys.exit(-1) return def freebsd_set_mtu(self, dev, mtu): s = socket.socket(type=socket.SOCK_DGRAM) try: ifr = struct.pack('<16sH', self.iface_name, mtu) + '\x00'*14 fcntl.ioctl(s, self.IOCTL_FREEBSD_SIOCSIFMTU, ifr) except Exception as e: common.internal_print("Cannot set MTU ({0}) on interface".format(mtu), -1) sys.exit(-1) return def freebsd_close_tunnel(self, tun): try: os.close(tun) except: pass s = socket.socket(type=socket.SOCK_DGRAM) try: ifr = struct.pack('<16s', self.iface_name) + '\x00'*16 fcntl.ioctl(s, self.IOCTL_FREEBSD_SIOCIFDESTROY, ifr) except Exception as e: common.internal_print("Cannot destroy interface: {0}".format(dev), -1) return def freebsd_check_default_route(self): # get default gateway ps = subprocess.Popen(["route", "-n", "get", "default"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() # is there a default gateway entry? if "route has not been found" in stderr: common.internal_print("No default route. Please set up your routing before executing the tool", -1) sys.exit(-1) return def freebsd_set_default_route(self, serverip, clientip, ip): # get default gateway ps = subprocess.Popen(["route", "-n", "get", "default"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() # is there a default gateway entry? if "route has not been found" in stderr: common.internal_print("No default route. Please set up your routing before executing the tool", -1) sys.exit(-1) self.orig_default_gw = stdout.split("gateway: ")[1].split("\n")[0] # is it an ipv4 address? if not common.is_ipv4(self.orig_default_gw): common.internal_print("Default gateway is not an IPv4 address.", -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "-net", serverip, self.orig_default_gw, "255.255.255.255"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "delete", "default"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: deleting default route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "default", ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: adding new default route: {0}".format(stderr), -1) sys.exit(-1) return def freebsd_set_intermediate_route(self, serverip, proxyip): common.internal_print("Changing route table for intermediate hop") ps = subprocess.Popen(["route", "delete", serverip+"/32", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: delete old route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "-net", proxyip, self.orig_default_gw, "255.255.255.255"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) return def freebsd_restore_routes(self, serverip, clientip, ip): common.internal_print("Restoring default route") ps = subprocess.Popen(["route", "delete", serverip+"/32", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: delete old route: {0}".format(stderr), -1) sys.exit(-1) ps = subprocess.Popen(["route", "add", "default", self.orig_default_gw], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: if not "File exists" in stderr: common.internal_print("Error: adding server route: {0}".format(stderr), -1) sys.exit(-1) return def freebsd_set_split_route(self, scope, ip): for entry in scope: ps = subprocess.Popen(["route", "add", "{0}/{1}".format(entry[0], entry[2]), ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: adding new split route: {0}".format(stderr), -1) sys.exit(-1) return def freebsd_del_split_route(self, scope, ip): return