Skip to main content

PICOGYM - Here's a LIBC

This challenge is brought to us courtesy of picoGym, which is picoCTF's year round training platform.  The challenge provides 3 files: libc.so.6, Makefile, and vuln.  All three files, and the code used in this walk-through, can be found in my GitHub repo at  https://github.com/ChrisCHumphreys/picolab/tree/main/heres_a_lib_c.  Also, a huge thanks to guyinatuxedo, whose resources at https://guyinatuxedo.github.io/index.html were invaluable in helping me to learn this stuff.

On my system at least, the executable would not work due to my having a different libc in use than the one the program was originally compiled with.  I fixed this by running pwninit on the file and generating a version of vuln that uses the libc provided with the challenge. The name of this new modified file is vuln_patched.  pwninit can be found at https://github.com/io12/pwninit.

First, running checksec on this executable shows that very few protections are enabled, but that the stack is not executable, which means we cannot just shove shellcode onto the stack and execute it.  We will need to use ROP chains.

 

Next, running the program reveals that it doesn't do a whole lot.  Mainly it just seems to take in input and return that same input, but with every other letter capitalized.


Examining the de-compiled application in Ghidra shows that the main function is not super helpful, but that there is a function named do_stuff, which is vulnerable to a buffer overflow. Bonus points for identifying the vulnerability before reading the next paragraph. 👅

The vulnerable line here is the scanf line that scans into the input buffer until a newline character is encountered.  Knowing this, we next need to find out how big our overwrite needs to be in order to overwrite the rip register and hijack the flow of execution.  Ghidra helps us again, the stack layout for this function shows us that the input buffer is exactly 0x88 bytes away from the RIP register.

We can also verify this with GDB by finding our input and subtracting that value form the value in rip.  In this case, the string I used as input was "this-is-input".  I put in a break in GDB for right after the input was accepted, and then ran "i f" to get info about the current stack frame.


 

Just as we suspected, the offset is 0x88 characters.

The next items we need are going to be the GOT and PLT addresses of puts.  The reason for this is we will need to find a way feed the GOT address (which is different every time the program is ran), into the PLT address of puts.  Essentially, what we are doing is printing the dynamic address of puts using puts.  Both of these values can be found using pwntools or objdump.  In my case I used pwntools.  The GOT address ended up being 0x601018 and the PLT address was 0x400540.

Now we need a "pop rdi" gadget.  The reason for this is in a 64 bit system, puts will print whatever is pointed at by the address in the RDI register when puts is called.  To print the GOT address, we need to first load it into the RDI register.  I found the gadget using the ROPGadget tool which can be found at https://github.com/JonathanSalwan/ROPgadget. The address we will be using is 0x400913.

Now, we need to find the offsets from the base of libc at which puts, system, and the string "/bin/sh" are located.  The reason for this is that although every time the program is ran the addresses will be different, the space between the base of libc and these functions will remain the same.  By subtracting the offset of one of these items from its location in a running program we will find the address of the base of libc.  Additionally, to get a shell we need to execute the syscall "system" and have it run /bin/sh.

I used gdb to get this information.  I started by simply breaking at main and then printing the items current value, and the current value of the base of libc.  For the current libc base I used the gdb command vmmap.

In the above example the base is at 0x00007ffff79e4000.  Next to find the addresses of puts, system, and /bin/sh, we do as in the screenshot below.

For the offsets we subtract the current value from the current base. So the offset of puts is 0x80a30, system is 0x4f4e0, and /bin/sh is 0x1b40fa.

The last piece needed is the address of main.  We need this because after we leak the address of libc we need to return to main, and overflow the buffer again, but on the second run we will pop a shell using our ROP chain instead of leaking the libc address.  This is easily found in Ghidra or by using objdump.  The address is 0x00400771.

That was a lot of prep, but we know have everything we need to write the exploit, which can be found below.

One final note, I spent hours on this exploit without being able to get it to work properly.  Finally, I took  a look at a different writeup and noticed that the second chain in that exploit started with a vanilla RET call.  It can be seen on line 67 in the exploit above.  With this, it works perfect.  Without it, it does not work at all.  If you know the how or why behind this feel free to drop a comment as I myself am very curious about why this is happening.

Comments

Popular posts from this blog

heap 0 - PicoGym

 This challenge, while technically a heap exploit is relatively easy. It is a simple buffer overflow in which the buffer that is supposedly "safe" is located directly after a user controlled buffer in the heap. Just like with a stack overflow, we can write past the bounds of our buffer and overwrite the "safe" buffer. The files and other items for this challenge can be found at the PicoGym under the challenge named "heap 0". I started as I always do by running "checksec" on the binary (pwntools required).   Fortunately, the debugging info is left in which is convenient, but we have access to the source code anyway so its not super helpful. Unfortunately, the stack is not executable, and addresses are randomized, since we know from the name and description that this is a heap overflow challenge though that is not really too surprising. While I can't really capture it in blog form, my next step was to read through and understand every line of ...

KnightCTF 2022 - Whats your name 2

This is my write up of the whats_your_name_2 challenge from KnightCTF 2022.  The files needed for this writeup can be found at https://github.com/ChrisCHumphreys/knightctf22/tree/main/whats_your_name_2 . First, by examining the executable with checksec from pwn tools we can see that we have a 64 bit binary with very few protections built in. Running the program shows that it simply asks us to enter a username, and then the program exits.   Next, examining the de compilation in Ghidra, we can see that there is an fgets call that will read in a max of 200 bytes to a char buffer named input_buf.  No real problems here are there is more than enough room for 200 characters in the buffer.   Following this, we can see that there is a call to strcpy which will copy the string in input_buf to the local_58 buffer.  This is where the problem is.  strcpy does not check to ensure that the string being copied will fit into the buffer being copied to.  It just kee...