Linked lists are great! They let you chain pieces of data together.

nc pwn.chal.csaw.io 9005

shellpointcode

Let’s check the file information first.

We have a 64-bit ELF shared object which is dynamically linked and not stripped.

Let’s look at its protections before we get our hands on it.

The binary is position independent but its stack is executable and there is no stack canary.

Now, we can run the executable to see what it does.

Interesting, we already got a segmentation fault with those small inputs. It seems we have a linked list with two nodes and it gives us the address of the second node which is great.

Let’s disassemble its main function to see how it allocates and fills these nodes and what causes the segmentation fault.

It simply disables the buffering for stdin and stdout and prints the introduction message of the program. Then, it calls nononode function. Let’s dive into it.

Now, it calls readline function twice with different buffers and 0xF as parameters. It is obviously filling nodes’ buffers. Afterwards, it calls the goodbye function which must be the one that asks for our initials. Before getting to the goodbye function, let’s see how the readline function is implemented.

It reads a line and copies up to first 15 bytes of it to the buffer. However, we know from its documentation that strncpy function does not append a null byte which means the 15th byte does not need to be null. Let’s move on to the goodbye function.

This function reads up to 0x1F characters. Notice that, it uses fgets and we know again from the documentation that it appends a null byte.

Let’s set a breakpoint on this fgets call and examine the stack.

0x7fffffffe0c0 is the address of the second node and our fgets function reads into the address 0x7fffffffe0ad. Also, we see that the return address of the goodbye function is stored at 0x7fffffffe0b8.

Let’s analyze the stack further.

We notice that the second node’s address does not point to its buffer directly. Also, we see that our first node’s buffer is located at 0x7fffffffe0e8 which is 0x28 bytes further then the first node’s address. And, just above it, we have the second node’s address. Therefore, we can guess the structure of node as:

Now, we now that we can modify the return address thanks to this fgets call, and we also know the first node’s buffer address since we have a known address from the stack which allows us to calculate other addresses from the stack using offsets. We can place a shellcode on the first node’s buffer. However, the buffer is too short. 15 bytes are not enough to write a 64-bit shellcode. However, instead of trying to write “/bin/sh” string to the stack in the shellcode, we can just place that string in the second node’s buffer. Then, we can just pop it into rdi. We also need to set rax to 59 and we need to make sure that rdx and rsi are 0. Let’s write the shellcode and calculate how many bytes it requires.

We also need to make sure that our shellcode does not contain 0x0A  or 0x00 bytes. 0x0A causes readline to stop and 0x00 stops the strncpy function.

Great! It is 14 bytes and does not contain 0x00 or 0x0A. Now, we have all the information that is required to create our exploit. Here is the exploit I wrote in python:

Let’s run the exploit.

Here we have flag{NONONODE_YOU_WRECKED_BRO}.