HackTheBox-Challenges Gunship Writeup
Intro
這次要來做的是一題 Web 而且有附上 Source Code 還有 Dockerfile,網站畫面很漂亮,只有一個輸入點。
這題雖然標非常簡單但個人認為有很多值得我們探討的知識點,一起來研究吧。
Exploitation
這題的檔案結構如下:
1 | ╰─ tree . ─╯ |
程式碼不多,其中特別直得注意的是 index.js。
1 | const path = require('path'); |
其中特別值得拿出來說明的是:
1 | router.post('/api/submit', (req, res) => { |
上面這段就是唯一使用者輸入的 post 請求,unflattern 則是會把平面化的資料還原成嵌套的物件,這代表 req.body 可以包含類似 {"artist.name": "Haigh"}
這樣的平面資料,透過 unflatten 轉換成 { artist: { name: "Haigh" } }
。
然後 const { artist } = unflatten(req.body);
會從中提取 artist 字段。
再來是檢查條件:
1 | if (artist.name.includes('Haigh') || artist.name.includes('Westaway') || artist.name.includes('Gingell')) { |
會檢查是否包含特定名字,符合條件就會使用 pug.compile
生成 HTML 內容,並傳入一個變數 user(值為 ‘guest’),產生類似 Hello guest, thank you for letting us know! 的訊息。
那上面提到的這兩段其實是有 Prototype Pollution 的風險,因為這段程式碼使用 unflatten 解析使用者輸入的 req.body,而如果使用者輸入了特殊鍵值(如 proto 或 constructor),在解展後可能改變 Object.prototype 的內容,造成原型污染。舉例:
1 | // 攻擊者如果輸入: |
Exploit
首先我們的首要目標是要觸發 pug.compile
,因為我們可以試圖透過 prototype pollution 讓 pug 在渲染的時候使用被我們污染的 block 物件,執行 execSync()
之類的指令操作。
所以我們可以透過 burp 去攔截 /api/submit 的封包,然後把 content 換成:
1 | { |
然後正常來說我們是看不到輸出的,所以有些人可能會透過 reverse shell 來解這題,但這裡提供一個有趣的解法是,我們可以透過執行 $(ls | grep flag)
讓指令執行的結果變成不能運行的指令,然後跟著錯誤訊息印出來,舉例像是如果我們執行:
1 | { |
我們會得到:
1 | Error: Command failed: kazma<br>/bin/sh: kazma: not found<br> on line 1<br> |
所以在這邊 kazma 被當成指令執行,所以根成錯誤訊息印出來,同樣的我們其他指令執行的結果也會被當成無法執行的指令印出來,就可以看到輸出了,所以我們接著來拿 flag:
1 | { |
我們送出上面的請求可以拿到下面的回應:
1 | Error: Command failed: $(ls | grep flag)<br>/bin/sh: flagvAbC2: not found<br> on line 1<br> |
所以檔案名稱是 flagvAbC2
,不過我們直接 cat flag* 也可以:
1 | { |
得到:
1 | Error: Command failed: $(cat flag*)<br>/bin/sh: HTB{wh3n_lif3_g1v3s_y0u_p6_st4rT_p0llut1ng_w1th_styl3!!}: not found<br> on line 1<br> |
Pwned !!!
References
- Title: HackTheBox-Challenges Gunship Writeup
- Author: kazma
- Created at : 2024-10-30 22:49:00
- Updated at : 2024-10-31 00:10:09
- Link: https://kazma.tw/2024/10/30/HackTheBox-Challenges-Gunship-Writeup/
- License: This work is licensed under CC BY-NC-SA 4.0.