转自公众号 长白山攻防实验室, 原文链接:
该漏洞对于5.1及5.0版本的影响较大,6.0版本默认不会开启多语言检测。
出品|长白山攻防实验室(ID:A_Tree)
0x00
声明
以下内容,来自长白山攻防实验室的A_Tree作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。
0x02
漏洞介绍
ThinkPHP是一个开源免费的,快速、简单的面向对象的轻量级PHP开发框架,是为了敏捷WEB应用开发和简化企业应用开发而诞生的。
近日,监测到其出现文件包含,代码执行漏洞,漏洞编号:QVD-2022-46174,当ThinkPHP开启了多语言功能时,攻击者可以通过lang参数和目录穿越实现文件包含,当存在其他扩展模块如 pear 扩展时,攻击者可进一步利用文件包含实现远程代码执行。
0x03
漏洞类型
文件包含,代码执行
0x04
漏洞影响范围
ThinkPHP 6.0.1—6.0.13,5.0.x,5.1.x
0x05
漏洞复现
漏洞复现环境:
GITHUB开源项目:
基于ThinkPHP5.0搭建的NIUSHOP开源建站系统。
环境链接:https://github.com/znlccy/niushop
此系统默认没有开启ThinkPHP多语言功能,需要做出如下修改。
修改/application/config.php文件中的lang_swit-ch_on参数为Ture
修改后再主页测试全局lang参数是否生效:
?lang=zh-cn
?lang=en-us
上述两个请求,发现语言进行了切换,环境即搭建成功。在网站根目录创建phpinfo文件作为测试包含文件。
访问URL:?lang=..\..\info
执行info文件,漏洞利用成功。
0x06
漏洞分析
先看PHP官方补丁内容:在think5中修改的文件定位在了think\lang.php的detect方法,定位漏洞位置。
观察未修复的漏洞文件:在detect()其结果为定义了一个self::$range,通过源码分析可得,此参数受到下列参数影响:
我们将其中:$_GET[self::$langDetectVar]输出出来,验证参数内容的可控程度,其参数内容完全可控,并且detect的返回值完全由lang参数决定。
我们继续跟进调用Lang::detect()的文件内容:App.php
App.php文件内容:在config中的多语言机制开启时(lang_switch_on = true)执行了detect()将self::range通过URL中发lang参数定义,我们在上面分析过了,接下来分别执行了range()方法和load()方法。
我们首先跟进range()方法:在App.php中调用range为Lang::range() 因此,range()此时直接return了self::$range此参数在detect中被URL的lang参数控制。
Langset(Lang::range()):继续跟进如langset方法,此时$lang为URL中的lang(污点参数)
非空后将类中私有langset进行赋值,并且$this全部返回。
接下来执行Lang::load()方法:在进入方法前,其中传入load()的变量中,$request->langset()为我们URL中的lang(污点参数)。
跟进lang():可见file参数被转换为列表后进行遍历,当遍历的路径存在时便include目标,file中的内容为我们可控内容,即导致文件包含。
将列表file输出,方便观察:可见内容为从application\lang路径和think\lang路径加上我们URL中lang参数的内容,但是需要我们的此时lang参数为..\..\info.php但是在file列表的内容为info.php.php因此我们不能加上php的后缀。
那么此时我们只需要保证is_file($_file)为真即可包含实现文件包含漏洞。
?Lang = ..\..\info
0x07
总结
此漏洞造成原因相对简单,就是输入参数过滤不严格,修复排查方案为查看当前网站源代码中app目录下的config文件中lang_switch_on是否True即可,如果涉及到语言切换的需要,那么就更新thinkPHP或者手动改一下Lang:: detect()中的源码也行。See Bay!