OSX ROP Exploit – EvoCam Case Study

Introduction

This post follows on from my previous OS X exploit tutorial which demonstrated finding a buffer overflow in an OS X application and developing a working exploit for it. The technique used in that tutorial only worked on the previous incarnation of Apple’s OS X operating system known as Leopard (10.5.x).

I stupidly mentioned at the end of my previous post that future OS X exploit would likely rely on ROP based techniques in order to bypass non-executable memory protection and achieve code execution. I was then challenged by then Offensive Security team to produce a follow up post, so the obvious next port of call was to get my previous EvoCam exploit working on Snow Leopard.

EvoCam ret2libc Version

My pervious exploit which only works on OS X 10.5.x (Leopard) used Dino Dai Zovi’s Exec Payload From Heap technique. This no longer works in Snow Leopard as the setjmp() function is no longer available in dyld. So I set about looking through dyld for a function I could use to copy a payload from Non-executable stack to somewhere we can execute it. We can use the nm command to dump the symbol table from the dyld library.

A quick examination of the output shows memcpy() as a possible candidate. After some testing I managed to get an exploit working on Snow Leopard using memcpy but with one small problem. One of the parameters passed to memcpy is size, and this was giving me issues with null characters, because my payload was only a few hundred bytes long. I managed to fudge a fix in gdb to get things to work but this obviously wasn’t going to be ideal for a real exploit scenario.

The next function I tried was strcpy() this doesn’t require a size variable as the end of the string is defined by a null byte. Going back to my original exploit and retooling we end up with the following python code:

So we’ve hit the break point for strcpy() at 0x8fe2db10, if we check the stack we can see the destination parameter (0x8fe66448 writeable memory in dyld) and our source parameter (0xb0102d62 which points to some nops and our payload on the stack). Let’s now set a breakpoint at the end of strcpy() and continue execution.

So at the end of the call to strcpy if we check the memory address of our destination parameter we can see that our payload has indeed been copied over, and it has bypassed the NX protection of the stack and is now executable! If we continue execution we receive a bind shell on our target host.

Okay so we now have our EvoCam exploit working on the latest version of OS X. Mission accomplished, well not quite…. The problem with this method is that we have hard coded the source parameter which is a pointer to our payload on the stack. This may not prove very reliable, the ideal method would be to calculate this address at runtime and pass it to strcpy. How hard can it be?

So how are we going to craft the parameter we require for our call to strcpy? It should be pretty easy to do this is x86 assembly, but because of the non-executable stack we would have to copy it to somewhere executable first, which is kind of where our problems started in the first place!

Can we use the ret2libc technique to do this for us? Probably not, unless I missed something I didn’t spot a function in dyld called _fudgemyexploitparamter()!

Rop-A-Bye Baby

We can however make use of a technique known as Return-Orientated-Programming or ROP for short. This is an adaption of the ret2libc technique whereby instead of making calls to entire functions we just use sections of code from these libraries in order to achieve what we want. The one caveat is that the assembly instructions we use must end in a RETN statement in order to return us back to the stack and the next ROP instruction.

For a great explanation of ROP and links to many other papers please check out Peter Van Eeckoutte’s article Chaining DEP with ROP – the Rubik’s[TM] Cube, here. I use Peter’s “more or less generic” ROP structure in developing my exploit.

So what do we need to do? We need to craft a parameter for strcpy() which points to our payload which is on the stack, we then need to write this parameter into the correct position on the stack so it gets picked up by strcpy when it is executed. Once strcpy has done it’s job we need to redirect execution to our newly copied payload on the heap.

The best thing to do is break these things down and take them one by one. However before we start we need to get our building blocks ready for our ROP structure, so Lego[TM].

Finding our Gadgets

So which libraries are we going search to find our ROP gadgets? Well due to ASLR on OS X the obvious choice is /usr/lib/dyld which is always loaded at a known fixed address. We can also use an area of memory known as the commpage which contains optimised library functions. The commpage is mapped into memory from the kernel so we can use gdb to dump its contents out to a file:

I originally tried to use the msfmachscan tool that is part of the Metasploit framework, but it was giving strange offsets. So in the end I used hexdump to dump the text segment from /usr/lib/dyld. OS X supports Univeral binaries which contains different flavours or code for difference architectures.

We can now scan these dumps for usable ROP instructions. We can identify those ending is a simple RETN instruction by searching for the hex C3. We Can create a simple script to do this for us. One of the features of ROP on the i386 architecture, which has no set word alignment in assembly code, is that we can split instructions to get different opcodes than originally attended. The best way to deal with this is to find our RETN instruction and then work backwards byte by byte and see what instructions we end up with.

In order to translate the resulting opcodes into assembly mnemonics I used the ndisasm disassembler which comes with the nasm assembler. After searching for ROP gadgets in both dyld and commpage I ended up with about 400 possible gadgets, but not all of these where useful. I had already filtered out a large number of ROP chains which contained bad instructions which would cause the flow of our exploit to change, such as JMP and CALL instructions.

The Puzzle Begins

We now have a list of potential ROP instructions we can use in order to complete the steps required to get our exploit to work as intended. So hopefully a few quick greps of our rop.txt file and we should be home and dry! The first problem we have to deal with is the EvoCam exploit required us to place a writable memory address and a set position in our payload, this was after the 4 bytes which overwrite the EIP register. So as we have one instruction before this writable address on the stack we look for a “POP, RETURN” instruction which will allow us to jump over it and for execution to continue. So after a quick search we start our ROP gadget like this:

