More about the JailbreakMe PDF exploit

Today has been released the source code of the Jailbreakme exploit, so maybe this explanation comes a bit late. In the update of the previous post about this subject I knew that I was right about the overflow in the arguments stack when parsing the charstrings in the Type 2 format, so here is a little more info.

After decoding the stream of the object 13 we can see the following bytes (talking about this file):

cff_bytes

The selected bytes are the important ones for this exploit because the overflow occurs when parsing them. Like I mentioned, the Type 2 format is composed of operands, operators and numbers, and use the stack to push and pop values. This stack has a maximum size of 48 elements. We can understand better the meaning of these bytes with this tips:

 

  • The 0xFF byte means that the next 4 bytes are interpreted as a 32-bit two’s-complement number that will be pushed into the stack.
  • 0x0C17 is the random operator that returns a pseudo random number greater than zero and less than or equal to one. This operator doesn't take any argument from the stack.
  • The operator 0x0C04 is an or that takes two arguments from the stack and puts a 0 is both arguments are zero and a 1 otherwise.
  • 0x0C0D is the index operator, which takes an argument num from the stack and puts the argument in the position num of the stack on the top of it.
  • The drop operator is composed by the bytes 0x0C12 and removes the stack top element.

 

Then, from the stack modification perspective we can separate the bytes in 4 "instructions" set:

 

  • 0xFFXXXXXXXX (45*5 bytes): we put XXXXXXXX into the stack. There is a limit here in the amount of this type of "instruction" because of the stack arguments size, that is checked in this case. So the maximum number that we can push is 45.
  • 0x0C170C170C040C1D (20*8 bytes): it pushes the stack element in position 1 (one position after the top element) into the stack. The position is always 1 because the random elements pushed are always non-zero. So in this case will be 0x00C00000.
  • 0x0C170C1D (170*4 bytes): we push the element in the position specified by the random number into the stack. The random number always has 16 bits and after a 16-bits movement to the right it becomes 0, so the pushed value will be always the top of the stack, 0x00C00000.
  • 0x0C1D0C12 (42*4 bytes): it pushes the stack element in the position C0 into the stack and removes it. The first "instruction" of this type will push F00DF00D (the 4th last number pushed with FF), and the next "instructions" will write into the stack the 41 previous numbers.

 

These "instructions", except the FF one, don't check if the stack is full before pushing values, so after parsing and executing them the stack state will be similar to this image, being 48*4 the maximum size for the stack:

After the last 0x0C12 an FF "instruction" is executed, checking the stack size and returning from the function with an error code. The successful exploitation will depend on the program and the architecture where the PDF file is parsed. As you know, this affects to Apple products (now patched) and to the Foxit Reader. In the case of the latter we can exploit it easily through a SEH overflow, putting the shellcode into the bytes pushed by the FF "instructions". Here we'll have more than 100 bytes for it, depending on the SEH position. Anyway we can jump from here to the rest of the decoded stream and really do what we want.

heap address

hi Jose,

Very good explanation, but I have some quesitons regarding it.

From the source code of the jailbreakme, it is stated that the starting address of the heap is 0xa24 and the start address to overwrite is 0xd28. The difference of the two address is 0xc0 words.

After parsing the cff fonts, and the stack has been built as you stated, which address does the code return to? is it 0xC0? The heap address being overwritten? what code is avaiable there?

Thanks,
Miki

heap address

Hi Miki,

firstly I have to remark that when I talk about the stack in this article I'm talking about the Type2 arguments stack, the one with a limit of 48 elements. Like you said, in the Jailbreakme source code he talks about heap addresses so I suppose that the exploiting will occur there, I've not probed that because I haven't got any Apple products :( All my tests have been made while trying to exploit Foxit Reader but I think the exploitation in Apple products will be very similar to it. Basically this is the theory on how to do it, after that some practice is needed ;) Anyway I'll try to answer you:

- Which address does the code return to? is it 0xC0?
Really I do not understand very well the question. Do you mean which address the parsing function return to? which code? after the last FF instruction is executed an error will be generated. After that the exploitation depends on the platform and application. The 0xC0 is the length between the first address we can overwrite and the address where the first word we want to copy is located.

- The heap address being overwritten? what code is avaiable there?
I really don't know which is the exact heap address being overwritten, because I've not tested any Apple product but reading the source code it should be 0xZZZZZd28, shouldn't it? The code written there will be the content of the first FF instructions till the 0xF00DF00D.

I hope I can help you, if not ask me again and I'll try to do it better ;)

Cheers!

Heap address

Hi Jose,

Thank you for the reply and you are really helpful :)
Maybe it is clearer for you to understand my question when I describe the whole process.

As you have explained, the free type 2 font engine will try to parse the CFF font embedded inside the pdf file. When it sees those FF, 0x0c17, 0x0c1d commands, it will fill up the stack accordingly as you have plotted previously. The size will be 41+1+3+20+128+41+1=235 words. Then an 0xFF command is executed and it will generate an error.

The question is: how does the payload get to run?

When we open the source code, we can see that the first 41 FF commands is generated from stage1.txt. Stage1.txt is generated by goo/zero.py. In the pdf file, stage2.txt (utilizing the integer overflow of IOSurface) is also embedded, including the installui.dylib and install.dylib.

In your foxit reader example, the implementation is very similar. The shell code is supposed to the code to invoke the calculator, right? you pushed similar number of elements into the stack. In the ff instructions, you include some zero bytes, the shell code, 0xFF90908aeb, a return address and 3 jump words. So how your shell code get to run then?

Thank you.

Best Regards,
Miki

Heap address

Hi Miki,

this is answered here.

Cheers!

more on heapaddr

Hi Jose,

I further checked stage1.txt (i.e. those FF commands), and found that it basically remap address 0x1024 to address 0x9100000. Then it copies the stage2.txt (the main payload) to 0x9100000 and jumps to it by setting the programming counter and stack pointer.

thanks.

Miki

instruction 0x0c1d0c12

Hi Jose,

In your description aobut 0x0C1D0C12, you mentioned that "•0x0C1D0C12 (42*4 bytes): it pushes the stack element in the position C0 into the stack and removes it. The first "instruction" of this type will push F00DF00D (the 4th last number pushed with FF), and the next "instructions" will write into the stack the 41 previous numbers."

Why is it so?
0x0C1D is index operator, and it will retrieve an element from the stack and push it to stack. Before this instruction, the top element of the stack is 0x00C00000. So the operator will retrieve the element at position 0xC0 from the top of the stack. The element happens to be 0xF00DF00D, so 0xF00DF00D will be added to the stack. Then 0x0C12 will remove the top element 0xC0 from the Type 2 argument stack.

How about the next 0x0C1D0C12 instruction? If the element at stack top is 0xF00DF00D, which elment will be pushed to stack and which one will be removed? How does the entire shell code get copied?

I feel very confused here...

Thank you.

Best Regards,
Miki

instruction 0x0c1d0c12

Hi Miki,

after the index instruction 0xF00DF00D is placed on the top of the stack, the stack pointer is pointing to it, so when the 0x0C12 is executed it removes the element on the top (0xF00DF00D) by decrementing the stack pointer. This way we'll have C0 again on the top...