到 RCE)
很多同學(xué)第一次見(jiàn)到 RCERemote Code Execution遠(yuǎn)程代碼執(zhí)行會(huì)以為必須配得上一個(gè)“驚天 0day”。真到業(yè)務(wù)里看事故單起點(diǎn)往往土得令人發(fā)指一個(gè)網(wǎng)絡(luò)診斷頁(yè)把用戶輸入的 IP 拼進(jìn)ping一個(gè)后臺(tái)“測(cè)試過(guò)濾器”把表達(dá)式丟進(jìn)eval一個(gè)運(yùn)維接口把文件名丟進(jìn)Runtime.exec卻還走了 shell。命令注入和代碼注入是兩條最接近“從參數(shù)到 RCE”的直道。它們和 SQL 注入同族——都是不可信輸入進(jìn)了解釋器——只是解釋器換成了系統(tǒng) shell、或語(yǔ)言自己的求值器。一、先把兩個(gè)名詞掰開(kāi)1. 命令注入OS Command Injection應(yīng)用拼出一條要交給操作系統(tǒng)執(zhí)行的命令行用戶輸入改變了命令的結(jié)構(gòu)或追加了新命令。例如本意ping -c 4 用戶輸入的主機(jī)若用戶輸入被拼進(jìn) shell攻擊者可能用命令分隔符塞進(jìn)額外指令讓系統(tǒng)在ping之外還執(zhí)行別的東西。2. 代碼注入Code Injection用戶輸入被送進(jìn)語(yǔ)言/模板/表達(dá)式引擎執(zhí)行例如eval、assert、動(dòng)態(tài)Function不安全的模板引擎SSTI 常單列本質(zhì)近親反序列化觸發(fā)的魔術(shù)方法調(diào)用常單列結(jié)果也是 RCERuntime反射亂調(diào)它不一定經(jīng)過(guò)/bin/sh但一樣能在進(jìn)程里跑任意邏輯。3. 一張對(duì)照表命令注入代碼注入解釋器shell / 命令行語(yǔ)言求值器/模板等典型函數(shù)system、popen、exec走 shell 時(shí)eval、動(dòng)態(tài)代碼編譯經(jīng)典入口ping、轉(zhuǎn)換、打印、備份腳本規(guī)則引擎、計(jì)算器、調(diào)試開(kāi)關(guān)修復(fù)關(guān)鍵不調(diào) shell參數(shù)數(shù)組 API白名單刪除求值沙箱極難做對(duì)下面兩條線都會(huì)寫(xiě)到實(shí)戰(zhàn)里它們還經(jīng)常接力——代碼注入里再拼命令或命令注入里再寫(xiě)文件落地。二、為什么偏偏是 ping 參數(shù)1. 業(yè)務(wù)理由很充分網(wǎng)絡(luò)設(shè)備面板、IDC 管理、監(jiān)控系統(tǒng)、售后診斷頁(yè)都需要“幫用戶 ping 一下”。產(chǎn)品經(jīng)理覺(jué)得這是運(yùn)維友好開(kāi)發(fā)隨手system(ping ip);于是安全問(wèn)題從第一天就埋好了。2. Shell 元字符是引爆點(diǎn)若命令經(jīng)過(guò) shell 解析用戶輸入中的分隔與鏈?zhǔn)椒?hào)就可能改變語(yǔ)義具體哪些字符危險(xiǎn)因 Windowscmd/ PowerShell / Linuxsh而異。教學(xué)與靶場(chǎng)里會(huì)演示“追加命令”的效果真實(shí)授權(quán)測(cè)試只需證明輸入能改變命令語(yǔ)義或觸發(fā)額外執(zhí)行不必上破壞性指令。3. 過(guò)濾為什么總失敗常見(jiàn)假修復(fù)黑名單刪掉;、|、——總有漏網(wǎng)與編碼變體只允許數(shù)字和點(diǎn)——卻忘了 IPv6、主機(jī)名、或旁路參數(shù)位置正則看起來(lái)很?chē)?yán)實(shí)際只校驗(yàn)了部分分支換行、管道、命令替換在不同層被解碼回來(lái)。過(guò)濾命令字符串幾乎注定輸給堅(jiān)持不懈的人。正解是不要用 shell 拼命令。4. 正確實(shí)現(xiàn) ping 的思路嚴(yán)格校驗(yàn)IPv4/IPv6/主機(jī)名白名單規(guī)則使用參數(shù)數(shù)組形式的進(jìn)程 API不經(jīng)過(guò) shell超時(shí)與輸出長(zhǎng)度限制僅管理員可用并審計(jì)能改成“對(duì)固定探針地址探測(cè)”就不要讓用戶指定任意主機(jī)。能做到前兩條ping 頁(yè)從“RCE 溫床”變回“診斷工具”。三、命令注入的一般形態(tài)1. 拼接位置不止主機(jī)名文件名、路徑備份、壓縮、轉(zhuǎn)換郵件地址老腳本發(fā)給mail圖片尺寸、視頻碼率ImageMagick、ffmpeg 命令行打印機(jī)名、共享路徑。審計(jì)時(shí)搜system、popen、shell_exec、Runtime.getRuntime().exec注意重載是否走 shell、ProcessBuilder用法、subprocess的shellTrue、os.system。2. 無(wú)回顯與有回顯有的接口把命令輸出回頁(yè)面——好證。有的只返回成功失敗——要用時(shí)間差、外帶通道授權(quán)下對(duì)你控制的接收端等謹(jǐn)慎證明生產(chǎn)環(huán)境避免大面積盲打。3. 到 RCE 的“短路徑”命令注入一旦成立通常已經(jīng)是 RCE你能執(zhí)行的就是系統(tǒng)命令。后續(xù)是權(quán)限多大www-data 還是 root、能不能寫(xiě) web 目錄、能不能碰云密鑰——那是影響評(píng)估不是“還沒(méi) RCE”。報(bào)告里寫(xiě)命令注入導(dǎo)致遠(yuǎn)程命令執(zhí)行并注明運(yùn)行身份與可達(dá)資產(chǎn)即可。四、代碼注入eval 家族與朋友們1. 為什么開(kāi)發(fā)愛(ài)用 eval規(guī)則引擎、動(dòng)態(tài)公式、功能開(kāi)關(guān)、灰度表達(dá)式、競(jìng)品“腳本化配置”——都想讓用戶或運(yùn)營(yíng)寫(xiě)一段邏輯。于是eval(user_input)或稍加偽裝的動(dòng)態(tài)編譯執(zhí)行。這等于把解釋器端口暴露給輸入。2. SSTI 模板注入近親用戶輸入進(jìn)了服務(wù)端模板引擎并被當(dāng)成模板語(yǔ)法解析。表現(xiàn)像代碼注入常能讀配置或執(zhí)行命令。修法是用戶內(nèi)容當(dāng)數(shù)據(jù)渲染禁止用戶模板進(jìn)服務(wù)端引擎或使用沙箱極嚴(yán)的引擎仍要謹(jǐn)慎。3. 反序列化不可信數(shù)據(jù)反序列化導(dǎo)致任意代碼執(zhí)行——許多報(bào)告直接寫(xiě) RCE。防御是不碰不可信反序列化換安全格式JSON 校驗(yàn)升級(jí)補(bǔ)丁。專題可另寫(xiě)這里只強(qiáng)調(diào)它和“從參數(shù)到 RCE”同一終點(diǎn)。4. 修復(fù)代碼注入能刪 eval 就刪換成白名單操作符的表達(dá)式解析器自己做要極慎重運(yùn)營(yíng)腳本走人工審核發(fā)布不進(jìn)線上動(dòng)態(tài)執(zhí)行語(yǔ)言沙箱不要迷信歷史逃逸太多。五、從“參數(shù)”到 RCE 的鏈路怎么講給領(lǐng)導(dǎo)聽(tīng)① 功能需要調(diào)用外部命令或動(dòng)態(tài)代碼 ② 開(kāi)發(fā)用字符串拼接把用戶輸入塞進(jìn)去 ③ 攻擊者構(gòu)造輸入改變語(yǔ)義 ④ 進(jìn)程以 Web/應(yīng)用身份執(zhí)行任意命令或代碼 ⑤ 讀配置、彈 shell、打內(nèi)網(wǎng)、拿云憑證……打斷點(diǎn)① 換安全 API 或不讓用戶指定② 禁止拼接③ 縱深最小權(quán)限、只讀文件系統(tǒng)④⑤ 檢測(cè)與分段。六、授權(quán)測(cè)試方法1. 發(fā)現(xiàn)功能關(guān)鍵詞診斷、ping、traceroute、nslookup、轉(zhuǎn)換、渲染、打印、備份、升級(jí)包處理。白盒搜危險(xiǎn)函數(shù)與shellTrue。2. 確認(rèn)在授權(quán)環(huán)境提交無(wú)害差異輸入如改變睡眠時(shí)間、回顯固定標(biāo)記證明注入點(diǎn)。避免rm、挖礦、大規(guī)模掃內(nèi)網(wǎng)。3. 影響評(píng)估誰(shuí)身份執(zhí)行能否讀業(yè)務(wù)配置是否容器內(nèi)是否連到生產(chǎn)庫(kù)給出級(jí)別通常是高危/嚴(yán)重。4. 報(bào)告復(fù)現(xiàn)步驟最小化修復(fù)給“參數(shù)數(shù)組示例”與“刪除 eval”的明確建議復(fù)測(cè)標(biāo)準(zhǔn)寫(xiě)清。七、防御工程清單命令執(zhí)行類禁止shellTrue/system拼用戶輸入使用參數(shù)列表 API主機(jī)/文件名等嚴(yán)格白名單應(yīng)用賬號(hào)最小權(quán)限上傳與命令處理隔離代碼執(zhí)行類無(wú)eval/assert處理用戶數(shù)據(jù)模板不渲染用戶模板表達(dá)式引擎白名單化反序列化不接不可信源檢測(cè)Web 日志中的命令分隔特征輔WAF/RASP 規(guī)則輔主機(jī)側(cè)異常子進(jìn)程www用戶起bash/curl參見(jiàn) HIDS 文云側(cè)異常行為。八、常見(jiàn)假修復(fù)與真翻車(chē)“我轉(zhuǎn)義了所有特殊字符?!逼脚_(tái)差異與二次解碼會(huì)打臉且業(yè)務(wù)字符可能被誤傷?!拔矣昧?escapeshellarg?!庇袔椭砸_用法與參數(shù)結(jié)構(gòu)復(fù)雜命令行依舊危險(xiǎn)。能不用 shell 就不用?!皟?nèi)網(wǎng)系統(tǒng)無(wú)所謂?!眱?nèi)網(wǎng) RCE 往往是勒索與漫游的跳板?!叭萜骼餂](méi)事?!比萜魈右菖c云元數(shù)據(jù)故事你我都寫(xiě)過(guò)RCE 仍是事故起點(diǎn)?!爸辉试S數(shù)字?!毕乱粋€(gè)需求就要支持域名一放開(kāi)又拼回去。九、和 SQL 注入、XSS、SSRF 的關(guān)系SQL 注入進(jìn)數(shù)據(jù)庫(kù)解釋器命令/代碼注入進(jìn) OS 或語(yǔ)言解釋器XSS進(jìn)瀏覽器解釋器SSRF進(jìn)服務(wù)端請(qǐng)求器。同一原則數(shù)據(jù)與代碼分離用戶輸入只能當(dāng)數(shù)據(jù)。命令注入常與上傳、文件名、圖片處理鏈組合代碼注入常與“運(yùn)營(yíng)可配置腳本”組合。架構(gòu)評(píng)審時(shí)把這些功能單獨(dú)標(biāo)紅。十、開(kāi)發(fā)規(guī)范可寫(xiě)進(jìn) Wiki 的幾條任何system/exec/eval必須安全評(píng)審默認(rèn)拒絕。子進(jìn)程只允許參數(shù)數(shù)組形式禁止拼接命令行字符串。用戶輸入進(jìn)入子進(jìn)程前必須白名單校驗(yàn)。禁止上線面向用戶的動(dòng)態(tài)代碼執(zhí)行功能內(nèi)部工具也要鑒權(quán)與審計(jì)。發(fā)現(xiàn)一處拼接回歸測(cè)試留樣例。十一、學(xué)習(xí)路徑靶場(chǎng)命令注入關(guān)卡理解分隔與無(wú) shell API 的差異自己寫(xiě)壞再寫(xiě)好一個(gè) ping先拼接再改 ProcessBuilder/參數(shù)數(shù)組搜公司代碼庫(kù)危險(xiǎn)函數(shù)清單出整改工單配一條 HIDS 規(guī)則Web 用戶起 shell 告警預(yù)發(fā)驗(yàn)證。十二、收尾從 ping 參數(shù)到 RCE中間往往沒(méi)有華麗漏洞編號(hào)只有一次“圖省事的拼接”。命令注入把輸入交給 shell代碼注入把輸入交給語(yǔ)言求值器——終點(diǎn)都是別人在你的進(jìn)程里說(shuō)話。防住它們不靠更長(zhǎng)的黑名單靠三樣老實(shí)東西不要用 shell 拼用戶輸入不要 eval 用戶輸入給應(yīng)用最小權(quán)限讓出事時(shí)少丟一點(diǎn)。今晚若只做一件事在代碼庫(kù)搜system(、popen(、shellTrue、eval(打開(kāi)每一處看有沒(méi)有用戶輸入。搜完你對(duì)“我們離 RCE 有多遠(yuǎn)”的判斷會(huì)比聽(tīng)十個(gè)黑客故事扎實(shí)得多。附危險(xiǎn) API 速記按語(yǔ)言自查PHPsystem、shell_exec、passthru、proc_open、eval、assertPythonos.system、subprocess(..., shellTrue)、eval、execJavaRuntime.exec字符串版、腳本引擎Nodechild_process.exec、eval、vm使用不當(dāng)運(yùn)維腳本Bash 里未加引號(hào)的變量展開(kāi)附修復(fù)優(yōu)先級(jí)優(yōu)先級(jí)動(dòng)作P0下線或修復(fù)可觸發(fā)的注入點(diǎn)P1改為無(wú) shell 參數(shù)數(shù)組 白名單P2刪除 eval/動(dòng)態(tài)代碼能力P3最小權(quán)限 子進(jìn)程監(jiān)控