So with that out of the way we can get back to our task of creating a pointer to our shellcode on the stack, which should sit and the end of all of our ROP gadgets. So if we save a copy of ESP we should then be able to increase it’s value to point the the corect place, and we can use a nop sled if we don’t want to be too accurate in our calculations. So we search through our output for available PUSH ESP instructions:

We have three possible options, but none of them are a simple “POP ESP, RET”. This is one of the problems we will often face, so it is a case of choosing the combination of commands which will do what we want and cause the least damage to the stack and any registers we are interested in. After a bit of trial and error I chose to use the last of these three options. However we need to prefix this instruction with a couple more instructions to set things up correctly.

Following the PUSH ESP command it ands part of the EAX register, this shouldn’t be a problem for us as this currently just contains some rubbish used to overflow the buffer. However the next command is moving the value in EDX to the memory location pointed to by EAX+0x28. In order for this to work we need to place a writeable memory address into EAX. We can do this with a POP EAX command followed on the stack by the value to pop into it. Now the previous AND AL,0x4 comes back into play, but luckily this doesn’t actually cause us any problems. Next we have a couple more MOV commands but luckily these don’t cause us any problems, and actually also copy the value of ESP into EDX which will come in handy later on. So we end up with the following ROP gadget:

The next thing we want to do is place our call to strcpy onto the stack along with it’s parameters. We currently know the destination parameter which will be the writeable memory location we have chosen, however we will have to leave a place holder for the source address which we need to calculate later on. However we don’t want to make our call to strcpy yet until we have calculated this source address so we prefix is with a short jmp command to move down the stack and get to our ROP gadgets to calculate the source address for strcpy and write it to the correct location on the stack to replace our placeholder. We can find an instruction which adds 22 bytes to esp so our next section of ROP code is:

Our placeholder for the strcpy parameter is 16 bytes after the value of ESP what was saved into both EAX and EDX. We need to find a way to increase one of these two registers by 16 bytes. We do have an INC EAX available but is there a better way? Well we do have some add instructions available but the only one which might be of use is to add ECX to EAX. We don’t have a POP ECX instruction available but we do have a MOV ECX,[ESP+0x4] which might work. Our next problem is one we saw earlier with the memcpy function arguments, if we include a small number such as 16 in our buffer it’s going to have NULL bytes in the upper two bytes, but we could use a large number such as 0xffffffff and add something to it to get what we want. After a search I don’t find many useful instructions to add numbers to ECX but I do have an INC ECX and an ADD ECX,ECX. We can combine these together to make ECX contain 0x10 (16):

Unfortunately following the MOV command above there are two arithmetic operations which damage the value we have previously stored in EAX, fortunately we can easily replace it with the following ROP gadget and then add the value of ECX to it, which means EAX should now point to our placeholder:

At this point EAX contains a pointer to the strcpy parameter placeholder and EDX contains our originally saved stack pointer. At this point I’m going to swap these two around so we have the original pointer in EAX so I can carry on increasing ECX to be an offset from our saved stack pointer which will point to our shellcode. This gives rise to the following set of instructions:

Great so we now have calculated our two required values. Next we need to replace the placeholder with the pointer to our shellcode on the stack. To do this we need a MOV [REG], EDX (or EAX) type command. Luckily we find an ideal candidate in the form of MOV [EAX],EDX # POP EAX # RET. (Did I say luckily? It actually took a lot of false starts and dead ends before getting lucky on this one!). In order for this command to work we need to swap around EAX and EDX so that EDX contains the value we want to write(the pointer to our shellcode) and EAX contains the address of the placeholder.

So we are nearly there, our strcpy function is all set up correctly now, we just need to move execution back up the stack to run it. We originally saved a copy of our stack pointer in both EAX and EDX but we have modified both of these. However if we exchange EAX and EDX once more and subtract the value still in ECX we should be back to where we started:

Now we could do with a PUSH EAX, POP ESP chain to do this for us, but it isn’t going to be that simple. The next best option I found was MOV ESP,EBP, POP EBP, RET. So if we can get EAX into EBP we might have a shot. I managed to find a XCHG EAX,EBP, INC EBP, RET combination, which might work if we managed to undo the INC EBP. Our final section of ROP code is:

Execution will now move up to our strcpy function call which now has the correct parameters to copy our shellcode from the stack to our chosen writable memory segment. Our last job is to add a ROP instruction to the end of the strcpy which directs execution to our shellcode, we also need to fix up some padding so that things align with the value we added to ESP earlier to jump over the strcpy routine. Our fixed up code is:

So if we launch our exploit we can see we do indeed get shell and it is a system running the latest version of OS X.

About the author

Paul Harrington (a.k.a. d1dn0t) has been working in IT Security for 10 years, working as an independent contractor for several well known global companies. When he gets a chance to escape from corporate policies and procedures he enjoys Vulnerability Development and Penetration Testing.

Paul is 0×26 years old and currently lives in the North West of England. You can reach him via didnot [at] me {dot} com.

Thanks to

* Dino Dai Zovi and Charlie Miller for writting the excellent The Mac Hacker’s Handbook.
* Peter Van Eeckhoutte for his excellent ROP tutorial.
* Thank Muts and Ryujin for their obscure form of encouragement.