DNS Despoofing

06 Aug 2010


These days I was playing with some well-known tools to perform DNS spoofing in both *nix and windows environment: Dnsspoof (part of the dsniff suite) and WinDNSSpoof, of which I learned only recently.

I was analyzing the generation of DNS replies when I discovered something interesting. Can you spot where the problem is?

The DNS request:

Forged DNS request

The DNS response:

Forged DNS response

Is not difficult to see that, if a request is artificially forged to include arbitrary trailing bytes at the end, these bytes are replicated in the replies of simple DNS spoofers.

These tools, since the response must contain a copy of the hostname queried, simply copy that part of the request using memcpy(), without fully parsing the header.

Thanks to this little oversight, it's easy to find out if we are victims of a DNS attack that use one of these stateless tools. This may not be indispensable, for example, if the attacker is also conducting a MITM attack based on the ARP protocol: we just need to monitor the incoming ARP packets to detect anomalies.

This trick comes in handy when the attacker is located on the router, or on an intermediate host of our network. In this case he is already "in the middle", and just need to monitor the traffic that traverse his machine to capture DNS requests. We may thus detect him, only if we are able to differentiate its behaviour from the behaviour of a valid DNS server.

I wrote a script, quite easy to use, to test if a naive DNS spoofer is intercepting your requests. It’s based on the hping3 TCL shell, which provides a little framework for packet forging.

NOTE: I also considered scapy, but hping requires fewer dependencies and is more portable, and I always prefer not to add extra burdens to users or distributions maintainers. Being a maintainer can be a very frustrating experience when you have to fight against badly organized software. Remember to offer them a beer if you meet the maintainers of the distribution you use at a convention...

Let’s now see how to use the tool:

root@backtrack-base# hping3 exec dns_despoof.tcl
DNS Despoofer - crossbower - 2010
Usage: hping3 exec dns_despoof.tcl <server> <interface> (<action:search|crash>)

root@backtrack-base# hping3 exec dns_despoof.tcl 192.168.56.101 vboxnet0 search
SPOOFER DETECTED!

We just launched dns_despoof in search mode.

The IP address 192.168.56.101 is the IP of the DNS server you normally use (e.g. 8.8.8.8/8.8.4.4 for google's nameservers). You must also provide the interface where to inject the packet. I was running dnsspoof on a virtual machine so I specified the interface vboxnet0.

After running, the script correctly detected a spoofer.

There is also another mode available: crash. This mode injects a malformed packet on the network, in which the hostname requested is not terminated by a null character.

The tools that fail to properly parse the request will exhibit a nice segfault, as the case of WinDNSSpoof.

Dnsspoof, instead, uses the function dh_expand, contained in resolv.h, which is able to detect this type of error. A little snippet of dnsspoof:

if ((i = dn_expand((u_char *)dns, end, p, name, sizeof(name))) < 0)
 return;

The output of the tool when started in crash mode is the following:

root@backtrack-base# hping3 exec dns_despoof.tcl 192.168.56.101 vboxnet0 crash
Bullet fired... Try again to search for spoofers:
1) No responses: the spoofer has probably crashed (windnsspoof).
2) Responses: it's a well written spoofer (dnsspoof).

A subsequent test in search mode will allow you to determine whether the tool crashed or not. This may also be used as a sort of fingerprint.

Here’s the source code of the tool. It’s only able to make a request with the name “google.com”, but can be easily adapted:

#
# DNS Despoofer
# crossbower - 2010
#
# Usage:
# hping3 exec dns_despoof.tcl <server> <interface> (<action:search|crash>)
#

#
# Search spoofers
#
proc search_spoofers { server interface } {

    # prepare and send DNS probe
    set probe {ip(daddr=192.168.0.1,ttl=64)+udp(dport=53,sport=44556)+data(str=\2f\69\01\00\00\01\00\00\00\00\00\00\06\67\6f\6f\67\6c\65\03\63\6f\6d\00\00\01\00\01\70\69\7a\7a\61)};

    set probe [hping setfield ip daddr $server $probe];

    # send probe
    hping send $probe;

    # sniff loop
    while { 1 } {

        # sniff a single packet
        set p [lindex [hping recv $interface] 0];

        # is it the DNS response?
        if { [hping getfield ip proto $p]  != 17      ||
             [hping getfield ip saddr $p]  != $server ||
             [hping getfield udp sport $p] != 53      ||
             [hping getfield udp dport $p] != 44556 } {
                continue
        }

        # get data
        set res_data [hping getfield data str $p];
        set result [string match "*pizza*" $res_data];

        if { $result == 0 } {
            puts "No spoofer detected...";
        } else {
            puts "SPOOFER DETECTED!";
        }

        break;
    }
}

#
# Crash spoofers
#
proc crash_spoofers { server interface } {

    # prepare and send DNS probe
    set probe {ip(daddr=192.168.0.1,ttl=64)+udp(dport=53,sport=44556)+data(str=\2f\69\01\00\00\01\00\00\00\00\00\00\06\67\6f\6f\67\6c\65\03\63\6f\6d\01\00\01\00\01\70\69\7a\7a\61)};

    set probe [hping setfield ip daddr $server $probe];

    # send probe
    hping send $probe;

    puts "Bullet fired... Try again to search for spoofers:\n1) No responses: the spoofer is probably crashed (windnsspoof).\n2) Responses: it's a well written spoofer (dnsspoof).";
}

#
# Usage
#
proc usage {} {
    puts "DNS Despoofer - crossbower - 2010\nUsage:\n  hping3 exec dns_despoof.tcl <server> <interface> (<action:search|crash>)";
    exit 250;
}

#
# Main
#

# get dns server
set server [lindex $argv 0];
set interface [lindex $argv 1];
set action [lindex $argv 2];

# check args
if { $server == "" || $interface == "" } { usage }

# simple trick to initialize libpcap
set p [lindex [hping recv $interface 1] 0];

# check action
if { $action == "" || $action == "search" } {
    search_spoofers $server $interface;
} elseif { $action == "crash" } {
    crash_spoofers $server $interface;
} else {
    usage;
}

exit 0;
      

The same code on pastebin: http://pastebin.com/78y4snFk.