Return-to-libc Attack: SEED Labs Walkthrough

Sudeepa Shiranthaka
7 min readJan 13, 2024

--

Image source: slideserve

In the article, we are going to talk about return-to-libc attack. You can find out this lab exercise which is available on the Seed project. The first thing is downloading the lab files from the above link. The next step is to install the Seed VM on the virtual box.

Download and install the Seed virtual machine from here. Once we have the relevant file, we have to install it on the Oracle virtual box. In the lab environment, we have 3 files (exploit.py, Makefile, retlib.c). We need to copy those files to the VM environment and when everything is set we are good to go with the exploitation.

What is Libc

Libc, short for “C library,” refers to the standard C library that provides fundamental functions and definitions for the C programming language. It is a collection of pre-compiled functions and macros that offer a wide range of functionality, such as input/output operations, memory management, string manipulation, mathematical operations, and more.

The C library serves as a crucial component of the operating system and programming environments, providing an interface between the application code and the underlying system. It abstracts the low-level system operations and provides a standardized set of functions that developers can use to write portable and efficient C programs.

What is a Return-to-libc attack

A return-to-libc attack is a type of computer security vulnerability that exploits a flaw in a program’s memory management to gain unauthorized access or control over the system. It is typically carried out against programs written in the C programming language that use the standard C library (libc).

The attack takes advantage of a buffer overflow or similar vulnerability in the target program. In a buffer overflow scenario, the attacker sends more data to a buffer than it can hold, causing the excess data to overwrite adjacent memory locations. By carefully crafting the input, the attacker can overwrite the return address on the program’s call stack, redirecting the program’s execution flow to a different location of their choice.

In a return-to-libc attack, instead of injecting arbitrary code into the program’s memory, the attacker manipulates the return address to point to a function within the libc library that already exists in memory. The libc library is used by many programs, and its functions are loaded into memory during program execution.

Steps for the Exploitation

Linux and Ubuntu distributions have implemented several security controls to prevent the buffer overflow attack. So, to perform this exploitation successfully, we need to disable the following security controls on the operating system.

  1. Disabling ASLR
  2. Configuring /bin/sh to /bin/zsh
  3. Disabling the StackGuard Protection scheme

Disabling ASLR

ASLR is a security technique used by operating systems to randomly arrange the memory addresses where system executables, libraries, and data areas are loaded. The primary goal of ASLR is to prevent attackers from predicting or reliably locating specific memory areas to exploit security vulnerabilities.

To disable the ASLR you can use the below command as shown.

sudo sysctl -w kernel.randomize_va_space=0

Configuring /bin/sh to /bin/zsh

The default Ubuntu 20.04 configuration links /bin/sh points to the /bin/dash. (Debian Almquist Shell) The dash shell has a countermeasure that prevents itself from being executed in a Set-UID process. If dash is executed in a Set UID process, it immediately changes the effective user ID to the process’s real user ID, essentially dropping its privilege. Use the following command the change the dash shell to ZSH. The following command link to zsh shell with the /bin/sh shell.

sudo ln -sf /bin/zsh /bin/sh

  • ln: This is the command used to create links between files. In this case, the -s option is used to create a symbolic link.
  • -sf: These are options for the ln command:
  • -s: Creates a symbolic link.
  • -f: If the target file (/bin/sh) already exists, it is removed without prompting the user.

Disabling the StackGuard Protection scheme

gcc -m32 -fno-stack-protector example.c

The gcc compiler implements a security control called StackGuard to prevent buffer overflows. If it is placed the buffer overflow attacks do not work. To disable StackGuard we can use the following command. Here in this lab, this command executes it self with the Makefile.

First, open and observe the Makefile using the editor.

gedit Makefile

To execute the Makefile, we need to type the “make” command and that will compile the vulnerable retlib. c and convert it into a SET UID program.

If we see the files now, we can see the compiled program called retlib executable.

Now let’s closely examine the exploit.py file. Here we can see, that it needs to find four things to perform this attack. After we find those four requirements we can create the badfile.

1. X, Y, and Z values (From decimal format)

2. The address of /bin/sh

3. The address of the system function

4. The address of the exit function

Before we find out those four, we need to create an empty file for our results to get saved. I’m naming it as badfile.

To figure out the three addresses and the values for X, Y, and Z, we can debug the retlib.c program and calculate the distance between %ebp and buffer inside the function bof(): We use the gdb debugger to find out the above requirements that we need to perform the exploitation.

Put a breakpoint at the Main function and then run the program according to the below screenshots.

Then we can print the system address and the exit address. For that, we can use the p command or print command.

After we can change the exploit.py file by replacing the system and exit address as below.

Next, we have to find the address of /bin/sh. For that, we can create new environmental variables as follows. Here new variable will be NEW001.

Next, create a c program to print the address of the env variable. Compile the program as an x32 version as shown in the below snippets.

Run the program and get the address of the /bin/sh shell.

Replace that address on the exploit.py.

As our findings:

sh_addr = 0xffffddfc

system_addr = 0xf7e12420

exit_addr = 0xf7e04f80

Finally, we have to find out the offset. To that, we can run the retlib executable.

This will give the buffer address and EBP or frame pointer address.

Address of buffer[] inside bof(): 0xffffcd70

Frame Pointer value inside bof(): 0xffffcd88

If we subtract these two, it will get the offset of the number.

The distance between %ebp and buffer is 24 bytes. Once we enter the system() function, the value of %ebp has gained four bytes. Therefore: Since we get the 24 as offset the X, Y, and Z values should be as follows:

Y = 24+4

Z = 24+8

Z = 24+12

Now, we are ready to do the exploitation. We can observe that before the execution of the exploit.py, the badfile is empty.

You can see the results after the execution in the below screenshot. ( See the size of the badfile )

Finally, we can run the retlib to get the root shell.

You can find me on😊:

Linkedin: www.linkedin.com/in/sudeepashiranthaka

Buy me a coffee: https://www.buymeacoffee.com/sudeepashiz

Medium: https://sudeepashiranthaka97.medium.com/

Twitter: https://twitter.com/sudeepashiran97

--

--

Sudeepa Shiranthaka

Security Engineer | Researcher | Blogger | Writer | AppSec & InfoSec enthusiastic