Bookshelf
This pwn challenge involved the use of return-oriented programming to call system and spawn a shell.
Description
Just finished up my project based around books! Hope you enjoy reading…
You can download the binary here and the corresponding libc here.
Part I: Getting the address of puts
When we connect to the server, we see that there are multiple options to choose from. Lets take a look at option 2.


It seem like we are given $500 to start, and we somehow have to pull $99999499 out of thin air so that we can get the address of puts. This sounds like a integer underflow vulnerability, so lets get hacking.
When we select the book that we want to read, the program would check if we have enough cash, and reply accordingly. However, it would then ask for a tip, without checking if you have enough cash to tip.


Hence, we can keep leaving the program a tip till our cash underflows!

Now we can successfully leak the address of puts 😄

Take note of the address as it will come in handy later.
Part II: Admin Access
There seems to be a function which only admins can access. Lets take a look at it’s decompilation.

Ignoring all the fancy stuff, we see that we are granted admin permissions if arg1 is not 0. So how can we do this?
After playing around with the inputs, I found out that choosing option 1 and writing exactly 40 characters would allow us to overflow into arg1 and modify it’s value to be 65, which corresponds to \n, which is non-zero. I can’t pinpoint the exact lines of code that is causing this behaviour, so it would be nice if any kind soul explained it to us at [email protected] 😄
Part III: Buffer Overflowing Admin
The function adminBook allows us to input some book_content. Upon looking at the disassembly, we can see that book_content is only allocated 48 bytes, but fgets reads 256 bytes in! This screams a buffer overflow to me.

Lets try dumping a lot of characters inside. As I’m too lazy to do the integer underflow everytime I reopened the program, I wrote a pwntools script to automate it for me. It’s really messy by the way.
But now that we have access to admin, lets create the long payload in GDB and send it!

Part IV: Controlling RET
In the disassembly of adminBook, we see that right after puts it calls leave followed by ret. If you’re interested, you can read up on it here. leave and ret assume that you have a valid stack that has not been tampered with.

However, since we have a buffer overflow, the stack isn’t valid anymore, and we can control ret to return to a malicious instruction instead. The illustration below shows how our overflowed input can be used to control the return address.

As the number of bytes between the start of the user input and overwriting the return address is fixed, we can create a cyclic pattern in GDB to calculate the number of bytes, aka the offset.

After putting the pattern in and stepping until the ret instruction, we see that the value which rsp points to is part of our input! ret would pop this value off the stack and jmp to the corresponding address, thus we can simply write our own address that we want to jump to.

We can calculate the offset simply by putting the command: pattern search {pattern in RSP}

And viola, we got the offset of 56!
Part V: Calculating the address of system
Since there is no function within the program that allows us to view the flag, we have to somehow spawn a shell and read the flag. Thankfully, we have the system function in libc that can help us do this. It is achieved by calling system("/bin/sh").
Let’s once again employ the help of pwntools to do this.
If we debug the program once more, we see that our stack pointer now points at system, which will be popped into RIP to be executed.

Part VI: Crafting an ROP Chain
Now we can call system, but how do we supply the argument /bin/sh to it? Well, first we need to somehow load the string /bin/sh into RDI, as specified by the section A.2 AMD64 Linux Kernel Conventions in System V Application Binary Interface
User-level applications use as integer registers for passing the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9.
We can load the string by finding an instruction address that does pop rdi; ret, and put the string /bin/sh into the stack right after the instruction. pwntools has a built-in way for us to do this, so lets try it!
Aaaaand we got a shell 🥳 (I could only do this locally as they shut the servers down when I was doing this writeup)

Flag: PCTF{r3t_2_libc_pl0x_52706196}
Final script: