這次挑了一個在台灣電腦網路危機處理暨協調中心看到的漏洞,是跟ASUS路由器的OpenVPN設定有關,在匯出設定時有可能會觸發此漏洞,不過並沒有詳細描述如何執行
RT-AX88U
小於3.0.0.4_388_23748所有版本
今天會著重介紹在externally-controlled format string本身而不是漏洞內容(TVN也沒有公佈太多細節🥲)
先從一段Express.js的程式開始
const app = require("express")();
app.get("/unauthorized", function handler(req, res) {
let user = req.query.user;
let ip = req.connection.remoteAddress;
console.log("Unauthorized access attempt by " + user, ip);
});
const server = app.listen(8081, function () {
const host = server.address().address;
const port = server.address().port;
console.log("Example app listening at http://%s:%s", host, port);
});
上面這段Code會開一個Endpoint在/unauthorized,並且將訪問者的User跟IP打印出來
如果我們今天照著他規則走的話(在這邊假設User id是87)
後端就會看到對應的紀錄
那如果今天故意丟一個他沒想到的輸入,比如說%d
這個時候再到後端看,User變成NaN了🤯
如果這種情況發生在log紀錄可能還有其他地方可以trace,但是如果是在商業邏輯處理或者驗證時出現的話,可能會發生想不到的災難。
回到上面的Code,這是因為我們在User帶入了%d
, 因此與前面的字串合併了從Unauthorized access attempt by
變成Unauthorized access attempt by %d
。
那麼大家都知道%d
代表這個地方要塞的是一個數字,但是我們只剩下一個IP這個參數可以用,但是他一定不是數字,所以就會被轉成NaN
了
// 這串也會吐NaN回來
Number(req.connection.remoteAddress)
我們原本想要記錄下來的事件也消失了💨
既然知道了輸入可能會被惡意更換成沒辦法預期的,那麼我們有沒有辦法可以預防?
當然有,就Express.js能使用的,有下面兩個
他們不僅可以提供驗證傳入資料格式正確性,也可以在需要的時候進行額外檢查(像是User ID是否存在、檢查額外權限等)
我們這次用Express Validator示範
const express = require("express");
const app = require("express")();
const { query, validationResult } = require('express-validator');
// Parse incoming request into json
app.use(express.json());
app.get("/unauthorized", query('user').isInt(), function handler(req, res) {
// Validate input
const validResult = validationResult(req);
let user = req.query.user;
let ip = req.connection.remoteAddress;
// Empty means there's no validation error
if (validResult.isEmpty()) {
console.log("Unauthorized access attempt by " + user, ip);
return res.status(403).send({ msg: 'Unauthorized' });
}
return res.status(400).send({ errors: validResult.array() });
});
const server = app.listen(8081, function () {
const host = server.address().address;
const port = server.address().port;
console.log("Example app listening at http://%s:%s", host, port);
});
在上面我們在/unauthorized
path後面加上了檢查user必須為number的條件(這邊後面你想檢查多少欄位就可以接多少)
這次,%d
沒有辦法順利通過了,得到的會是express-validator傳回來的錯誤