Thinkphp漏洞跟踪

2018-12-18 8,723

漏洞分析环境:https://github.com/vulnspy/thinkphp-5.1.29与phpstudy php-7.0.12-nts+apache

Thinkphp框架中url的访问为index.php/模块/控制器/操作,index.php称为入口文件(pathinfo方式的URL访问),也可使用兼容模式,通过变量s传参/模块/控制器/操作。

图片1.png

图片2.png

图片3.png

根据参考链接中的关键代码,追溯漏洞源文件/thinkphp/library/think/route/dispatch/Module.php,如下图所示:

图片4.png

继续跟踪result数组,如下图所示:

图片5.png

发现调用父类init函数,module类继承于抽象类Dispatch,追踪Dispatch如下图所示:

图片6.png

观察判断语句,通过伪变量this对象引用到rule.php文件中,执行doAfter函数,获取路由的后置操作。如下图所示:

图片7.png

这里稍微引入一下路由后置操作简介,至于大佬可以加以补充,小弟就不献丑了

Thinkphp路由为三种模式:

1、普通模式(采用默认的pathinfo方式url):'url_route_on' => false

2、混合模式(该方式下面,只需要对需要定义路由规则的访问地址定义路由规则,其它的仍然按照默认的PATH_INFO模式访问URL):'url_route_on' => true

3、强制模式(必须定义路由才可访问):'url_route_must' => true 'url_route_on' => true

Thinkphp 5.0以上版本对新的路由功能做了新的增强,支持路由到模块(模块/控制器/操作)、控制器(控制器类/操作)、类(任何类库),也是此次漏洞的原因之一

根据以上描述,分析此次漏洞环境代码,采用为混合模式,在采用s变量获取,触发漏洞。分析获取pathifo的过程,如下图所示:

图片8.png

第一步先判断pathinfo是否有兼容模式的参数,第二步分析pathinfo信息,将ORIG_PATH_INFO,REDIRECT_PATH_INFO,REDIRECT_URL三种模式循环赋值给变量$type,使用server函数,将变量$type转换为大写存入 server数组中,当变量server数组已被赋值,则返回server数组,否则返回空,如下图所示:

图片9.png

最终当变量pathinfo不为空时,$pathinfo为删除/符号前后空白字符的字符串,追溯pathinfo函数,找到path函数,如下图所示:

图片10.png

跟踪path函数,找到App.php文件中的routeCheck函数,如下图所示:

图片11.png

可以观察到此函数返回一个Dispatch对象,而module文件中$result = $this->dispatch,继续跟踪Check函数,如下图所示:

图片12.png

返回值为创建的新对象UrlDispatch,跟踪UrlDispatch类,发现为Url的别名引用,如下图所示:

图片13.png

跟踪Url类,发现继承于抽象类Dispatch,再由魔术方法__construct在方法被实现时用,观察此方法,如下图所示:

图片14.png

此方法用伪变量this将dispatch对象赋值为$dispatch,返回观察Url类,如下图所示:

图片15.png

init方法返回到新对象Module的init方法中,也就是Module.php中parent::init(),其中dispatch通过parseUrl函数赋值给$result,观察parseUrl,如下图所示:

图片16.png

变量module由getConfig函数获取app_multi_module的值,观察得知app_multi_module为真,得到删除第一个数组的变量$path,最后返回一个封装路由route,包含变量module、controller和action,并传递给变量result。

通过以上分析得到变量result的生成结果,当使用explode分割字符串时,输入/index/\think\request/cache,得到如图所示:

图片17.png

通过上述删除第一个数组,赋值给module.php中的变量module,如下图所示:

图片18.png

此时变量module为index,继续观察找到控制器变量controller,如下图所示:

图片19.png

此时变量controller为\think\request,继续观察找到操作名变量actionName,如下图所示:

图片20.png

此时变量actionName为cache,最后进入请求操作,跟踪到操作器controller文件中,找到cache方法,如下图所示:

图片21.png

通过特殊构造达到执行phpinfo的效果,将变量key设置为1|phpinfo,跳过判断是否存在于匿名类中,并通过true===1,进入list函数将变量key和fun分别赋值为1和phpinfo(根据php-7.0.12-nts环境,list赋值为从右向左),从而达到运行phpinfo函数。

第二个poc为:

/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=php%20-r%20'phpinfo();'

也等同于:

/index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=php%20-r%20'phpinfo();’

跟踪到invokefunction函数,如下图所示:

图片22.png

通过将function设置为call_user_func_array,vars[0]=system,即可生成system()函数,通过vars[1][]对上述call_user_func_array返回的回调函数system设置参数变量,达到运行系统命令。

参考链接:

http://www.vulnspy.com/cn-thinkphp-5.x-rce/thinkphp_5.x_(v5.0.23%E5%8F%8Av5.1.31%E4%BB%A5%E4%B8%8B%E7%89%88%E6%9C%AC)_%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%EF%BC%88getshell%EF%BC%89/

https://www.jianshu.com/p/73ed6e42d389



本文作者:二胖

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/93431.html

Tags:
评论  (1)
快来写下你的想法吧!

二胖

文章数:4 积分: 58

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号