
0x01 Low级别
<?php
// 判断用户是否提交表单
if( isset( $_POST[ 'Submit' ] ) ) {
// 获取表单中提交的目标ip
$target = $_REQUEST[ 'ip' ];
// 返回判断PHP运行平台的系统类型 如果是Windows则执行 否则执行else中的
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix *nix表示Union和Linux操作系统
// 执行系统命令ping并发送4次ICMP包
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// 将输出结果返回到页面
echo "<pre>{$cmd}</pre>";
}
?>
通过审计代码发现,并没有对我们传入的ip参数进行过滤,这就导致了命令执行的漏洞。在window中有很多管道符,可以帮我们在一行中执行多个语句。
0x01.2 Windows 中的管道符
" && " 如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句只能为真。例如:ping 127.0.0.1&&whoami
" & " 如果前面的语句为假则直接执行后面的语句,前面的语句可真可假 如 ping 127.0.0.1&whoami
" || " 只有前面的语句出错 才会执行后面的语句 如 123 || ls
" | " 执行管道符后面语句例如:ping 127.0.0.1|ls
0x01.3 Linux中的管道符
" ; " 执行完分号前面的语句再执行分号后面的语句。如ping -c 1 127.0.0.1;ls
" | " 显示后面语句的执行结果 例如:ping 127.0.0.1|ls
"||" 当前面的语句出错时,执行后面的语句,例如:ping a||ls
" & " 将&前的语句转为后台运行,然后再执行后面的语句 例如 whoami&ls
" && " 如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句只能为真。例如:ping -c 1 127.0.0.1&&whoami
被``包裹起来的英文,可以当做命令进行执行
了解到上述管道符的用法之后,我们尝试在本关应用
以下DVWA靶场基于Macos系统搭建
127.0.0.1 || whoami

我们使用到了||或管道符,它的特点就是只有前面的127.0.0.1执行错误,才会执行我们的whoami命令
于是我们看到ping 127.0.0.1 成功执行了,所以不会执行我们的whoami命令。
于是我们尝试让||的语句故意执行出错,从而执行我们的whoami命令
a || whoami
众所周知,一个a字符不管对于ip地址来讲还是对于域名来说。格式都是非法的。所以会执行错误,于是我们就成功执行了whoami命令,返回了我们当前计算机登录的用户名

对于后端程序员而言,它只是单纯的想让我们传入一个ip地址,之后执行ping命令,判断网络连通性。这个表单本来传的是一个ip地址参数,由于没有经过严格的过滤,被我们利用。假设我们传入的表单数据为a || whoami 经过后端的字符串拼接为ping -c 4 a || whoami,由于macos是基于Unix开发的,一般被识别为Unix。所以执行了else中的拼接。之后经过shell_exec()处理,成功执行了我们系统的shell命令。shell_exec是一个可以调用系统命令的函数。
它实际执行的语句为

我们可以看到ping命令抛出了一个错误 Unknown host。于是就执行了whoami这个命令。
其他的管道符可以自行尝试。这里不再演示。
0x02 Medium 级别
<?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' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// 将命令执行的结果输出到页面
echo "<pre>{$cmd}</pre>";
}
?>
这一关,与上关基本一致。只是本关做了一些过滤,但是过滤的不完整。从而仍旧存在命令注入。
我们可以发现,它过滤了&&和; 但是没有过滤其他的管道符,比如|、&、||等等。
于是我们尝试使用 & 进行绕过
127.0.0.1 & whoami

刚刚前面有提过,不管&前面的ping命令是否执行成功,都会去执行后面的whoami。也就是说两个都可以被执行
以下是针对不同系统的管道符&讲解:
- windows系统:刚刚前面有提过,不管&前面的ping命令是否执行成功,都会去执行后面的whoami。也就是说两个都可以被执行
- *nix系统: 而*nix系统中的&符号是将一个程序以后台的方式运行,ping -c 4 127.0.0.1 & whoami 也就是说将ping命令挂后台运行了,于是先执行了我们的whoami命令,之后又执行了ping命令。
0x03 Hight 级别
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// 获取用户输入的值
$target = trim($_REQUEST[ 'ip' ]);
// 设置黑名单
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// 将用户输入的值进行替换 如果存在黑名单中的值,则被替换为空
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// 判断操作系统类型
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// 将命令执行结果输出到页面
echo "<pre>{$cmd}</pre>";
}
?>
通过审计代码发现,过滤的很严格,几乎过滤了我们常用的管道符。但是我们要注意在代码中的第12行。即 | 我们注意到它这里过滤的是| ,注意"| "后面有一个空格。实际上它过滤的是"|+空格",所以我们只需要在传入参数的时候在|后面不加空格就可以绕过了。
127.0.0.1|whoami

