Thursday, March 21, 2013

Introduction.This is the second part of the article about binary instrumentation for exploit analysis purposes and this time we will discuss a real pdf exploit: a Stack-based buffer overflow in CoolType.dll (CVE-2010-2883). You can retrieve it from the metasploit module exploit/windows/fileformat/adobe_cooltype_sing .In order to bypass DEP, this exploit makes use of Heap Spraying to run its ROP shellcode. On the other hand, our goal is to come closer to the point where the vulnerability occurs, so one clever thing to do is to use Pintool to detect the ROP itself.

To do that, we can simply check if the instruction executed after a RET is located after a CALL, but be aware that performing this test alone could lead to false positives. A better test would be to control wether this check works for three times in a row, but this gives rise to some Pintool's problems that we will discuss later.Another method to detect ROP is to control the ESP register and look for the "0c0c0c0c" value, but inspecting the register with Pin is very slow and will degrade the performance of your Pintool. So we won't implement this one.Finally, one last check is to log the "pop ESP" instruction, that is a common ROP gadget employed right before the ROP shellcode itself.Detecting the ROP with a Pintool.Here is the function to detect the ROP:

in the "DetectEip()" function (where specified by the comments).Now a brief description of what the code does. Basically, this Pintool looks for two opcodes: the one corresponding to RET (Pin code 557) and the one corresponding to POP (Pin code 486).If a RET is encountered, the Pintool follows it and checks if the previous opcode is a CALL, looking for the E8 opcode or the ones provided in the "OpcCheck[].Opcode" array (the list may not be complete, but while testing it was reasonably accurate). In case it's not, it notifies the user with the message: "*Address* RETurned here, but not after call".If a POP is encountered, it checks if it is a "POP ESP" and, in case it is, it notifies the user by printing "*Adress* POP ESP DETECTED!!" and dumps the last executed instructions on file.That's it. You are finally ready to compile the Pintool and run it within Adobe Acrobat Reader to analyse the PDF exploit.Analyzing the outputHere is an excerpt from the output produced by the Pintool:Exception handler address: 7C91EAEC Starting PintoolLoading module C:\Programmi\Adobe\Reader 9.0\Reader\AcroRd32.exe Main exe Base: 00400000 End: 00453FFFLoading module C:\WINDOWS\system32\kernel32.dll Module Base: 7C800000 Module end: 7C8FEFFF Loading module C:\WINDOWS\system32\ntdll.dll Module Base: 7C910000 Module end: 7C9C5FFF Starting thread 0...0D6D8192 RETurned here, but not after call02D43FA5 RETurned here, but not after call22326DB0 RETurned here, but not after call5B18174F RETurned here, but not after call08171CF0 RETurned here, but not after call08171D47 RETurned here, but not after call06066EED RETurned here, but not after call0633DE6B RETurned here, but not after call...4A82A714 RETurned here, but not after call4A82A714 POP ESP DETECTED!!Dumping list of previously executed EIPs 0803DDC60803DDCA0803DDCC0803DDCD...0808B3040808B3050808B3070808B3084A80CB384A80CB3E4A80CB3F4A82A714

From the log above we can see all the modules being loaded and threads being created. Then, we notice some false positives: these are legitimate RETs, which don't return to an instruction after a CALL.Finally, we get to the part where both checks are detected: the code returns to an instruction not located after a call and a "POP ESP" instruction is executed.In particular, the last logged EIPs correspond to following ROP gadgets: 4A80CB38 81C5 94070000 ADD EBP,794 4A80CB3E C9 LEAVE 4A80CB3F C3 RETN 4A82A714 5C POP ESP(4A82A715 C3 RETN)So we have located where the exploit occurs (i.e. the address "0808B308"): not bad!Note that the last instruction reported here (the RETN between parentheses) is not logged by the Pintool because a crash happened right after its execution... but......Why???

As I said before, this exploit makes use of Heap Spraying. In particular, we can see it by debugging Adobe Acrobat Reader while Pin is not instrumenting it and setting a breakpoint on address "0808B308". Now, if we open the PDF exploit and leave the debugger running, we can inspect the memory when the code hits the breakpoint:

This is exactly what we were expecting: you can notice the ROP shellcode at "0c0c0c0c" and the Heap Spraying all around. On the other hand, if we debug the Adobe Acrobat Reader while Pin is instrumenting it, we obtain:

So... no ROP, nor Heap Spraying... but the blocks of memory are still allocated. Who has allocated them?

To get the answer we need to look inside the code window:

... It's Pin itself!

Pin allocates a lot of memory to perform binary instrumentation, occupying also the addresses usually employed by the Heap Spraying. This means that when the ROP shellcode is executed, it's not located where it is supposed to be and this will result in Adobe Acrobat Reader crashing.

Another problem I ran into, is that even when I modified the Pintool in order to force the exploit to work with the shellcode that was placed at a different address than 0x0C0C0C0C, the exploit still crashed.

This time I could see it run all the ROP shellcode, which allocates a block of executable memory, copies itself to it and then jumps to it.

I haven't investigated the problem yet, but it seems that the instrumented shellcode is placed in an area that is read only, therefore the self decryption failed when writing the decrypted bytes back to the shellcode memory.

Sunday, March 10, 2013

