提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
基本的命令注入原理
理解服务器在后台执行系统命令时,如何把用户输入拼接到命令字符串中。
如果输入没有正确过滤,攻击者可以注入额外的系统命令并执行,比如:
127.0.0.1; whoami
简单的注入技巧与命令连接符的使用
熟练掌握各种操作系统的命令连接符:
Linux/Unix下常用:
;
,&&
,|
,||
Windows下也可以使用:
&
,|
在 Windows cmd 中,
|
也是管道符(pipe),把前一个命令的输出作为后一个命令的输入。和Linux一致。在 Windows cmd 中,
&
是顺序执行(不论前一个命令是否成功,都会继续执行后一个命令)。相当于Linux的;(分号)。
Linux中的 :
(冒号)
在 Linux(Shell里),冒号 :
是一个非常特别的内置命令,叫做 "空命令"(null command)。
👉 它的特点是:
什么也不做,但返回成功(退出码0)。
常用于占位、写流程时需要一个“啥也不做但要占个位置”的情况。
127.0.0.1 && cat /etc/passwd
简单绕过手法
比如,直接用替代连接符
&&
或用 URL 编码%26%26
。或者注入拼接无害内容使得原命令完成后继续执行新的命令。
提示:以下是本篇文章正文内容,下面案例可供参考
一、low级别
输入127.0.0.1点击提交出现乱码
在phpstudy中打开网站根目录,找到DVWA中对应的文件 然后把编码形式进行修改,把utf-8改为gb2312
文件路径在dvwa/includes/dvwapage.inc.php
修改完成之后,再点击提交或者刷新页面即可
那么可以尝试用管道符(|)将ping的结果交给管道后面的命令处理,查看可以查看当前用户是谁
尝试用分号去拼接两个命令报错,分号拼接前后两个命令之间没有逻辑关系
在Linux里,命令行中的 &&
是用来串联两个命令的执行关系的,具体来说:
只有当前一个命令执行成功(返回值是0),后一个命令才会执行。
如果前一个命令执行失败(返回非0退出码),后面的命令就不会执行。
所以,&&
在Shell中体现的逻辑是:“如果前面的命令成功了,那么继续执行后面的命令”。
这里&&后面除了用ipconfig查看ip,也可以whoami查看用户、systeminfo查看系统信息,dir查看目录,可以自己尝试
也可以将上面的两个&&替换成winds中一个&表示顺序执行,无论前面的地址是否能够ping通,后面的命令都会执行,但是输入一个ping不通的地址,等待返回超时,需要多等会儿。而这时候如果这个地址ping不通,是不能用&&的,两个&&链接的命令是有逻辑关系的,前面命令成功才会执行后面的命令,前面地址ping不通,那么&&后面的命令也不执行。
那么如果前面地址ping不通的情况下,使用||表示逻辑或
在 Linux (比如 bash/zsh 这些Shell)中,
||
是逻辑或(OR)操作符,意思是:
如果前一个命令失败(退出状态码非0),那么就执行后一个命令;
如果前一个命令成功(退出码是0),那么后一个命令不会执行。
以下情况由于地址192.168.10.1ping不通(执行失败),那么执行||后的命令whoami
low级别源码分析
<?php
if( isset( $_POST[ 'Submit' ] ) ) { // 如果用户提交了表单(点击了Submit按钮)
// Get input
$target = $_REQUEST[ 'ip' ]; // 从请求中获取ip参数(可能来自GET或POST)
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // 判断服务器操作系统是不是Windows
// Windows
$cmd = shell_exec( 'ping ' . $target ); // Windows系统下执行 ping 命令
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target ); // Linux/Unix系统下执行 ping 命令,加上参数-c 4表示只发送4次
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>"; // 把ping命令的执行结果输出到页面上,并用<pre>标签保留格式
}
?>
这里隐藏的漏洞点:
$target
直接拿用户输入,没有经过任何过滤(比如禁止特殊字符&
、|
、;
之类的)。shell_exec()
是直接调用操作系统的Shell命令执行函数。所以如果用户在
ip
参数里不仅传IP,还加了| whoami
,那么整个Shell执行的命令就变成了:Windows:
ping 127.0.0.1 | whoami
Linux:
ping -c 4 127.0.0.1 | whoami
结果就可以直接命令注入执行了!
二、medium级别
有了前面low级别的基础,直接来分析源码
<?php
if( isset( $_POST[ 'Submit' ] ) ) { // 如果用户提交了表单(点击了Submit按钮)
// Get input
$target = $_REQUEST[ 'ip' ]; // 从请求中获取 'ip' 参数的值,可能来自 GET 或 POST 请求
// 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
// Windows
$cmd = shell_exec( 'ping ' . $target ); // 如果是Windows,执行 ping 命令,ping目标是用户提供的IP地址
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target ); // 如果是 Linux 或 Unix 系统,执行带有 -c 4 参数的 ping 命令,指定只发送4个包
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>"; // 将命令执行结果输出到页面,保留格式输出(<pre>标签保持换行和空格)
}
?>
代码分析:
黑名单过滤:
通过创建一个
$substitutions
数组,代码尝试过滤掉可能导致命令注入的字符,比如&&
(命令链)和;
(命令分隔符)。这是为了防止用户在输入中注入多个命令。但是,这个方法并不完全安全,因为还有其他特殊字符或者更复杂的注入方式(如管道符
|
)没有被拦截。
操作系统判断:
使用
php_uname('s')
判断操作系统是 Windows 还是类 Unix 系统,然后分别执行不同的ping
命令。这个判断是根据操作系统不同选择不同的命令格式,这本身是合理的。
命令执行:
shell_exec()
函数用来执行拼接出来的命令,并返回命令的输出。如果用户输入的
ip
包含不安全的字符,str_replace()
可能可以在一定程度上避免注入,但是这并不是一个完整的安全解决方案,因为它只过滤了少数几个字符。
用户反馈:
命令执行结果会被输出到网页上,使用
<pre>
标签进行格式化显示。这使得用户能够看到ping
命令的执行结果。
注入方法
使用不在黑名单里面的命令连接符,比如 | 或者 || 或者 & ,同样可以拼接前面的命令
三、high级别
源码分析
<?php
if( isset( $_POST[ 'Submit' ] ) ) { // 如果用户提交了表单(点击了Submit按钮)
// Get input
$target = trim($_REQUEST[ 'ip' ]); // 获取用户提交的IP地址,并用trim去除前后空格
// Set blacklist
$substitutions = array( // 定义一个黑名单,把一些危险字符列出来
'&' => '', // 单个&,用于连接命令,清除
';' => '', // 分号,能分隔多个命令,清除
'| ' => '', // 管道符后带空格的,清除
'-' => '', // 减号,可能用来传参数,如ping命令参数,清除
'$' => '', // 美元符号,能用来执行如$(命令)或环境变量,清除
'(' => '', // 左括号,配合$用于子命令执行,清除
')' => '', // 右括号,配合$用于子命令执行,清除
'`' => '', // 反引号,可执行反引号内的命令,清除
'||' => '', // 双竖线,逻辑或连接符,可分开执行命令,清除
);
// 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(根据系统名中是否有"Windows NT"判断)
// Windows系统下执行ping命令
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix系统(比如Linux)下执行ping命令,加参数-c 4,表示ping 4次
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>"; // 把命令执行结果用pre标签包裹,格式化输出到网页上
}
?>
仔细看这里的 '| '后面有一个空格,所以依然可以用 | 这个注入。我们之前在注入的时候
127.0.0.1 | whoami #|后面是有个空格的,是可以成功的
这次high级别写的时候|后面不要加空格,加上空格会被黑名单过滤
简单总结一下:
这一版比
medium.php
更加严格了,过滤了更多危险字符。尤其是把单独的
&
、|
、反引号`
、美元符号$()
等都清除了。但它依然是基于黑名单过滤,理论上还是可能被绕过(比如编码、空格变形、双写等手法)。
四、impossible级别
源码分析
<?php
if( isset( $_POST[ 'Submit' ] ) ) { // 如果点击了提交按钮
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // 检查CSRF防护令牌,防止跨站请求伪造攻击
// Get input
$target = $_REQUEST[ 'ip' ]; // 获取用户提交的IP地址
$target = stripslashes( $target ); // 去除字符串中的反斜杠(如果有)
// Split the IP into 4 octects
$octet = explode( ".", $target ); // 按点号"."分割成4段(四个8位字节)
// 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 ) ) {
// 如果每一段都是数字,且总共有4段,说明输入的是合法IP格式
// If all 4 octets are int's put the IP back together.
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3]; // 将IP重新组合成标准格式
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// 如果服务器是Windows系统
$cmd = shell_exec( 'ping ' . $target ); // 执行ping命令
}
else {
// 否则认为是Linux或Unix系统
$cmd = shell_exec( 'ping -c 4 ' . $target ); // 执行ping命令并指定4次
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>"; // 把ping结果输出给用户
}
else {
// 如果输入的IP不合法
echo '<pre>ERROR: You have entered an invalid IP.</pre>'; // 提示错误信息
}
}
// Generate Anti-CSRF token
generateSessionToken(); // 重新生成新的CSRF防护令牌
?>
🛡️ 过滤方式 | 只允许格式正确的IP地址(四段数字,用"."分隔),其他任何输入直接拒绝。 |
🛡️ CSRF防护 | 使用了token机制防止跨站请求伪造。 |
🛡️ 命令执行点 | 只有经过严格校验的纯数字IP才能进入执行流程,避免了注入。 |
🔥 注入可能性 | 理论上基本没法注入(除非PHP本身或服务器存在其他漏洞,比如某些超老版本PHP漏洞) |