Untrusted code or why exploit code should only be executed by professionals
2 Comments | Posted by Michael Thumann
In march 2012 Microsoft announced a critical vulnerability (Microsoft Security Bulletin MS12-020) related to RDP that affects all windows operating systems and allows remote code execution. A lot of security professionals are expecting almost the same impact as with MS08-067 (the conficker vulnerability) and that it will be only a matter of time, until we will spot reliable exploits in the wild. Only a few days later an exploit, working for all unpatched windows versions was released, so it seems that they were right ;-), but of course no one will run an exploit without investigating the code. So lets have a look into the exploit Code.
First we take a look into the Microsoft advisory to get some information about the vulnerability itself:
The vulnerability requires some “specifically crafted RDP packets” to be sent to the vulnerable system to trigger the problem. We should spot this trigger in the exploit:
OK, the trigger is there and we also see some shellcode, that will open a bindshell on TCP port 8888. The next step is to figure out, what the exploit is doing with this code:
The exploit code converts a lot of opcodes to the big endian format, that looks reasonable because the exploit claims to work on all affected windows versions. The last step is to verifiy, how all the stuff is sent to the vulnerable system:
We see that target IP address and the RDP port are assigned and collected from the command line, the RDP packet is generated and the “specifically crafted RDP packets” are sent to the target.Finally the shellcode is sent and we are ready to connect to a remote shell that listens on TCP port 8888. Game over ;-).
We have verified the exploit, so it’s time now to run it against some unpatched test system and see, if we can compromise all these unpatched boxes out there …
…JUST KIDDING, never ever do that and I’m not talking about the legal issues this time ;-). It is a quite common mistake by unexperienced testers to work in this way. The exploit code was gathered from an untrusted source, so it needs detailed investigations before you run it, not just a short walk-through. You have to ensure that you understand every line of code completely to avoid being targeted by yourself, even the shellcode and the trigger of the rdp example. So let’s digg a little bit deeper into this.
First we have to extract the shellcode and trigger (the opcodes) from the exploit for further analysis. I prefer a special editor for this task that has all needed functionality (and much more ) built-in. It’s a commercial tool called “010 Editor” that can be obtained here and is available as a windows and MAC OS X version.
Copy just the trigger opcodes into a dedicated text file, don’t forget to remove the double quotes. The text files should look almost like this:
Use the editors replace function to replace “\x” with “0x” for the trigger and shellcode text files. Take the shellcode as an example, how the opcodes should look now:
Mark all this hex data and copy it to the clipboard.
Choose “File-New-New Hex File” from the “010 Editors” menu to create an empty hex file.
Now choose “Edit-Paste From-Paste from Hex Text” to paste the data as hex data into the new hex file.
Save both files (trigger and shellcode) as trigger.sc and shellcode.sc
Now we would be ready to analyze the opcodes with some toolset, but I assume that all of you already spotted some very interesting stuff within the shellcode part ;-):
Yes, it looks like the shellcode doesn’t open a bindshell, it just erases parts of your hard drive on windows and your complete root partition on unix.
This is really GAME OVER, if you would have run the exploit without a detailed analysis on a productive system. This code is referenced with the following code:
def __init__(self, payload, shellcode):
super(RDPsocket, self).__init__(socket.AF_INET, socket.SOCK_STREAM)
self.payload = payload
self.table = __import__("__builtin__").__dict__
self.shellcode = shellcode
and then executed using this code:
seeker = (struct.pack(">I", 0x6576616c)
read = self.table[seeker]
return str(read(shellcode)), parsed
But in case that the shellcode wouldn’t have been so easily readable, there are more options for an easy analysis. Based on the shellcode emulation library libemu there are some tools available to find out what the shellcode is doing without reverse engineering it. SCDBG is one that runs on all unix based systems and also on windows, you can grab it here.
Let us see how SCDBG works with a short example shellcode:
scdbg -f UrlDownloadToFile.sc
Loaded 150 bytes from file UrlDownloadToFile.sc
Max Steps: 2000000
Using base offset: 0x401000
So the example shellcode downloads a malicious file and executes it, let’s have a look at our shellocde now:
scdbg -f shellcode.sc
Loaded 10d bytes from file shellcode.sc
Max Steps: 2000000
Using base offset: 0x401000
401002opcode 69 not supported
SCDBG fails to analyze the shellcode (for obvious reasons as we already know), so you can take this result as a good hint, that some stuff is hidden in the code and that you better shouldn’t run it.
So finally, let’s summarize some lessons that every serious penetration tester should be aware of:
1. Never run any untrusted code (especially exploits) without a detailed analysis.
2. Ensure that you understand every line of code and this also includes the shellcode.
3. Before using untrusted code in a real pentest, verify it in a test environment (virtual machines are a good choice for that).
4. When using exploits on customer systems be aware that you’re running it on one of the assets of your customer! Don’t do that without your customers permission!
5. Your customer trusts your professional knowledge, so it’s your responsibility to avoid damaging any of your customers systems by mistake.
So happy practicing and enjoy your week