当前位置:   article > 正文

html input上传文件_finecms代码审计之任意文件上传漏洞

data:image/png;base64 文件上传漏洞

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

任意文件上传背景介绍

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

再比如说,一个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,来定位:

41ea5b658eb653f892eb343576d380ae.png

看到函数注释,很明显是一个头像上传功能,根据文件所在的路径结构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 (!@file_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)

a14f4692daef44df8e12481c81ff39ff.png

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

9ce130efb73659b7f4185550adb1bcad.png

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

漏洞复现

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

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

cb7b1ca5021b3731f09a80bbf882a12a.png

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

<?php  phpinfo()?>

然后用base64编码一下:

PD9waHAgcGhwaW5mbygpPz4=

04f149864e2adf905c32e0b07d78d880.png

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

$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

22428c52e7404aff26315a1212f52734.png

小结

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

004511d52019e9bd912b12cf46980434.png

3f6ae377f9d9840beaefc9316d2b7b41.png

扫码关注

       隐刃安全实验室,专注于渗透测试、源码审计、漏洞分析、
       内网攻防、脚本工具开发等安全领域,致力于分享精品原
       创文章、漏洞复现、工具靶场、CTF等技术干货。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/257800
推荐阅读
相关标签
  

闽ICP备14008679号