Question

I have been playing around with intentionally vulnerable c programs using strcpy, sprint, gets, etc. These all behaved as expected when running on linux but something strange is happening on my OS X machine. Here is the program I have wrote:

#include <stdio.h>
int main(int argc, char **argv) {
    char buffer[64];
    strcpy(buffer, argv[1]);
    printf("buffer: %s\n", buffer);
    return 0;
}

I ran it like this:

(gdb) run test
Starting program: /Users/****/test2 test
buffer: test
[Inferior 1 (process 5290) exited normally]
(gdb) run `python -c 'print "A"*64'`
Starting program: /Users/****/test2 `python -c 'print "A"*64'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5291) exited normally]
(gdb) run `python -c 'print "A"*70'`
Starting program: /Users/****/test2 `python -c 'print "A"*70'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5294) exited normally]
(gdb) run `python -c 'print "A"*80'`
Starting program: /Users/****/test2 `python -c 'print "A"*80'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5297) exited normally]
(gdb) run `python -c 'print "A"*100'`
Starting program: /Users/****/test2 `python -c 'print "A"*100'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGABRT, Aborted.
0x00007fff8eef3866 in ?? ()
(gdb) 

First of all, I had expected 80 bytes to be enough to crash it. Second I expected to see 0x4141414141414141 instead of 0x00007fff8eef3866 due to the fact that I just attempted to overwrite some memory with a bunch of A's. Where is the other data coming from? Also, why did the program get SIGABRT? Why is there no seg fault?

Here is the assembly:

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  4, 0x90
_main:                                  ## @main
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp2:
    .cfi_def_cfa_offset 16
Ltmp3:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp4:
    .cfi_def_cfa_register %rbp
    subq    $112, %rsp
    movq    ___stack_chk_guard@GOTPCREL(%rip), %rax
    movq    (%rax), %rax
    movq    %rax, -8(%rbp)
    leaq    -96(%rbp), %rax
    movl    $0, -12(%rbp)
    movl    %edi, -16(%rbp)
    movq    %rsi, -24(%rbp)
    movq    -24(%rbp), %rsi
    movq    8(%rsi), %rsi
    movq    %rax, %rdi
    callq   _strcpy
    leaq    L_.str(%rip), %rdi
    leaq    -96(%rbp), %rsi
    movq    %rax, -104(%rbp)        ## 8-byte Spill
    movb    $0, %al
    callq   _printf
    movq    ___stack_chk_guard@GOTPCREL(%rip), %rsi
    movq    (%rsi), %rsi
    movq    -8(%rbp), %rdi
    cmpq    %rdi, %rsi
    movl    %eax, -108(%rbp)        ## 4-byte Spill
    jne LBB0_2
## BB#1:                                ## %SP_return
    movl    $0, %eax
    addq    $112, %rsp
    popq    %rbp
    ret
LBB0_2:                                 ## %CallStackCheckFailBlk
    callq   ___stack_chk_fail
    .cfi_endproc

    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz   "buffer: %s\n"


.subsections_via_symbols

[UPDATE]

Actually, none of the registers seem to be getting overwritten, yet it looks like they should be:

Starting program: /Users/henrypitcairn/test2 python -c 'print "A"*128'

Breakpoint 1, 0x0000000100000ed4 in main ()
(gdb) c
Continuing.
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGABRT, Aborted.
0x00007fff8eef3866 in ?? ()
(gdb) info registers
rax            0x0  0
rbx            0x7fff77921310   140735199449872
rcx            0x7fff5fbff9f8   140734799804920
rdx            0x0  0
rsi            0x6  6
rdi            0xc07    3079
rbp            0x7fff5fbffa20   0x7fff5fbffa20
rsp            0x7fff5fbff9f8   0x7fff5fbff9f8
r8             0x0  0
r9             0x0  0
r10            0x8000000    134217728
r11            0x206    518
r12            0x0  0
r13            0x0  0
r14            0x6  6
r15            0x0  0
rip            0x7fff8eef3866   0x7fff8eef3866
eflags         0x206    [ PF IF ]
cs             0x7  7
ss             *value not available*
ds             *value not available*
es             *value not available*
fs             0x0  0
gs             0x30000  196608
(gdb) disas main
Dump of assembler code for function main:
   0x0000000100000ed0 <+0>: push   %rbp
   0x0000000100000ed1 <+1>: mov    %rsp,%rbp
   0x0000000100000ed4 <+4>: sub    $0x70,%rsp
   0x0000000100000ed8 <+8>: mov    0x131(%rip),%rax        # 0x100001010
   0x0000000100000edf <+15>:    mov    (%rax),%rax
   0x0000000100000ee2 <+18>:    mov    %rax,-0x8(%rbp)
   0x0000000100000ee6 <+22>:    lea    -0x60(%rbp),%rax
   0x0000000100000eea <+26>:    movl   $0x0,-0xc(%rbp)
   0x0000000100000ef1 <+33>:    mov    %edi,-0x10(%rbp)
   0x0000000100000ef4 <+36>:    mov    %rsi,-0x18(%rbp)
   0x0000000100000ef8 <+40>:    mov    -0x18(%rbp),%rsi
   0x0000000100000efc <+44>:    mov    0x8(%rsi),%rsi
   0x0000000100000f00 <+48>:    mov    %rax,%rdi
   0x0000000100000f03 <+51>:    callq  0x100000f54
   0x0000000100000f08 <+56>:    lea    0x7b(%rip),%rdi        # 0x100000f8a
   0x0000000100000f0f <+63>:    lea    -0x60(%rbp),%rsi
   0x0000000100000f13 <+67>:    mov    %rax,-0x68(%rbp)
   0x0000000100000f17 <+71>:    mov    $0x0,%al
   0x0000000100000f19 <+73>:    callq  0x100000f4e
   0x0000000100000f1e <+78>:    mov    0xeb(%rip),%rsi        # 0x100001010
   0x0000000100000f25 <+85>:    mov    (%rsi),%rsi
   0x0000000100000f28 <+88>:    mov    -0x8(%rbp),%rdi
   0x0000000100000f2c <+92>:    cmp    %rdi,%rsi
   0x0000000100000f2f <+95>:    mov    %eax,-0x6c(%rbp)
   0x0000000100000f32 <+98>:    jne    0x100000f43 <main+115>
   0x0000000100000f38 <+104>:   mov    $0x0,%eax
   0x0000000100000f3d <+109>:   add    $0x70,%rsp
   0x0000000100000f41 <+113>:   pop    %rbp
   0x0000000100000f42 <+114>:   retq   
   0x0000000100000f43 <+115>:   callq  0x100000f48
End of assembler dump.
(gdb) 
Was it helpful?

Solution

From your assembly code you can see that the compiler added a stack_chk_guard, to get more "easily manipulated" programs try compiling with -fno-stack-protector (assuming it's gcc). Some more options (also in llvm) are here.

It also shows that the compiler is saving 112 bytes on the stack for local variables, which include 4 byte and 8 byte register spills - so it's possible that the rest 100 bytes are the buffer + some padding for protection, which might explain why you see it failing above 100.

You actually didn't finish the entire frame there, you're probably overrunning the spilled registers, which explains why they appear to have been "changed" in your previous runs, and also possibly why you got a SIGABORT instead of seg fault and why the return address is normal - you didn't overwrite the return address, you overwrote some register (leading to god knows what)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top