[2023蓝帽杯] lovePHP

题目

<?php
class Saferman{
    public $check = True;
    public function __destruct(){
        if($this->check === True){
            file($_GET['secret']);
        }
    }
    public function __wakeup(){
        $this->check=False;
        echo $this->check;
    }
}

if(isset($_GET['my_secret.flag'])){
    unserialize($_GET['my_secret.flag']);
}else{
    highlight_file(__FILE__);
}

解题流程

PHP变量解析

由于PHP的机制,直接传入参数my_secret.flag,其中.这种特殊字符会被转化为_。经过测试输入my[secret.flag只有第一个[会被转化,而之后的.会保留。

反序列化wakeup绕过

所用的方法是C代替OC:8:"Saferman":0:{},虽然这种方法无法添加属性的内容,不过$check 初始就是True,所以也无所无所谓就是。

php://filter 无回显读文件

太复杂了就看个大概。

关键点:

  • 长字符串使PHP报错:convert.iconv.L1.UCS-4LE过滤器会将字符串长度扩大4倍,对一个非空字符串多次应用这个过滤器,它会快速达到 PHP 的内存限制,并引发一个 500 错误。
  • dechunk 过滤器:dechunk 过滤器在处理没有新行的字符串时,会根据字符串的开始字符进行操作。如果字符串以 A-Fa-f0-9 中的任何字符开头,它会清空整个字符串;否则,它会保持字符串不变。

开头不在此范围,正常输出

开头字符为a,输出空

结合这两个特点可以根据服务器响应的状态码判断字符具体的值,整个工作有三步:

  1. 使用convert.base64-encode转化flag,使字符串只有a-zA-Z0-9
  2. convert.iconv.CSUNICODE.UCS-2BE反转字符串每两个字符,convert.iconv.UCS-4LE.10646-1:1993反转字符串每四个字符。用这两个过滤器来将每一位反转到字符串的开头逐步猜解。
  3. 然后就是判断每一位具体的值,使用string.rot13和各种过滤器组合,以dechunkA-Fa-f0-9 这个判断区间移动字符,根据状态码判断出具体的字符。

脚本:DownUnderCTF2022 minimal-php