本次是一次漏洞复现记录,记录了一个有趣的后台文件上传漏洞,以后遇到类似的拦截可以参考这次的过程绕过。
站点搭建,根据系统需要,使用phpstudy即可在windows上成功搭建
0x01黑盒问路
使用默认口令进入后台,先对网站的上传功能进行黑盒测试,左侧栏中找到图库管理,正常点击上传按钮,弹出文件上传对话框。
选择文件,使用一张测试图片,文件名随便写,反正之后要在数据包里修改。
点击提交后,进行抓包,记一下上传路径,方便之后去看源码。
修改文件名为test.php
并发送数据包,从可以看到返回包的路径,文件名称被添加.jpg
此时需要根据上传路径,看源码中的上传逻辑,寻找突破点。
0x02上传但没完全上传
/Admin/SEMCMS_Upfile.php
从源码中可以看到,文件类型校验是从此处的filename
获得,并截取了.
后面的文件后缀。
之后使用正则对截取的后缀进行匹配,感觉这块可能存在问题。
于是构造filename为test.jpg.php
,进行测试发现,因为使用了end()函数,导致jpg
没有被取到,未能绕过正则校验。
提醒了允许上传的白名单,他人还蛮好的。
继续看到下面的代码,发现原来是对POST传上去wname
强行拼接了刚刚$filename
的后缀。难怪之前直接上传后,返回的文件名多了个后缀。
还有令人在意的是这个test_input()函数,跟进看一下,发现是个实现去除空格、去除反斜杠、对html字符进行实体编码功能的。
最后就是熟悉的move_uploaded_file()函数写入文件,新的文件名就是刚刚拼接起来的$newname
整理一下思路,目前的状况就是preg_match()没办法直接绕过,只能从其中正则匹配的jpg|jpeg|gif|png|doc|xls|pdf|rar|zip|bmp|ico
这些后缀中选择,并添加到我们可控的文件名后面,让原本可执行的文件变成图片。这种简单粗暴的添加方式还是可以被绕过的。
秘密藏在windows文件命名规则中:
在windows环境中,当创建的文件名中存在:
时,文件名会被截断并生成空文件。以本系统为例,构造数据包,只需要控制$wname为test.php:
,其他参数都符合上传要求即可。
访问返回的路径,果然发现通过该方式成功上传php文件,但是内容并没有被写入,感觉比较鸡肋。
0x03注入灵魂
之前也有碰到过类似的,虽然利用该windows系统中特有的方式上传php文件,但是无法写入信息,只能无奈放弃的情况。
根据偶然间看到的漏洞poc,师傅还利用了PHP语言在Windows上的一些奇妙特性<<
再次进行上传,给第一次传上去的php写入内容,注入了灵魂。
与之前测试构造的数据包不太一样,poc中需要控制$wname为test.jpg.php:
,其他参数都符合上传要求就行。
上传时,文件拼接后完整的名称为test.jpg.php:.jpg
;创建文件时会生成test.jpg.php
空文件。
再次发送数据包,控制$wname为test
,$filename为test.jpg<<
这里就需要提一下PHP语言在windows上的特性:
- 大于号
>
相等于通配符问号?
- 小于号
<<
相当于通配符星号*
- 双引号
"
相等于字符点.
根据描述,上传时,文件拼接后完整的名称为test.jpg<<
,应该会匹配为test.jpg.php
但和预想的不同,并没有任何内容被写入php文件中。
虽大佬诚不欺人,但最好还是编写代码验证一下,找找出问题的原因。因为该特性是PHP在windows下存在的,因此与具体的函数无关,推测在读文件的时候应该同样存在等效的通配符匹配效果,于是编写如下代码文件读取代码进行测试。
<?php
$file
= @
$_REQUEST
[
'filename'
];
$file_contents
= @file_get_contents(
$file
);
echo
"匹配到的文件为:"
.
$file_contents
;
在测试代码的同目录下创建六个内容为文件名称的文件。
使用test.jpg<<<
进行匹配,果然先匹配到了test.jpg
和test.jpg.jpg
,从而导致给php文件写入失败。
<<
效果测试:
需要注意单个<
无法正常匹配到结果,只有连着使用两个或两个以上<
才有通配符*
的效果。
删除test.jpg
再次使用相同的参数请求,匹配到test.jpg.aaa
删除test.jpg.aaa
再次使用相同的参数请求,匹配到test.jpg.jpg
删除test.jpg.jpg
文件后再次使用test.jpg<<<
进行匹配,成功获得.php
文件
对其他符号同样进行测试
>
效果测试:
单独>
可以匹配零个或一个字符,效果类似于通配符?
,缺失多少拼接多少>
就可以匹配到对应的文件。
但经过测试无法代替文件名中的字符.
使用
"
效果测试:
符号"
可以匹配到字符.
。
通过上述的测试,发现原来内容被写入到同目录下第一次测试时上传的test.jpg.jpg
文件中=.=!
发现问题,就好解决,重新利用payload,更换文件名为test123上传即可。这次就没手贱放包了。
创建空php文件test123.jpg.php
写入内容
成功执行
根据上述匹配方式,尝试构造并测试其他poc
还是以给test123.jpg.php写入php代码为例,除了可以使用test123.jpg<<
,具有相同效果的poc为:
test123.jpg">>p
test123.jpg"php
0x04后记
考虑到该特性是否与php版本有关,特意测试了在php5、php7以及php8中的利用,读写文件均可以成功。
从网上找到的文章解释来看,是因为在windows下,PHP中的部分函数会调用支持上述字符作为通配符的底层Windows API函数,从而产生的特性。这部分函数包括但不限于如下:
include
()
include_once
()
require
()
require_once
()
fopen()
copy()
file_get_contents()
readfile()
file_put_contents()
mkdir()
opendir()
readdir()
move_uploaded_file()
getimagesize()
因时间原因,这次关于特性的学习仅仅只停留在浅显的利用阶段,后续应该要好好看一下这部分的原理,加深理解,才能更好和狡猾的漏洞做斗争。
文章来源:奇安信攻防社区
https://forum.butian.net/share/2399
黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!
如侵权请私聊我们删文
END
原文始发于微信公众号(黑白之道):记某系统有趣的文件上传
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论