fliX
fliX

Reputation: 813

Modify return address on stack

I looked at the basics of buffer overflow vulnerabilities and tried to understand how the stack is working. For that I wanted to write a simple program which changes the address of the return address to some value. Can anybody help me with figuring out the size of the base pointer to get the offset from the first argument?

void foo(void)
{
    char ret;
    char *ptr;

    ptr = &ret; //add some offset value here 
    *ptr = 0x00;
}

int main(int argc, char **argv)
{
    foo();

    return 1;
}

The generated assembler code looks as follows:

    .file   "test.c"
    .text
    .globl  foo 
    .type   foo, @function
foo:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16 
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    leaq    -9(%rbp), %rax
    movq    %rax, -8(%rbp)
    movq    -8(%rbp), %rax
    movb    $0, (%rax)
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret 
    .cfi_endproc
.LFE0:
    .size   foo, .-foo
    .globl  main
    .type   main, @function
main:
.LFB1:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16 
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    %edi, -4(%rbp)
    movq    %rsi, -16(%rbp)
    call    foo 
    movl    $1, %eax
    leave
    .cfi_def_cfa 7, 8
    ret 
    .cfi_endproc
.LFE1:
    .size   main, .-main
    .ident  "GCC: (GNU) 4.7.1 20120721 (prerelease)"
    .section    .note.GNU-stack,"",@progbits

The relevant part of the foo frame segment should look like this:

[char ret] [base pointer] [return address]

I have the position of the first one which is only 1 byte in size. Is it only 1 byte further to the base pointer or the size of a word as mentioned in http://insecure.org/stf/smashstack.html? And how do I get to know the size of the base pointer?

Upvotes: 9

Views: 7874

Answers (3)

Uli Klank
Uli Klank

Reputation: 199

Your basepointer is most likely just a pointer, so it has the size sizeof(int*). But there is also another value in between your variable ret and the base pointer. I would assume its value of a register (eax?). This would lead to something like the following, if you want an endless loop:

void foo(void)
{
    char ret;
    char *ptr;

    ptr = (char*)(&ret) + (sizeof(ret)  + 2*sizeof(int*)) ;
    *(int*)ptr -= 0x0c;
}

The return target which is modified assuming it has the size of a pointer (could differ for other instruction sets). By decrementing it, the return target is set to a point before the calling point of foo.

Upvotes: 1

Keith Randall
Keith Randall

Reputation: 23265

You're not going to be able to do this in vanilla C, you don't have control of how the compiler lays out the stack frame.

In x86-64, the return address should be at %rbp + 8. You could use some inline assembly to get that (gcc syntax):

uint64_t returnaddr;
asm("mov 8(%%rbp),%0" : "=r"(returnaddr) : : );

Similarly for setting it.

Even that is a bit sketchy, as you don't know whether the compiler is going to set up %rbp or not. YMMV.

Upvotes: 2

Claudi
Claudi

Reputation: 5416

It seems you're using a 64-bit architecture, since RBP and RSP registers are 64-bit long. If you declare ptr as char* you will have to increment it 8 times to move through the stack. Instead you could declare it as uint64_t *. This data type is normally available in <stdint.h>.

However the stack frame definition varies depending on the target architecture and even on the compiler behaviour and optimizations. It's fine if you are experimenting, though.

Upvotes: 0

Related Questions