一、showdoc sqli to rce漏洞利用思考
漏洞版本
sqli<=3.2.5
phar反序列化<=3.2.4
漏洞分析
前台sqli
补丁:github.com/star7th/show...
补丁对比显示,在server/Application/Api/Controller/ItemController.class.php中,$item_id变量从拼接方式变为参数绑定,可能存在sql注入。
在server/Application/Api/Controller/ItemController.class.php的pwd方法中,从请求中获取item_id参数,拼接到where条件中执行,无鉴权,判断为前台sql注入。
但在注入点之前,需从请求中获取captcha_id和captcha参数进行验证码验证,每次触发注入前需提交验证码。
验证码逻辑为根据captcha_id从Captcha表中获取未超时验证码进行比对,验证后设置验证码过期。
完整拼接的sql语句:SELECT* FROM item WHERE(item_id='1') LIMIT 1
$password和$refer_url参数可控,通过联合查询控制password的值满足条件返回$refer_url参数值,1') union select 1,2,3,4,5,6,7,8,9,0,11,12--,6对应的是password字段,所以password参数传递6,条件成立,回显传入$refer_url参数,存在sql注入。
sqli获取token
鉴权是通过调用server/Application/Api/Controller/BaseController.class.php的checkLogin方法进行验证。
未登录时,从请求中获取user_token参数,通过user_token在UserToken表中查询,验证是否超时,将未超时记录的uid字段拿到User表中查询,最后将返回的$login_user设置到Session中。
通过注入获取UserToken表中未超时的token,可使用该token访问后台接口。
phar反序列化rce
补丁:github.com/star7th/show...
补丁将new_is_writeable方法的访问权限从public设置为private。
在server/Application/Home/Controller/IndexController.class.php的new_is_writeable方法中,调用is_dir,$file可控,熟悉phar反序列化的朋友知道,is_dir函数在协议可控的情况下可触发反序列化。
有了触发反序列化的点,还需找到一条利用链,Thinkphp环境中用到GuzzleHttp,GuzzleHttp\Cookie\FileCookieJar的__destruct方法可保存文件。
网上已有分析,直接给出生成phar的exp。
生成exp时,写入的路径需指定绝对路径,在docker中部署的默认为/var/www/html,其他可通过访问时指定一个不存在的模块报错拿到绝对路径。
后续利用,找到一个上传且知道路径的点,将生成的phar文件改为png进行上传。
访问返回的链接,可获取上传文件的路径。
调用new_is_writeable方法,通过phar://访问文件触发反序列化。
武器化利用思考
在java环境下,对漏洞进行武器化时,需考虑两点情况,一是通过sqli获取token时,需对验证码进行识别,目前网上已有师傅移植了ddddocr。
github.com/BreathofWild...
二是使用exp生成phar文件时,需指定写入文件的绝对路径及内容,在java下未找到直接生成phar文件的方法,无法动态生成phar文件,需解析phar文档格式,实现一个可在java环境下指定反序列化数据来生成phar文件的方法。
phar文档格式解析
通过php生成一个phar文件,用010 Editor打开,通过官网文档对phar格式说明,解析phar的文件。
php.net/manual/zh/phar....
phar文档分为四个部分:Stub、manifest、contents、signature
Stub
就是一个php文件,用于标识该文件为phar文件,该文件内容必须以__HALT_COMPILER();goto start;结尾,类似于文件头。
manifest
这个部分不同区间指定了一些信息,其中就包含了反序列化的数据。
php.net/manual/zh/phar....
1- 4(bytes)存放的是整个manifest的长度,01C7转换为10进制为455,代表整个manifest的长度455。
5- 8(bytes)Phar中的文件数,也就是contents中的文件数,有一个文件。
9-10存放的是API version版本。
11-14Global Phar bitmapped flags。
15- 18如果有别名,那么该区间存放的是别名长度,这里不存在别名。
19- 22元数据长度,0191转换为十进制为401,代表元数据长度为401。
22-?元数据,元数据中存放的就是反序列化的数据。
contents
这个部分可有可无,是manifest第二个区间指定的一个内容,官网没有具体说明,漏洞利用时也不会用到。
signature
actual signature
这个部分存放签名内容。签名的方式不同,签名的长度也不一样,SHA1签名为20字节,MD5签名为16字节,SHA256签名为32字节,SHA512签名为64字节,OPENSSL签名的长度取决于私钥的大小。
ignature flags(4 bytes)
这个部分标识签名的算法,0x0001用于定义MD5签名,0x0002用于定义SHA1签名,0x0003用于定义SHA256签名,0x0004用于定义SHA512签名,0x0010用于定义OPENSSL签名。
GBMB(4 bytes)
Magic GBMB
签名算法为02,使用的即是SHA1签名。
签名的长度为20字节。
通过对整个phar文件格式进行解析,发现大部分字段都是固定不变的。需要变化的字段有:
1、manifest的长度
2、manifest中元数据
3、manifest中的元数据长度
4、signature flag签名算法
5、signature签名数据
java生成phar文件
最终构造得到:
二、showdoc使用
官方地址
登录并创建一个项目,如图:
再项目设置中有开发api,点开如下:
上述的环境和脚本下好了后,将脚本放在需要生成接口文档的目录下,并编辑:
api_key、api_token、url三个属性,api_key和api_token都在上面的项目设置中复制粘贴即可,url默认是 url=,但如果是使用开源版showdoc,则需要将地址改为 ,否则就不需要修改。
首先再接口头部写上注释,参考:
/**
* showdoc
*@catalog测试文档/用户相关
*@title用户登录
*@description用户登录的接口
*@method get
*@url
*@header token可选 string设备token
*@param username必选 string用户名
*@param password必选 string密码
*@param name可选 string用户昵称
*@return{"error_code":0,"data":{"uid":"1","username":"12154545","name":"吴系挂","groupid":2,"reg_time":"1436864169","last_login_time":"0"}}
*@return_param groupid int用户组id
*@return_param name string用户昵称
*@remark这里是备注信息
*@number 99
*/
然后运行shoudoc脚本(windows要配合git使用),然后就可以再自己showdoc工作台的项目中看见接口了,示例:
使用git:
右键showdoc_api.sh,选择打开方式,在其他打开方式中选择git安装目录下的bin>sh.exe;
或右键showdoc_api.sh:
三、本地部署showDoc
ShowDoc就是一个非常适合IT团队的在线文档分享工具,它可以加快团队之间沟通的效率。有时候公司为了保密性以及一些特定需求,需要本地部署文档工具,下面介绍一下如何进行showDoc的本地化部署,本文采用Docker的方式安装showDoc。
其中:
showdoc_test:容器的名字;
8100:服务运行的端口;
注意:需要释放云服务和防火墙的 8100端口
可以看到该容器已经运行成功了。