<?php
if( isset( $_POST[ 'Submit' ] ) ) { //查看前端传入的post值submit是否为空
// Get input
$target = trim($_REQUEST[ 'ip' ]); //获取get、post、cookie的ip值传参赋值给$target
// Set blacklist
$substitutions = array( //定义数组,变量名为$substitutions
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// Remove any of the charactars in the array (blacklist). //str_replace子字符串替换
$target = str_replace( array_keys( $substitutions ), $substitutions, $target ); //返回数组中部分的或所有的键名
//将传入的$target的‘ip’值里的$substitutions的keys值都替换成$substitutions里的意思(空值)。
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows //查询字符串首次出现的位置,并且忽略大小写 //如果是windows的话
$cmd = shell_exec( 'ping ' . $target );
} // 执行ping命令到$target
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target ); //否则就ping -c 4次 $target
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
以上代码是DVWN靶场的高级等级的命令执行漏洞上的页面代码,下面先说一下整体思路,
这篇代码是由php语言编写的,所以在这段代码里碰到不熟悉的函数,我们需要查找函数对应的意思。可以在网页上查询php语言里的函数语义,有工具的也可以使用工具,比如php手册等。
首先要明白最基础的if,如果if()里的值为真,就执行if下花括号{}里面循环体(也就是代码),反之如果为假,就执行else里的循环体(代码)。等我们查询完函数之后再结合语义带入到if里,大致思路就是这个意思哈,咱们接着往下看。
正文开始,已经提前对一些函数进行了注释,帮助代码审计时进行更好的理解,让我们从第一行代码开始理解:
第一行我们先看到了 if( isset( $_POST[ 'Submit' ] ) ) ,上来就看到了if,表示if里面的值为条件,条件如果为真就执行下面的代码。那么if里面的意思是什么呢?我们先从里面看,$_POST['']的意思,学过php语言的都了解,大致就是从前端通过['']内定义的值传参到后面。通过$_POST的变量可以把你在页面上输入的字符串传到我后端上来。
$_POST外面的isset函数的意思是检查变量是否为空,isset()里面有值的话就为真。下面直接用isset做实验,$_ERQUEST[]表示post和get传参都可以,赋值到一个变量$cnd。再前端url里随便写入一个命令值,输出isset,结果表示为1,说明条件为真。
再看第二行:$target = trim($_REQUEST[ 'ip' ]),trim()函数的意思是取出字符串首尾的空白字符,也就是括号里面的字符串输出之后是没有空格的,下面是trim()使用过程,可以看到我填写了一个都是空格的变量,用trim函数输出之后空格没了。而$_REQUEST的意思和$_GET、$_POST一个意思,从前端传参到我们后端。
我们再来看第三行 $substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
); 很明显,这就是一个关联数组,前面为键,后面为值。
再看第四部分:$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
这一行是重点,虽然看着字母多,但是我们拆开来逐个分析,先看函数里面的array_keys()函数,这个函数的意思是返回数组中部分的或所有的键名。
我们来做个实验,我们定义一个关联类型的数组,然后查看array_keys的数据类型,再定义数组的时候分为键和值,然后我们再数组外面加了一个array_keys()的函数,进行查看,可以看到输出出来的是索引、类型和它的键,也就是这个数组的‘name’、‘age’、‘id’这三个。那么就说明array_keys()的作用就是挑选这个数组的 => 前面的键的值,组成一个数组。右面的图是解析当前的这个数组。可以直观看到:keys()函数返回的是里面数组的键值。
然后再回来看这行的str_replace()函数,他的意思是:子字符串替换。
结合上面几行,如下图所示,第一个参数是keys()的一个数组,第二个参数是一个完整数组,第三个参数是前端传参过来的值(下图为直观改为后端输入的值)。一共有三个参数,可以看到输出之后得知已经没有乱七八槽的特殊符号了。
我们的参数1是要换掉的部分,参数2可以理解为换掉成为什么数字,本函数里是替换成为 ‘ ’ 空值。参数3是写入的整体,1和2是要作用再参数3的身上。
这样我们总结一下上部分的代码含义为:你输入的内容如果是真的,那么需要由代码先来过滤,把你输入的含有& ; | -$ () ` || 的数值转换成空值。
然后我们看下部分的内容,if( stristr( php_uname( 's' ), 'Windows NT' ) ),先解析php_uname('s'),的含义,php_uname官方解读是返回运行 PHP 的系统的有关信息,其中‘s’属性是返回操作系统名称,stristr()的含义是查找字符串的首次出现。如果目前系统是“windows NT”的话,就执行下面循环体的内容,如果不是就执行else里的内容。
我们再来看循环体内的内容:
第一个IF内的内容是执行ping命令,后面的变量跟的是你输入的已经替换过的值。
第二个else的内容为:如果不是就执行4次ping命令跟你输入的已经替换过的值。
所以下部分总结为:先查看你当前操作的系统是否为“windows NT”。如果是的话就直接ping你输入的IP(window系统自动oing4次),如果不是的话就手动命令ping4次你输入的IP
最后输出你输入的IP