漏洞流程分析
- 入口文件
App.php
中的run()
,实例化了一个$request
对象传给了routeCheck()
- 跟进
routeCheck()
,查看路由检测规则 - 跟进
check()
,$method
通过$request->method()
获得,$rules
根据$method
获得不同的路由规则 - 跟进
method()
,外部传入的Config::get('var_method')
可控,其中var_method
是表单请求类型伪装变量,传入_method
即可进入判断。$_POST['_method']
的值赋值给$this->method
,然后动态调用$this->{$this->method}($_POST)
。这意味着可以调用该类任意函数并以$_POST
作为第一个参数。如果动态调用__construct
函数,则会导致代码执行 - 在
Request类
的构造方法中,$options
可控,因此可以覆盖该类的任意属性。其中$this->filter
保存着全局过滤规则 - payload种用的路由是
captcha
,他的路由规则为get
。因此需要让$this->method
返回值为get
,所以payload中有个method=get
,然后才会取出self::$rules[$method]
的值给$rules
- 继续查看入口文件的路由检测之后,可以看到执行了
exec()
- 在
exec()
中查看method
- 跟进
param()
,$this->param
通过array_merge
将当前请求参数和URL地址中的参数合并。 - 跟进
input()
,该方法用于对请求中的数据即接收到的参数进行过滤,而过滤器通过$this->getFilter
获得 - 跟进
getFilter()
,$this->filter
为system
,回到input
,因为data为数组,因此可以进入if条件调用array_walk_recursive($data, [$this, 'filterValue'], $filter)
,对$data
中的每一个值调用filterValue
函数 - 跟进
filterValue()
,间接调用call_user_func
,且两个参数可控,造成RCE
1 | ?s=captcha |
payload:
1 | /home/index.php?s=/home/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls -l |
5.0.0-5.0.24
1 | index.php?s=captcha |