[WRITE-UP] 35C3 CTF
Had some spare time during the Christmas holidays to make a couple reversing challenges in the 35C3 CTF.
For now on I will write my write-ups on this site.
Sanity check (1p)
Number of solves: 612.
#!/usr/bin/env python3
import codecs
codecs.encode('35P3_hfr_guvf_gb_REDACTED', 'ROT13')
35C3_use_this_to_ERQNPGRQ
Maybe not worth mentioning… but may be helpful for someone :)
0pack (49p)
Number of solves: 167.
$ strace ./0pack.elf
# ...
getpid() = 98520
open("/proc/98520/maps", O_RDONLY) = 21
read(21, "560ba2215000", 12) = 12
close(21) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
brk(NULL) = 0x560ca262e000
brk(0x560ca264f000) = 0x560ca264f000
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
write(1, "Input password: ", 16Input password: ) = 16
read(0, PASSWORD
"PASSWORD\n", 1024) = 9
write(1, "\n", 1
) = 1
write(1, "Awwww \x154\x106\xf0_\x154\x106\xf0\n", 14Awwww ಠ_ಠ
) = 14
exit_group(0) = ?
+++ exited with 0 +++
The binary appears to map executable code into memory. I loaded the binary into radare2. The binary does have security mechanisms, but isn’t stripped:
[0x55b9f1fd3a1e] > i ~pic,nx,canary,relro,stripped
canary true
nx true
pic true
relro partial
stripped false
Continued until system call write at “Input password:” using dcs write, and stepped from libc code to binary code in the rwx memory map.
[0x55b9f1fd3a1e]> dm.
0x000055b9f1fc1000 - 0x000055b9f1fd5000 * usr 80K s rwx /root/Downloads/0pack/0pack.elf /root/Downloads/0pack/0pack.elf ; r12
To analyze I dumped the memory map: dmd dump.bin and imported it into Hopper debugger, as raw binary with base address 0x0 and offset 0x0.
The function at address 0x55b9f1fd3a1e is sub_129a0, starting at address 0x55b9f1fd39a0.
The function sub_12927 is just a anti-debugging method.
Register r15 points to the start of memory map (0x000055b9f1fc1000).
[0x55b9f1fd3a1e]> dr r15
0x000055b9f1fc1000
Each flag byte is calculated at statement, e.g. the first byte: r15 + 0x12475.
int sub_129a0(int arg0, int arg1, int arg2, int arg3) {
rcx = arg3;
var_83 = 0x1;
sym.imp.printf();
sym.imp.fgets();
sym.imp.putchar();
if (((var_80 & 0xff & 0xff) != (*(int8_t *)(r15 + 0x12475) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_7F & 0xff & 0xff) != (*(int8_t *)(r15 + 0x124d8) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_7E & 0xff & 0xff) != (*(int8_t *)(r15 + 0x1223a) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_7D & 0xff & 0xff) != (*(int8_t *)(r15 + 0x1224f) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_7C & 0xff & 0xff) != (*(int8_t *)(r15 + 0x12474) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_7B & 0xff & 0xff) != (*(int8_t *)(r15 + 0x1224f) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_7A & 0xff & 0xff) != (*(int8_t *)(r15 + 0x123a8) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_79 & 0xff & 0xff) != (*(int8_t *)(r15 + 0x12475) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_78 & 0xff & 0xff) != (*(int8_t *)(r15 + 0x1247a) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_77 & 0xff & 0xff) != (*(int8_t *)(r15 + 0x1223a) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_76 & 0xff & 0xff) != (*(int8_t *)(r15 + 0x12245) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_75 & 0xff & 0xff) != (*(int8_t *)(r15 + 0x124ca) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_74 & 0xff & 0xff) != (*(int8_t *)(r15 + 0x12428) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (((var_73 & 0xff & 0xff) != (*(int8_t *)(r15 + 0x124d8) & 0xff)) || (sub_12927(0xa, 0xf, *0x214060, rcx) != 0x0)) {
var_83 = 0x0;
}
if (var_83 != 0x0) {
sym.imp.printf();
}
else {
sym.imp.printf();
}
rax = 0x0;
rcx = *0x28 ^ *0x28;
if (rcx != 0x0) {
rax = sub_12750();
}
return rax;
}
Solution:
#!/usr/bin/env python3
import sys
f = open("dump.bin", "r")
for o in [0x12475, 0x124d8, 0x1223a, 0x1224f, 0x12474, 0x1224f, 0x123a8, 0x12475, 0x1247a, 0x1223a, 0x12245, 0x124ca, 0x12428, 0x124d8]:
f.seek(o)
sys.stdout.write(f.read(1))
35C3_ThisIsATriumph
permute (140p)
Number of solves: 33.
$ ldd program
linux-gate.so.1 (0xf7f9f000)
libcapstone.so.3 => /lib/i386-linux-gnu/libcapstone.so.3 (0xf7cd1000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7af3000)
/lib/ld-linux.so.2 (0xf7fa1000)
The binary loads shared library libcapstone, most likely to to disassemble and assemble a new, patched version of the binary after a execution.
Lets try.
$ sha1sum program; ./program; sha1sum program
501191144bf7984825aa1a472bcce4391a1ce3a7 program
Usage: ./program <flag byte>
9fcac879cff4619a969a20f907eff7358b066403 program
As I assumed.
Lets open the binary in Hopper debugger.
The function call at address 0x08049f9f checks the input argument.
0x080489b9 E8E1150000 call sub_8049f9f ; 0x08049f9f
The XOR operation at 0x0804a013 performs the check of arg1 byte “A”, in the example.
0x0804a013 31D0 xor eax, edx
Lets check the registers… eax is correct byte and edx is the input byte.
[0x0804a013]> dr eax,edx
0x00000033
0x00000041
Due to the self-modifying binary, the address of the XOR changes. Below is a r2pipe1 script solving the challenge.
#!/usr/bin/env python3
import r2pipe
import sys
import subprocess
def main(argv):
while True:
subprocess.call("cp program /tmp/prev", shell=True)
r2 = r2pipe.open("./program")
r2.cmd("doo A; dcu main;")
r2.cmd("dcu %s" % r2.cmd("/a xor eax,edx"))
eax = int(r2.cmd("dr eax").split('\n')[0], 16)
r2.quit()
if eax == 0:
break
sys.stdout.write(chr(eax))
subprocess.call("cp /tmp/prev program", shell=True)
subprocess.call("./program %c" % chr(eax), shell=True)
if __name__ == '__main__':
main(sys.argv)
35C3_tempuemr_temupre
NOTE: Note that using radare2 development branch from github can be buggy if the script does not work…
References
-
https://radare.gitbooks.io/radare2book/content/scripting/r2pipe.html ↩
