finecms代码审计之任意文件上传漏洞

admin 2023年1月8日22:49:22代码审计评论17 views3239字阅读10分47秒阅读模式

“文件上传这个功能在大部分系统中都存在,但是要做好一个文件上传和下载功能的话就非常吃经验,因为涉及到很多的边界条件。”

任意文件上传背景介绍

首先,文件上传首先就要校验文件类型是否在允许范围内;比如一张图片,用文本编辑器打开 在后面追加文字,此时这个文件到底判定成一张图片还是文本呢?虽此时图片仍然能打开,但假如图片里面的信息是文字比图片还多,那么到底认为这个文件是一个图片还是一个文本呢?

再比如说,一个txt,把它的扩展名改成了png,那么能认为这是一个图片文件吗?用文本编辑器打开png图片时,第一行有一个89 50 4E 47 0D 0A 1A 0A,一些程序会取这个文件头的信息,作为文件类型的判断依据;

而在Web的场景里,浏览器在上传文件的时候,会提供一个Content-Type,作为后端的一个参考;同时上传的文件信息会在PHP的$_FILES数组里,这个数组同时也会出现Content-Type信息;

PHP里移动上传的文件用的是move_uploaded_file函数,所以尝试搜索image/png这种图片的Content-Type信息,以及在上下文查找move_uploaded_file这个函数,可以作为一个审计的技巧,方便我们定位到上传功能的代码。

上传图片有时也会使用base64来传输,通常在富文本编辑器插件或者是图片裁剪插件的图片上传,所谓的base64图片,就是开头是

data:image/png;base64,

这种带着格式声明,后面接一段长长的base64字符串,后端代码则接受这段字符串,然后判断是否是图片,这种代码特征也是很明显,搜索data:image或者base64之类的代码,或者自己总结一下特征写一段正则表达式也是可以的;

在文件上传之后,要做到不解析,不执行,,比如真的被用户上传了一个PHP文件,也不应该让用户有机会去执行,而是弹出下载. 同理,对于用户上传了的html文件,为了防止xss漏洞,也应该让浏览器直接弹出下载,而不是解析和渲染这个html文件;

下载功能,让浏览器弹出下载.一般是在响应头中,设置一个Content-Disposition信息

Content-Disposition: attachment; filename=example.html

如果文件名部分,没有经过过滤,传入了一些不可见的字符例如换行符n,会导致浏览器不会弹出下载,而是依据响应头中Content-type的信息再次判断,在这里就有可能让一个下载html的行为变成了渲染html!!所以搜索Content-Disposition: attachment;这种代码,然后查看调用的上下文,审计文件名部分是否可控,也是一个审计的技巧。


finecms5.0.8代码审计


这套系统存在base64图片上传功能,可以通过base64,来定位:


finecms代码审计之任意文件上传漏洞


看到函数注释,很明显是一个头像上传功能,根据文件所在的路径结构finecms/dayrui/controllers/member/Account.php,猜测该功能在会员账号相关地方有对应的入口。前几行根据注释都能知道是创建存放图片文件夹的代码,关注上传流程:


if ($_POST['tx']) {
$file = str_replace(' ', '+', $_POST['tx']);
if (preg_match('/^(data:s*image/(w+);base64,)/', $file, $result)){
$new_file = $dir.'0x0.'.$result[2];
if ([email protected]_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))) {
exit(dr_json(0, '目录权限不足或磁盘已满'));
}


这里是保存文件的逻辑代码.这个项目是基于CodeIgniter(PHP框架)写的。CodeIgniter中获取$_GET这个全局数组,一般是用$this→input→get()这个函数去取数据。同理,这个框架获取$_POST数组,对应的是$this→input→post()然而在这里直接出现了:

$_POST['tx']

这种代码并没有按照框架的约定用$this→input→post()代替,当在代码中不遵守规范的时候,就是容易犯错的时候,所以就要对接下来的代码重点关注了。

提取一下核心的逻辑,它保存文件的核心代码只有这4行:


$file = str_replace(' ', '+', $_POST['tx']);
preg_match('/^(data:s*image/(w+);base64,)/', $file, $result)
$new_file = $dir.'0x0.'.$result[2];
file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))


这4行的作用分别是:

1.替换base64字符串中的空格为加号

2.利用正则表达式提取base64字符串的信息,把匹配到的信息放入$result数组

3.拼接上传文件的路径,文件名中扩展名部分从上面正则表达式中匹配结果的第2组而来

4.把base64字符串去掉前面几个字节中和格式相关的内容,然后做base64解码,然后写入文件

看一下其中的正则匹配,用于对base64字符串做匹配,提取信息,推荐一个正则表达式可视化的在线工具 :

https://jex.im/regulex


preg_match('/^(data:s*image/(w+);base64,)/', $file, $result)


finecms代码审计之任意文件上传漏洞


这段代码在保存文件的时候,文件名中的扩展名部分,只是把来自正则表达式检测base64中的结果第2组信息作为扩展名,并没有对于扩展名进行丝毫的验证,第2组信息是完全可控的,根据可视化工具的提示,第2组信息的位置是image/的后面,;base64,的前面:


finecms代码审计之任意文件上传漏洞

后面文件内容部分也是我们可控的,那么得到一个任意文件上传的漏洞。只需要构造一个类似base64图片的字符串,在这个正则表达式的第2组位置填入想要生成的文件的扩展名如php,然后在文件信息的位置写入PHP代码,然后把PHP代码base64编码一下,就可以形成一个payload了。

漏洞复现


在这里访问前台的会员的头像上传处:

http://localhost/index.php?s=member&c=account&m=avatar

finecms代码审计之任意文件上传漏洞


这里选择一张图片,上传的时候抓包,把代表文件格式的png改成php,代表图片内容的字符串改成php脚本:


<?php phpinfo()?>

然后用base64编码一下:


PD9waHAgcGhwaW5mbygpPz4=


finecms代码审计之任意文件上传漏洞


根据源码中保存文件的部分:


$dir = SYS_UPLOAD_PATH.'/member/'.$this->uid.'/';
……
$new_file = $dir.'0x0.'.$result[2];


得出上传文件路径为:


/uploadfile/member/用户id/0x0.扩展名

查看cookie字段中的member_uid可以获取到用户id,简单拼接一下就可以得到:

http://localhost/uploadfile/member/3/0x0.php

finecms代码审计之任意文件上传漏洞





小结

通过针对文件上传漏洞对fincms5.0.8进行php代码审计,发现头像上传处开发人员没有遵循CodeIgniter框架约定进行传参,而且没有对后缀名进行验证且该后缀名完全可控,导致任意文件上传漏洞的发生。



finecms代码审计之任意文件上传漏洞

finecms代码审计之任意文件上传漏洞


扫码关注


       隐刃安全实验室,专注于渗透测试、源码审计、漏洞分析、
       内网攻防、脚本工具开发等安全领域,致力于分享精品原
       创文章、漏洞复现、工具靶场、CTF等技术干货。



原文始发于微信公众号(SK安全实验室):finecms代码审计之任意文件上传漏洞

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年1月8日22:49:22
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  finecms代码审计之任意文件上传漏洞 http://cn-sec.com/archives/1038868.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: