Bof Demystified

of 24

  • date post

    03-Jun-2018
  • Category

    Documents

  • view

    223
  • download

    0

Embed Size (px)

Transcript of Bof Demystified

  • 8/12/2019 Bof Demystified

    1/24

    BUFFER OVERFLOWS DEMYSTIFIED by murat@enderunix.org

    Emergence of buffer overflow vulnerabilities dates back to 1970s.Morris Worm (1980s) can be considered the first public use of them.Documents such as Aleph1's famous "Smashing the Stack for Fun and Profit"and code related to it has been being published on the Internet since1990s.

    This document is a starter of a series of documents about some sortof subjects, which require great attention and involve pretty much detail;and aims to explain and clarify the very basic vulnerability type, namelylocal buffer overflows, and document the way to write exploits making use ofsuch vulnerabilities.

    To understand what goes on, some C and assembly knowledge isrequired. Virtual Memory, some Operating Systems essentials, like, forexample, how a process is laid out in memory will be helpful. You MUST knowwhat a setuid binary is, and of course you need to be able to -at least-use UNIX systems. If you have an experince of gdb/cc, that is something

    really really good. Document is Linux/ix86 specific. The details differdepending on the Operating System or architecture you're using. In theupcoming documents, relatively more advanced overflow and shellcodetechniques will be explained.

    Recent versions of document can be found on:http://www.enderunix.org/documents/eng/bof-eng.txt

    What's a Buffer Overflow?

    If you know C, you - most probably - know what a character array is.Assuming that you code in C, you should already know the basic propertiesof arrays, like: arrays hold objects of similar type, e.g. int, char, float.

    Just like all other data structures, they can be classifiedas either being "static" or being "dynamic". Static variables are loaded tothe data segment part of the program, whereas dynamic variables are allocatedand deallocated within the stack region of the executable in the memory.And, "stack-based" buffer overflows occur here, we stuff more data than adata structure, say an array, can hold, we exceed the boundaries of the arrayoverriding many important data. Simply, it is copying 20 bytes to an array thatcan handle only 12 bytes...

    Memory layout for a Linux ELF binary is quite complex. It has becomeeven more complex, expecially after ELF (for detailed info, conduct a searchin google as "Executable and Linkable Format") and shared libraries areintroduced. However, basically, every process starts running with 3 segments:

    1. Text Segment, is a read-only part that includes all the program instructions. For such assembly instructions that are the equivalent of the below C code will be included in this segment:

    for (i = 0; i < 10; i++)s += i;

    2. Data Segment is the block where initialized and uninitialized (which is also known as BSS) data is.

  • 8/12/2019 Bof Demystified

    2/24

    For example:

    if you code;

    int i;

    the variable is an uninitialized variable, and it'll be stored in the "uninitialized variables" part of the Data Segment. (BSS)

    and, if you code;

    int j = 5;

    the variable is an initialized variable, and the the space forthe j variable will be allocated in the "initialized variables"part of the Data Segment.

    3. A segment, which is called "Stack", where dynamic variables (or in C jargon, automatic variables) are allocated and deallocated; and where return addresses for functions are stored temporarily. For example, in the following code snippet, i variable is created in the stack, just after the function returns, it is destroyed:

    int myfunc(void){int i;

    for (i = 0; i < 10; i++)putchar("*");

    putchar('\n');}

    If we are to symbolize the stack:

    0xBFFFFFFF ---------------------

    | | | . | | . | | . | | . | | etc | | env/argv pointer. | | argc | |-------------------| | | | stack | | | | | |

    | | | | V | / / \ \ | | | ^ | | | | | | | | | | heap |

  • 8/12/2019 Bof Demystified

    3/24

    |-------------------| | bss | |-------------------| | initialized data | |-------------------| | text | |-------------------| | shared libraries | | etc. |0x8000000 |-------------------|

    _* STACK *_

    Stack is in basic terms a data structure, which all of youwill remember from your Data Structures courses. It has thesame basic operation. It's a LIFO (Last-In, First Out) datadata structure. Its processes are controlled directly by the CPUvia some special instructions like PUSH and POP. You PUSH some datato the Stack, and POP some other data. Whoever comes in LAST, he'sthe one who will go out FIRST. So, in technical terms, the firstthat will be popped from the stack is the one that is pushed last.

    SP (Stack Pointer) register on the CPU contains the address of data

    that will be popped from the stack. Whether SP points to the last dataor the one after the last data on the stack is CPU-specific; however,ix86 architecture, which is our subject, SP points to the address of thelast data on the Stack. In ix86 protected mode (32 bit/double word),PUSH and POP instructions are done in 4-byte-units. Another important detailto be noted here is that Stack grows downward, that is, if SP is 0xFF, justafter PUSH EAX instruction, SP will become 0xFC and the value of EAX will beplaced on 0xFC address.

    PUSH instruction will subtract 4 bytes from ESP (remember the aboveparagraph), and will push a double word to the stack, placing the doublewordin the address pointed by the ESP register. POP instruction, on the otherhand, reads the address in the ESP register, POPs the value pointed by that

    address from the Stack, and adds 4 to ESP (adds 4 to the address in the ESPregister). Assuming that ESP is initially 0x1000, let's examine the followingassembly code:

    PUSH dword1 ;value at dword1: 1, ESP's value: 0xFFC (0x1000 - 4)PUSH dword2 ;value at dword2: 2, ESP's value: 0xFF8 (0xFFC - 4)PUSH dword3 ;value at dword3: 3, ESP's value: 0xFF4 (0xFF8 - 4)POP EAX ;EAX' value 3, ESP's value: 0xFF8 (0xFF4 + 4)POP EBX ;EBX's value 2, ESP's value: 0xFFC (0xFF8 + 4)POP ECX ;ECX's value 1, ESP's value: 0x1000 (0xFFC + 4)

    Stack, while being used as a temporay storage for dymmic variables,it's being used to store the return addresses for some fuction calls

    storing temporary variables and for passing parameters to fuctions.And, of course, this is where evil things come into ground.

    EIP register, CALL & RET instructions-------------------------------------

    CPU, in each machine cycle, looks at what's stored in the Instruction Pointerregister (In ix86 32-bit protected mode this is EIP - Extended InstructionPointer) to know what to execute next. In the EIP register, the address of the

  • 8/12/2019 Bof Demystified

    4/24

    instruction that will be executed next is stored. Usually, the addresses aresequential, meaning the next instruction that'll be executed next is, a few bytesahead of the current instruction in the memory. The CPU calculates that "a fewbytes" according to how many bytes long the current instruction is; and addsthat "a few bytes" value to the address of the present address. Toexamplify, assume that the present instruction's address is 0x8048438. This isthe value that's written in EIP. So, CPU is executing the instruction that'sfound in memory location: 0x8048438. Say, it's a PUSH instruction:

    push %ebpCPU knows that a PUSH instruction is 1 byte long, so the next instruction willbe at 0x8048439, which may be

    mov %esp,%ebp

    While executing the PUSH, CPU will put the address of MOV in EIP.

    Okay, we said that the values that'll be put in EIP are calculated by the CPUitself. What if we JMP to a function? The addresses of the instructionsin the function will be somewhere else in the memory. After they are executed,how can the CPU know where to go on with the calling procedure and execute.For this, just before we JMP to the function, we save the address of the nextinstruction in a temporary register, say in EDX; and before returning from thefunction we write the address in EDX to EIP back again. If we use JMP to jumpto the addresses of functions, that would be a very tiresome work actually.

    However, ix86 processor family provides us with two instructions: CALL andRET, making our lives easy! the CALL instruction writes that "next instructionto be executed after function returns" (from then on, we'll call thisas the "return address") to the stack. It PUSHes it onto the stack, and writesthe address of the function to EIP. Thus, a function call is made. The RETinstruction, on the other hand, POPs the "return address" from the stack, andwrites that address in the EIP. Thus we'll safely return from the function,and continue with the program's next thread of execution.

    Let's have a look at the following code snippet:

    x = 0;

    function(1, 2, 3); x = 1;

    After several assembly instructions has been run for (x = 0), we need to gothe memory location where function() is located. As we said earlier, for thisto happen, first we copy the address of the return address, (the address of x= 1 instructions in this case.) to some temporary space (might be a register)jump to the address space of function with JMP, and, in the end of thefunction we restore the return address that we'd copied to the EIP.

    Thank God, all these dirty operations are done on behalf of us via CALL

    and RET by the CPU itself, and you can get the details from the aboveparagraph.

    Generally, the Stack region of the program can be symbolized like:

    |_parameter_I____| ESP+8|_parameter II___| ESP+4|_return address_| ESP

    Figure X : Stack

  • 8/12/2019 Bof Demystified

    5/24

    ESP, EBP--------

    The stack, as we've said, is also used to store dynamic variables.Dynamically, the CPU PUSHes some data, as the program requests new space, andPOPs oth