In my last post, I fuzzed FSSO on port 8000 with Peach fuzzer to replicate the exploitable overflow discovered by Enrique Nissim of Core Security. It turns out that the DC Agent service on UDP port 8002 also has an exploitable overflow that seems to have been patched after build 143. In this post we’ll present an exploit for each of these issues.
The DCAgent protocol is a collector service that aggregates login events from other domain controllers. There is no authentication and it’s transported over UDP. We’ll ignore the obvious security flaw here — anyone can send a UDP packet and will be authenticated in FSSO as any user they choose. Instead we’ll fuzz the service and see if we can find an exploitable crash.
To get an idea of the various fields in this protocol, you can download the Peach Pit from github. It’s basically a header and trailer with a login record encapsulated within. The login record is comprised of the user’s IP address and their “DOMAIN\user” AD username.
Fuzzing this protocol on build 143 results in some fairly obvious stack overwrites:
r eax=00000000 ebx=75e89894 ecx=00000000 edx=11a9f744 esi=75ea47ad edi=75e8dbeb eip=41fffe41 esp=11a9f848 ebp=1c6fcf60 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206 41fffe41 ?? ??? kb ChildEBP RetAddr Args to Child WARNING: Frame IP not in any known module. Following frames may be wrong. 11a9f844 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41 11a9f848 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41 11a9f84c 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41 11a9f850 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41 11a9f854 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41 11a9f858 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41
This appears to be triggered by overflowing the IP address string field in the login record, resulting in direct return address overwrite. Why they wouldn’t use /GS and SafeSEH (not to mention ASLR) on a service providing authentication is beyond me.
The first step towards developing a stack buffer overflow exploit is figuring out the buffer offset to the EIP overwrite. I used Metasploit’s pattern_create/pattern_offset utilities for this purpose. This provided reliable control over EIP.
Unfortunately, there is still DEP to contend with. Normally, when DEP is combined with ASLR, it forms a fairly robust defense against stack buffer overflow exploitation. DEP is opt-out on Windows 2008 and Windows 2012 Server, so it is enabled by default. ASLR, on the other hand, is opt-in. It requires that the libraries and binary are relocatable. In the FSSO service, both collectoragent.exe and ssleay32.dll are not relocatable and no ASLR is applied to these modules.
Without ASLR, we can reuse existing pieces of the code contained in non-ASLR modules so that we are not required to directly execute code from the stack. This can be accomplished by using a ROP Chain. There are a few other issues however.
First, we have very limited buffer space since the exploit must fit in a small buffer within a single UDP packet. This means we have to keep the ROP chain quite small.
Second, at the point of EIP overwrite, our buffer has been modifed — all lower case characters have been converted to upper case. Normally this would make exploitation very difficult since it’s nearly impossible to build a ROP chain without any lower case characters (it’s tricky enough to build a ROP chain in the first place). Using the debugger, I discovered that the original buffer is still on the stack. We’ll have to adjust ESP through a phase I ROP chain in order to pivot to the unmodified buffer.
Third, we can’t use nulls since the exploitable condition is a result of a libc string handling function, probably an sprintf or strcpy (I haven’t actually checked). To make things even more fun, we also can’t use the forward slash (byte value 0x2f) because this is the delimiter between the IP Address, domain and user name. These values, 0x00 and 0x2f, are so-called bad characters.
The ROP Chain
Given the constraints above, I decided to keep things simple (or hacky, depending on your perspective). I’ll use a short chain to call WinExec. WinExec will launch a short snippet of Powershell code, which will call back to our web server and pull down a Powershell payload.
The WinExec function isn’t present in any of the non-ASLR module’s import address tables (IATs), so we’ll have to use an offset from a Kernel32 export present in the IAT. I’m using GetTimeZoneInformation for this purpose, which is probably not a great choice. The caveat with using a delta is that we must use hard-coded values. The delta between the GetTZInfo function and WinExec is Windows version specific and it often changes between service pack or even between security updates.
Strictly speaking, we also can’t use a hard-coded delta since this contains nulls, instead we must use the binary 1’s complement in the actual ROP chain and hope that it doesn’t contain any bad characters. I’ve included most of the magic values for versions of the kernel32.dll used by Windows Server 2008 R2 and 2012 R2 in the exploit. If I missed a version you’d like to test with, use this short stand-alone Ruby script to generate magic values for kernel32.dll. I admit it’s a bit of a kludge.
The PoC exploit, complete with ROP chain, is available on github here. This issue seems to be patched after build 143.
Now that we have a working ROP chain we can plug that into the FSSO exploit and it should just work. Again we’ll need to use MSF pattern_create/pattern_offset to find the EIP overwrite. For our exploit, this is at 96 bytes into the serial number field of the FSSO packet. We can simply start the ROP chain right at offset 96.
You can find the PoC exploit for FSSO on TCP port 8000 here. This issue is patched in build 237, but the ROP chain currently only works up to build 143 due to changes in the OpenSSL libraries (feel free to tweak if you need to pop build 161).
While the DCAgent overflow requires such a convoluted ROP chain due to bad characters and space limitations, the FSSO overflow does not have these limitations. It is triggered via a bad memcpy into a stack buffer, so nulls are allowed. If I have time I’ll write a new ROP chain for that exploit in order to make it more portable (or at least not Windows version specific). Until then, happy exploitation!