Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

OS 命令注入

| , 2 minutes reading.

1. 定義

OS 命令注入 (OS Command Injection)(又稱 Shell 注入)是一種允許攻擊者在運行應用程式的伺服器上執行任意作業系統命令的漏洞。這通常發生在應用程式將未經安全處理的用戶輸入(如表單、Cookie、標頭)傳遞給系統 Shell 時。

它與程式碼注入不同(程式碼注入執行的是 PHP/Node 等應用語言程式碼)。在命令注入中,攻擊者利用的是 Shell(如 bash, sh, cmd.exe)。

2. 技術原理

許多應用程式需要調用底層的作業系統程式(例如,使用 ping 檢查網路狀態或使用 ffmpeg 處理影片)。 如果應用程式使用調用 Shell 的函數(如 C 的 system()、PHP 的 shell_exec() 或 Node 的 exec())並將參數以字串形式拼接:

// 易受攻擊的 Node.js 程式碼
const { exec } = require('child_process');
exec('ping -c 1 ' + userInput, (err, stdout) => { ... });

如果用戶輸入是 google.com,它運行 ping -c 1 google.com。 但如果輸入是 google.com; rm -rf /,Shell 會將分號 ; 視為命令分隔符並執行:

  1. ping -c 1 google.com
  2. rm -rf /(刪除檔案)

注意: Java 的 Runtime.exec() 常被誤解;它預設調用 Shell,這意味著除非開發者顯式調用 /bin/sh -c,否則 Shell 元字元不會被解析。

3. 攻擊流程

sequenceDiagram
    participant Attacker
    participant App as Web 伺服器
    participant OS as 作業系統

    Attacker->>App: GET /dns-lookup?hostname=google.com&#59; cat /etc/passwd

    Note over App: 應用程式拼接字串:<br/>"nslookup google.com&#59; cat /etc/passwd"
    
    App->>OS: 通過 Shell 執行命令
    
    OS->>OS: 1. 運行 nslookup (成功)
    OS->>OS: 2. 運行 cat /etc/passwd (成功)
    
    OS-->>App: 返回組合後的輸出
    App-->>Attacker: 顯示 DNS 結果 + 系統用戶列表

4. 真實案例:Shellshock (2014)

目標: 全球 Linux/Unix 系統(Bash 漏洞)。 漏洞類別: OS 命令注入(環境變數)。 CVE: CVE-2014-6271。

技術細節: Bash 存在一個缺陷:如果環境變數以函數定義 () { :;}; 開頭,它會無意中執行該變數中隨後的命令。 Web 伺服器(如通過 CGI 的 Apache)會將 HTTP 標頭(如 User-Agent)映射到環境變數。

攻擊方式: 攻擊者發送帶有精心構造的 User-Agent 的 HTTP 請求: User-Agent: () { :; }; /bin/cat /etc/passwd

當伺服器處理請求時,它設置了環境變數並觸發了該漏洞,立即執行了 cat /etc/passwd

影響: 數百萬台伺服器遭到入侵。它允許任何公開暴露的使用基於 Bash 的 CGI 腳本、DHCP 客戶端或 SSH 受限 Shell 的系統執行遠端程式碼執行 (RCE)。

5. 深度防禦策略

A. 避免調用 Shell

最有效的防禦是使用程式語言的原生 API,而不是調用外部 Shell。

  • 錯誤做法: exec("mkdir " + dirName)
  • 正確做法: fs.mkdir(dirName) (Node.js) 或 os.mkdir(dirName) (Python)。

B. 使用 execFile 或參數陣列

如果必須運行外部命令,請使用接受參數陣列而非字串的函數。這可以防止 Shell 解釋元字元(如 &, ;, |)。

  • Node.js 範例:

    // 安全:不生成 Shell
    const { execFile } = require('child_process');
    execFile('ping', ['-c', '1', userInput], (error, stdout) => { ... });

    如果 userInputgoogle.com; ls,系統會嘗試 ping 一個字面名為 "google.com; ls" 的主機,最終由於找不到主機而安全失敗。

C. 強力輸入驗證(白名單)

如果必須使用 exec()

  • 根據嚴格的正則表達式(如 ^[a-zA-Z0-9.-]+$)驗證輸入。
  • 拒絕任何包含 Shell 元字元的輸入:&, ;, |, $, >, <, `, !

D. 最小權限原則

確保 Web 應用程式以權限極低的用戶(如 www-data)運行。它不應擁有 root 權限,也不應能寫入敏感目錄。

6. 參考資料