管道符|不管是在Linux或者windows中都是会执行|管道符前后的命令,但是它只会回显管道符|后面的命令的执行结果,不会回显管道符前面命令的执行结果(但是它可以被执行,只是不会有结果)。
0x03.2 管道符|在*nix中的实验:

可以看到管道符|后面的成功被执行了并且返回了结果,那么管道符前面的ls命令是否执行成功了呢?
我们再次实验 通过创建文件的方式。判断管道符前面的命令是否被执行
我们输入功能为 创建一个名为123.txt文件,并在文件中写入123的命令,然后使用管道符|在后面追加了whoami命令。
前面测试到whoami可以被执行并且有回显。

通过实验得出,即使管道符前面的没有回显结果,但是它成功的并执行了。我们可以看到创建了123.txt文件,并在里面写了内容123。
0x03.3 管道符|在Windows中的实验:
发现和*nix的结果一致,创建了test.txt文件并且内容为"test"


0x04 impossible 等级
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// 检查用户的token值是否与后端一致
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 获取用户输入的IP值
$target = $_REQUEST[ 'ip' ];
// 删除用户输入的值中的反斜杠 如 \
$target = stripslashes( $target );
// 将用户传入的IP以点分隔为一个数组。
$octet = explode( ".", $target );
// 判断用户输入的值是否是一个数字,并且需要是4位。就比如172.16.1.10 以点进行分隔,就可以分隔为四位,这是正常的ip地址
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// 将打散之后的数组重新拼接成一个字符串
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// 检查PHP的运行平台操作系统
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// 将执行结果输出到页面
echo "<pre>{$cmd}</pre>";
}
else {
//如果用户输入的不是一个合法的ip地址 则输出下面这句话
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// 创建token值
generateSessionToken();
?>
我们引用DVWA-BruteForce文章对token进行简单的描述:

也就是说即使用户进行了传参操作,但是如果用户前端页面的token和后端的不一致。可能就不会执行ping命令。
这个token值是隐藏在form表单中的,除了传IP参数的input以外,还有一个传入token的input,只不过这个input是隐藏的,其中的value值是通过后端页面生成并传给前端的页面的。当我们点击submit的时候,这个token值会跟随IP参数一并传给后端进行验证

除了检查token以外,它还将用户输入的值以点进行分隔,一个合法的ip地址,是需要满足点分十进制的。并且IP地址的十进制是一个以点进行分隔的四位数字。那么它分隔之后又将我们传入的ip值进行逐个排查,检查是否是数字类型,并且是一个四位数。只有满足这个条件的时候,我们的ping 命令才会被执行,于是就有效的防止了命令注入的风险。
0x05 命令注入漏洞原因
当应用需要调用一些外部程序时会用到一些系统命令的函数。应用在调用这些函数执行系统命令的时候,如果将用户传入的值作为系统命令的参数,拼接到字符串中,就很有可能存在命令执行漏洞
0x06 命令注入的危害
1) 继承web服务器的权限,执行系统命令
2) 继承web服务器的权限,读取重要文件
3) 反弹shell
4) 控制整个网站乃至于控制整个服务器
0x07 命令注入的防护
1) 对用户传入的参数进行严格的过滤,过滤一些特殊的符号 若干 &、&&、\、||、|、/、;、等等
2) 结合实际的应用场景,将参数的特征进行反复研究,制造一个针对理想型参数执行的if语句。
3) 尽量不要将命令执行的参数,作为用户可动态控制的参数
4) 判断用户的token值
本文作者为blog,转载请注明。