Home ........ Blog ........ Travels ........ Software ........ Web 3D ........ LinkedIn

Tuesday, March 05, 2013

Thinking Outside the "Package" for Packets

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.

Friday, March 01, 2013

Blog about NOT Logging

A question came across a mailing list I subscribe to about limiting the syslog messages sent from a Cisco router to a syslog server. The question arose since a certain Cisco blade switch has a known bug where it reports the redundant power supply is faulty even though it doesn't have one. The message - sent every 5 minutes - was becoming quite bother to the operations folks since there were 80 such devices all reporting the erroneous error.

The asker had already found the 'logging discriminator ...' command, but couldn't apply it. A quick test in Dynamips and I had the answer for him.

The 'discriminator' option as we applied it looked for a regular expression in the syslog message body and was configured to "drop" the message (not send it to the syslog server). It worked with the following configuration:

logging discriminator NOREPORT msg-body drops "Redundant power supply faulty or in standby mode"
logging host 1.1.1.1 discriminator NOREPORT

Satisfied we had a working fix, it was time for some more investigation.

I've selectively enabled SNMP traps with the 'snmp-server enable traps XXX' commands, but I didn't know it was possible with syslog messages - I never really tried to be honest. In fact, all logging is enabled with a simple command:

logging 192.168.100.254

There are options for which facility or severity to send, but not many options for creative tuning - they're all of certain class or nothing. The 'discriminator' option seemed pretty useful. However ...

The 'discriminator NAME' doesn't work like an access-list where you can add multiple lines. You get one (1) discriminator and you get one (1) time to apply it to the syslog host. So how long can the regular expression be? Not very - as soon as I started to get fancy with the regular expression to block multiple messages, I got errors:

R1(config)#$msg-body drops "((Configured from)|(Interface         ))"
R1(config)#$msg-body drops "((Configured from)|(Interface          ))"
% unmatched ()

With the grouping parenthesis and the logical or vertical bar (pipe), I could only get a maximum of 38 characters. When I tried 39, I started getting the "unmatched" error and looking at the 'show run', my configuration line was truncated at 38 characters:

R1(config)#do sh run | i logg
logging discriminator NOREPORT msg-body drops ((Configured from)|(Interface          )

Notice the last parenthesis is left off (should be two of them). This severely limits the creativity when trying to selectively block syslog messages. There are other alternatives, like 'mnemonic' which will block an entire category of syslog messages by regular expression. So less characters to fit within the 38, but entire classes of messages dropped.

Maybe there's a better way?

 

Copyright © VinsWorld. All Rights Reserved.