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. 参考资料