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