记某系统有趣的文件上传

本次是一次漏洞复现记录,记录了一个有趣的后台文件上传漏洞,以后遇到类似的拦截可以参考这次的过程绕过。

记某系统有趣的文件上传

本次是一次漏洞复现记录,记录了一个有趣的后台文件上传漏洞,以后遇到类似的拦截可以参考这次的过程绕过。
站点搭建,根据系统需要,使用phpstudy即可在windows上成功搭建
image.png

0x01黑盒问路

使用默认口令进入后台,先对网站的上传功能进行黑盒测试,左侧栏中找到图库管理,正常点击上传按钮,弹出文件上传对话框。
image.png
选择文件,使用一张测试图片,文件名随便写,反正之后要在数据包里修改。
image.png
点击提交后,进行抓包,记一下上传路径,方便之后去看源码。
image-20231207163738868
修改文件名为test.php并发送数据包,从可以看到返回包的路径,文件名称被添加.jpg
image.png
此时需要根据上传路径,看源码中的上传逻辑,寻找突破点。

0x02上传但没完全上传

/Admin/SEMCMS_Upfile.php
image.png
从源码中可以看到,文件类型校验是从此处的filename获得,并截取了.后面的文件后缀。
image.png
之后使用正则对截取的后缀进行匹配,感觉这块可能存在问题。
image.png
于是构造filename为test.jpg.php,进行测试发现,因为使用了end()函数,导致jpg没有被取到,未能绕过正则校验。image.png
提醒了允许上传的白名单,他人还蛮好的。
image.png
继续看到下面的代码,发现原来是对POST传上去wname强行拼接了刚刚$filename的后缀。难怪之前直接上传后,返回的文件名多了个后缀。
image.png
还有令人在意的是这个test_input()函数,跟进看一下,发现是个实现去除空格、去除反斜杠、对html字符进行实体编码功能的。
image.png
最后就是熟悉的move_uploaded_file()函数写入文件,新的文件名就是刚刚拼接起来的$newname
image.png
整理一下思路,目前的状况就是preg_match()没办法直接绕过,只能从其中正则匹配的jpg|jpeg|gif|png|doc|xls|pdf|rar|zip|bmp|ico这些后缀中选择,并添加到我们可控的文件名后面,让原本可执行的文件变成图片。这种简单粗暴的添加方式还是可以被绕过的。
秘密藏在windows文件命名规则中:
image.png
在windows环境中,当创建的文件名中存在:时,文件名会被截断并生成空文件。以本系统为例,构造数据包,只需要控制$wname为test.php:,其他参数都符合上传要求即可。
image.png
访问返回的路径,果然发现通过该方式成功上传php文件,但是内容并没有被写入,感觉比较鸡肋。
image.png
image.png

0x03注入灵魂

之前也有碰到过类似的,虽然利用该windows系统中特有的方式上传php文件,但是无法写入信息,只能无奈放弃的情况。
根据偶然间看到的漏洞poc,师傅还利用了PHP语言在Windows上的一些奇妙特性<<再次进行上传,给第一次传上去的php写入内容,注入了灵魂。
与之前测试构造的数据包不太一样,poc中需要控制$wname为test.jpg.php:,其他参数都符合上传要求就行。
上传时,文件拼接后完整的名称为test.jpg.php:.jpg;创建文件时会生成test.jpg.php空文件。
image.png
image.png
image.png
再次发送数据包,控制$wname为test,$filename为test.jpg<<
这里就需要提一下PHP语言在windows上的特性:

  • 大于号>相等于通配符问号?
  • 小于号<<相当于通配符星号*
  • 双引号"相等于字符.

根据描述,上传时,文件拼接后完整的名称为test.jpg<<,应该会匹配为test.jpg.php
image.png
image.png
但和预想的不同,并没有任何内容被写入php文件中。
image.png
虽大佬诚不欺人,但最好还是编写代码验证一下,找找出问题的原因。因为该特性是PHP在windows下存在的,因此与具体的函数无关,推测在读文件的时候应该同样存在等效的通配符匹配效果,于是编写如下代码文件读取代码进行测试。

<?php
$file = @$_REQUEST['filename'];
$file_contents = @file_get_contents($file);
echo "匹配到的文件为:".$file_contents;

在测试代码的同目录下创建六个内容为文件名称的文件。
image.png
使用test.jpg<<<进行匹配,果然先匹配到了test.jpgtest.jpg.jpg,从而导致给php文件写入失败。
<<效果测试:
需要注意单个<无法正常匹配到结果,只有连着使用两个或两个以上<才有通配符*的效果。
image.png
删除test.jpg再次使用相同的参数请求,匹配到test.jpg.aaa
image.png
删除test.jpg.aaa再次使用相同的参数请求,匹配到test.jpg.jpg
image.png
删除test.jpg.jpg文件后再次使用test.jpg<<<进行匹配,成功获得.php文件
image.png
对其他符号同样进行测试
>效果测试:
单独>可以匹配零个或一个字符,效果类似于通配符?,缺失多少拼接多少>就可以匹配到对应的文件。
image.png
但经过测试无法代替文件名中的字符.使用
image.png
"效果测试:
符号"可以匹配到字符.
image.png
通过上述的测试,发现原来内容被写入到同目录下第一次测试时上传的test.jpg.jpg文件中=.=!
发现问题,就好解决,重新利用payload,更换文件名为test123上传即可。这次就没手贱放包了。
image.png
创建空php文件test123.jpg.php
image.png
写入内容
image.png
成功执行
image.png
根据上述匹配方式,尝试构造并测试其他poc
还是以给test123.jpg.php写入php代码为例,除了可以使用test123.jpg<<,具有相同效果的poc为:
test123.jpg\">>p
image.png
image.png
test123.jpg\"php
image.png
image.png

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()

因时间原因,这次关于特性的学习仅仅只停留在浅显的利用阶段,后续应该要好好看一下这部分的原理,加深理解,才能更好和狡猾的漏洞做斗争。
-----------------------------我是分割线-----------------------------------------------
关于PHP语言在Windows上特性的参考文章,感兴趣的师傅可以自行深入研究:
PHP源码调试之Windows文件通配符分析

  • 发表于 2023-12-11 15:41:23
  • 阅读 ( 37410 )
  • 分类:漏洞分析

7 条评论

Pseudoknot
大哥,文章前面的图好像对不上
请先 登录 后评论
fagulas
大佬,上传的图片好像有点问题
我也感觉有问题,同一张图上传了好多次。
请先 登录 后评论
c铃儿响叮当
TQL
请先 登录 后评论
刀刀
TQL
请先 登录 后评论
LOGfggg
这个靶机可以分享一下嘛,我想自己本地试试
请先 登录 后评论
秋殇
学到了
请先 登录 后评论
请先 登录 后评论
中铁13层打工人
中铁13层打工人

79 篇文章

站长统计