This article is about binary instrumentation over various exploit scenarios. In particular, we are going to use Pin, a software developed by Intel, to show how this approach can help with the analysis.Pin is employed to create dynamic program analysis tools, the so called "Pintools". Once executed, a Pintool acts almost like a virtual machine that runs the code from a target executable image and rebuilds it by adding the code you need to perform your own analysis. For example, you can: install a callback that is invoked every time a single instruction is executed; inspect registers; alter the context and so on.Note: I've tested the whole work using Windows XP 32 bit and Visual Studio 2010.How to compile and execute a Pintool.The simplest way to compile a Pintool is to use the Visual Studio project provided by Intel, located in the Pin folder at: \source\tools\MyPinTool .To run it, simply type: pin -t <your_pintool.dll> -- <application_path>.In this way your Pintool will be executed within the application you want to test.

How to code a Pintool: a (very) short description.

A Pintool begins with a standard initialization of the Pin engine by using the "PIN_Init()" function; then, you need to register the callbacks for the events you want to handle. For instance, you can use:

"INS_AddInstrumentFunction()" to register a callback that is invoked at every executed instruction;

"IMG_AddInstrumentFunction()" to register a callback that notifies you every time an executable module is loaded;

"PIN_AddThreadStartFunction()" and "PIN_AddThreadFiniFunction()" to handle thread creation and ending.

In particular, if you register a callback with "INS_AddInstrumentFunction()", you can then use the "INS_InsertCall()" function from it and register other callbacks.These callbacks have a special property: they can be invoked before or after an instruction is executed. Also, you can pass to them any kind of data, including the value of specific registers (the instruction pointer, for instance), memory addresses and so on.Finally, you'll have to use "PIN_AddFiniFunction()" to register the callback that is invoked when the application quits.Once all the callbacks are registered, you can start the instrumented program by calling "PIN_StartProgram()".Your Pintool can filter specific conditions with an incredibly accurate resolution, but bear in mind that the performances may degrade badly depending on what kind of actions you choose to do.As an example, let's consider again the "INS_AddInstrumentFunction()", and suppose that we are going to register a callback that logs every executed instruction to a file: if you are distracted, you might generate a file I/O for every single instruction, which is very inefficient. Another operation that will reduce your Pintool's performances, if called frequently, is the disassembler functionality.So be careful: your instrumented application can run almost at realtime speed if your Pintool is well written, but a bad implementation may slow down your application up to the point where it will take minutes to run.

A basic Pintool.

Here is a very basic Pintool to which we will add more specific functions later.

It basically logs to a file: the address of each instruction being executed; all the exceptions occurred; the name of each module being loaded, including the base and the end address.I have also put a comment in the "DetectEip()" function, to specify where you can call the functions we will add later.First exploit scenario: stack overflow.As a first case study, we are going to consider a specially crafted sample:

Before compiling and linking it (I used Visual Studio 10), be sure to disable all the security options (stack canaries, DEP, ASLR) and to set the Base Address to 0x41410000.I know it might sound a little unreal, and in fact... it is! But don't worry, as I said before, this is just the simplest example that crossed my mind and we are going to use it as a first test. Anyway the methodology I'm proposing is very effective and we will see a real case study later.First, we need to "exploit" this little test: I'll be quick. We can open the executable with Ollydbg and debug it until we find the "getchar" function, that grabs an input string. Then, we enter the following (in my case at least, you should check the parameters explained later if you want to be 100% sure!): "123456789abcAAAA 0AABBBBBBBBBBBBBBBBBBB" (remove the " ").

What's the meaning of it? We are going to fill all the 12 required bytes, and because of the lack of control over the size of the input, we also type:

"AAAA", that is the padding added by the compiler;

" 0AA", that corresponds to the 0x41413020 address (= "AA0 ", because of the endianness) where the "JMP ESP" instruction (= "0xFF 0xE4" as an opcode) is located --- this will overwrite the return address of the "main" function;

a bunch of "B", that corresponds to the "INC EDX" instruction --- this is where you will usually put the shellcode, but as a test every valid instruction will be fine!

Now that you have tested that the string I provided works also in your case, or you have built your own valid string, we are ready to analyze our first exploit scenario: a simple stack overflow. How can we detect that?The most natural idea is to perform a check over EIP to see whether its value corresponds to a non-executable area (the stack in this case).The Pintool maintains two variables containing the base and end address of the module being executed.If the value of the EIP isn't in the range specified by these two addresses, Pintool accesses the modules list maintained by Pin, looking for a new executable module in which the value of EIP resides (for instance, after an API call). When such a module is found, the variables containing the base and end address are updated (making it the current module).If the value of EIP isn't located within any of the modules, the Pintool reports it as suspicious and logs the list of the last 1000 executed values of EIP.Here is the code to do that:

it jumps to the overwritten return address, that is the address "0x41413020", where a "JMP ESP" is located;

Pintool successfully detects that we are trying to execute code within a non executable module (that is the "0x0012FF84" address, belonging to the stack).

Conclusions

This was an introductory article on binary instrumentation for exploit analysis purposes and I really hope you liked it! See you for the second part in a few days, where I will discuss another scenario: a real pdf exploit, that makes use of ROP and Heap Spraying.