perlsecuritydnspacket-injection

Piping binary data to a command from a perl variable


For a homework assignment related to security, DNS cache poisoning, and Kaminsky attacks, I am building a Perl script that uses Nemesis to send packets to a local DNS server (this is all being done on a closed, host-only VMWare network). I have done everything up to automating the process of calling nemesis.

I am using Perl specifically to choose random DNS transaction IDs, add them to the DNS payload I've crafted earlier. Right now the payload isn't a DNS answer, but just a query so I can perfect the method of crafting the ID part of the payload and pushing it to nemesis with Perl.

My code currently is...

 #!/usr/bin/perl

use strict;
use warnings;

my $dnsId = int(rand(65535));

my $idString = sprintf("%x", $dnsId);

if(length($idString) == 1){$idString = "000".$idString}
elsif(length($idString) == 2){$idString = "00".$idString}
elsif(length($idString) == 3){$idString = "0".$idString}

my $payload = $idString."01000001000000000000037777770f646e737068697368696e676c61627303636f6d0000010001";

print(`echo "$payload" | nemesis udp -S10.1.3.1 -D10.1.3.100 -x53 -y33333 -P-`)

The issue that I am having is that, nemesis is reading the data as a string, which is of course, echo's doing. So what I need to do is pipe the data to nemesis as binary data, not ASCII.

I think I could use pack() to write a binary file and then use "cat /foo/bar/file | nemesis -..." to execute the payload, but this is not a optimal solution, as I don't want the extra IO time to be a factor in how many malicious DNS answers I can attempt before the (hypothetical, it will never arrive) authentic response is received.

Whats are some methods I could look into that would allow me to feed this data in binary format to nemesis?


Solution

  • First,

    print(`...`);
    

    simplifies to

    system("...");
    

    Since we no longer pipe out of nemesis, that means we can easily pipe in. (If you want to do both, use IPC::Run3 or IPC::Run.)

    my @cmd = ('nemesis',
       'udp', '-S10.1.3.1', '-D10.1.3.100',
       '-x53', '-y33333', '-P-' );
    
    open(my $pipe, '|-', @cmd)
       or die $!;
    binmode($pipe);
    print($pipe $payload);
    close($pipe);
    

    Of course, your code's $payload doesn't actually contain the payload, but the hex representation of it. You could pack 'H*' the whole thing, but I think the following is a better approach:

    my $dnsId = int(rand(65536));
    
    my $payload = pack('n H*',
        $dnsId, '010000010000'.  # 000000
        '0000000003777777'.      # 000010
        '0f646e7370686973'.      # 000020
        '68696e676c616273'.      # 000030
        '03636f6d00000100'.      # 000040
        '01');                   # 000050
    

    Note that I changed 65535 to 65536 so make it possible to actually return 65535.