paint-brush
Introduction to One Gadget: A Necessary Tool for Exploitationby@pwnbykenny
2,576 reads
2,576 reads

Introduction to One Gadget: A Necessary Tool for Exploitation

by pwnbykennyJanuary 10th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

This post uses a concrete CTF problem to show you step-by-step how to find and execute a one gadget and eventually spawn a shell.

Coin Mentioned

Mention Thumbnail
featured image - Introduction to One Gadget: A Necessary Tool for Exploitation
pwnbykenny HackerNoon profile picture

1. Content

2. You definitely want to choose one gadget for exploitation

One gadget is a line of C code: execve(“/bin/sh”, 0, 0);. Apparently, this code spawns a shell. If you are able to find and run it in the memory, you get a shell! See? EASY and POWERFUL!

Fortunately, it exists in libc. And libc is used in most programs! libc is a C library file. The one_gadget tool finds all the occurrences of the code in such files.

3. The famous one_gadget tool

(Linux OS) Download the tool by running: gem install one_gadget. Then you can easily run it in a terminal: one_gadget /usr/lib/x86_64-linux-gnu/libc-2.31.so. The argument is the path to your libc file. It may change on your machine. This will produce the following output:

execve(“/bin/sh”, r12, r13) is the one gadget. Before running this, you need to make sure the two lines of constraints are all true/satisfied. The one gadget becomes execve(“/bin/sh”, 0, 0) only if the constraints are satisfied. 0xcbcda is the offset of the gadget within the libc file. In order to know the address of the gadget in the memory, you also need to know the base address of the libc file in the memory: the gadget’s memory address = the libc file’s memory base address + the offset of the gadget within the libc file.

4. A concrete CTF example showing you step-by-step on how to use the tool

4.1 Environment Setup and Introduction

First, download this file. It comes from this platform > Pwn > [BJDCTF 2nd]one_gadget. Then we run it, it produces the following output:

If you disassemble the executable, you will find that the gift is actually the address of the libc function printf. It may change on each different execution. And you will also find that the executable requires you to input the address of a one gadget. The executable will execute the one gadget at the given address. Now let’s see how we are gonna execute a one gadget.

4.2 Conquer the CTF Problem

Here are the steps:

Install some kind of plugin to gdb that supports the command “vmmap”. I use gef.Run “gdb problem”. Now gdb reads in the executable file. Then, run the program under gdb with the command “r”. Next, press “ctrl + c” to give the control back to gdb. Now we are able to inspect the memory with the command “vmmap”. In the output, you will notice something like the following picture that shows the path to the libc file. This is the libc used in this program. Now we want to locate some one gadgets in the libc file.

Quit the gdb process. Now run “one_gadget /usr/lib/x86_64-linux-gnu/libc-2.31.so”. You need to change the path to yours. You will observe something like the picture in section 3 in the output. So now we have the one gadget’s offset within the libc file: 0xcbcda.Well, you also need to find the offset of the libc function printf. Run this command: objdump -tT /usr/lib/x86_64-linux-gnu/libc-2.31.so | grep ” printf”. You need to replace the path to the libc file with yours. The following picture is the output. The first column is the offset. So the offset of printf is: 0x56cb0.

Start gdb again: gdb problem. Set a breakpoint at main: b main. Run the program: r. Now you will see that the program stops at the main function. Run a line of code at a time with the command: n. If you see an output like this: here is the gift for u:0x7ffff7e3bcb0, calculate the one gadget’s address with the formula I gave you in section 3: gadget address = libc base address + gadget offset = printf address – printf offset + gadget offset = 0x7ffff7e3bcb0 – 0x56cb0 + 0xcbcda = 0x7ffff7eb0cda = 140737352764634. Continue to run the program with n. Now look at the following picture. After the printf function is executed, you will see the output: Give me your one gadget:. But now is not the right time to input the one gadget address. You need to wait until the red scanf function is executed. That’s when you need to input the one gadget address.

After running the scanf function, now is the time to input your one gadget address. Type in the address in decimal format instead of hex format! In my case, it’s 140737352764634. Please be careful from now on, because some magic is about to happen. After the scanf function, the following is the machine code that’s gonna be executed. Beginning from the $pc pointer, you see what the three lines of code are doing? They move a value in the memory [rbp-0x18] to rdx. After you run the three lines of code, you will find out that rdx stores the one gadget address! Then “call rdx” goes to execute the one gadget!

Run the code until “call rdx”. But don’t run “call rdx”! Why? Recall that there are constraints that need to be satisfied before we are able to successfully execute the one gadget. The constraints are: r12 == NULL && r13 == NULL. Now let me observe what they are (shown in the following picture). Apparently, r12 is not null. We need to set it to null with the command: set $r12 = 0. Now if you run this command: p $r12, you will find that r12 becomes null.

Now we can run “call rdx”. This time, we continue our execution with the command: c. A shell will be immediately spawned!

5. Summary

This post used a concrete CTF problem to show you step-by-step how to find and execute a one gadget and eventually spawn a shell. If you like this post, please help me share it on your social media. Thank you so much!

Previously published at https://pwnbykenny.com/en/2020/12/31/one-gadget-easy-powerful-tool-example/