32-Bit Return2Libc
Last updated
Was this helpful?
Last updated
Was this helpful?
The target binary is a custom binary written for the purpose of introducing two techniques.
Memory leakage through existing function calls
32-Bit Ret2Libc Exploitation
Running the file command shows the following key points.
The binary is a 32-bit ELF executable.
The binary is dynamically linked, meaning GLIBC is not compiled into the binary.
The binary is not stripped of its debugging symbols.
Checking the memory protections in place, shows the most notable protection is Data Execution Prevention. Additionally the binary has Partial RELRO enabled, meaning that the GOT entries are writable.
Looking at the binary within a disassembler, then browsing to the "vuln" function. It immediately becomes apparent where the vulnerability lies. Due to the program using a "gets()" function call to take user input, this binary suffers from a buffer overflow vulnerability.
The first step is to obtain control over the stored return address; this value is written to the instruction pointer register (EIP) in the function epilogue granting the attacker control over the execution flow.
The distance between the user's input and the stored return address can be determined by analyzing the two instructions prior to the gets() function call.
The memory address of the destination buffer is retrieved and stored into the EAX register.
The value stored within the EAX register is then pushed onto the stack; this value is then used as the size argument for the gets() function.
The lea instruction shows that the destination buffer starts at an offset of "0xd0"relative to the base pointer. Due to the stored return address being located in the adjacent 4 bytes after the stored base pointer, the padding required to reach the stored return address will be "0xd4" or 212 in decimal.
Due to the binary lacking any clear ways to obtain a shell; no "/bin/sh" string or system calls, the next viable step would to be use those items from GLIBC.
However, due to GLIBC always being effected by Address Space Layout Randomization (ASLR) a leaked address is required to determine the location of the system() function and the string "/bin/sh". These addresses can then be used to mimic a legitimate function call.
In order to leak an address from GLIBC, an existing I/O function can be utilized to output an address stored in the Global Offset Table (GOT). For example, this can be done by mimicking a printf() function call in which the address of a GOT entry serves as the buffer address.
In the case of the binaries "vuln" function, a printf() call is present which can serve to execute the theory explained above.
In order to leak an address from an the Global Offset Table, the location of where this address is stored first needs to be known. This location can be found by further inspection of existing function calls within the target binary. In this case, the existing function call utilized is printf().
Rather than the call immediately redirecting the execution flow into the printf() function, the execution flow is first directed into the Procedure Linkage Table (PLT).
Following this execution flow, the address at which the desired GOT entry resides can be seen.
By overwriting the return address with the address of the printf() function and laying out the address of the GOT entry in a manner which represents the state of a stack frame after a function call has been made, the printf() function will output the value of the GOT entry. This can be done using the following python script.
Within the exploit script below, the reasoning for providing the order of:
Printf @ PLT
Start of the Vuln Function
Printf @ GOT
Is due to the cdecl calling conventions, the 8 bytes behind the stored return address will figure as the return address (first 4 bytes) and the argument (last 4 bytes). By returning back into the first instruction of the vuln() function, the vulnerability can be exploited a second time with the knowledge given by the leak.
Running this script gives the following result.
In order to determine the addresses of the desired system() function and "/bin/sh" string, the offset between the leaked address and the base address of GLIBC has to be calculated.
This can be done by analyzing the binaries virtual memory mapping during the same runtime session, and subtracting the GLIBC base address from the leaked value.
Due to irregularities between the different versions of GLIBC the required offsets will differ.
The reason for providing the last three characters of the leaked address is due to memory page sizes being iterations of 0x100 regardless of the memory pages location. The last three characters will not be effected by the dynamic address at which the memory page resides.
In the second overflow, the leaked information is used to direct the execution flow to GLIBC's system() function with the address of the "/bin/sh" string as an argument causing the binary to spawn a shell.
Below is a proof of concept showing this.
Finding the correct offsets can be accomplished by using the Presenting the last 3 characters of the leaked address along with the name of the function being utilized to leak the GOT entry, the offset to the desired system() function and "/bin/sh" string becomes visible.