当前位置:   article > 正文

文件上传漏洞 详细教程(最全讲解)

文件上传漏洞

文件上传漏洞


漏洞简述


指由于程序员未对上传的文件进行严格的验证和过滤,导致用户可以越过其本身权限向服务器上传可执行的动态脚本文件,上传文件可以是木马、病毒、恶意脚本或Webshell等,即“文件上传”本身没有问题,有问题的是文件上传后,服务器怎么处理和解释文件。如果服务器的处理逻辑做得不够安全,则会导致严重的后果,这种攻击方式是最为直接和有效的。

漏洞危害


一般情况下文件上传漏洞是指攻击者上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。文件上传本身是web中最为常见的一种功能需求,关键是文件上传之后服务器端的处理、解释文件的过程是否安全。一般的情况有:
●本地文件上传限制被绕过
●过滤不严或被绕过
●文件路径截断
●开源编辑器上传漏洞
●中间件解析漏洞
服务器配置不当-HTTP启用不安全的方法(PUT方法)
●上传的文件目录,脚本文件可执行。
●对于Web Server,上传文件或者指定目录的行为没有做限制。
利用场景
●相册、头像上传处
●附件上传处
●视频、照片分享
●文件管理器


文件上传基础知识


开发中需要上传图片、音乐、视频等等,这种上传传递是二进制数据。
客户端上传文件
文件域
<input type="file" name="image">
表单的enctype属性
    默认情况下,表单传递是字符流,不能传递二进制流,通过设置表单的enctype属性传递复合数据。
enctype属性的值有:
1 application/x-www-form-urlencoded:【默认】,表示传递的是带格式的文本数据。
2 multipart/form-data:复合的表单数据(字符串,文件),文件上传必须设置此值
3 text/plain:用于向服务器传递无格式的文本数据,主要用户电子邮件
单词
multipart:复合
form-data:表单数组
服务器接受文件
超全局变量`$_FILES`是一个二维数组,用来保存客户端上传到服务器的文件信息。二维数组的行是文件域的名称,列有5个。
1、`$_FILES[][‘name’]`:上传的文件名
2、`$_FILES[][‘type]`:上传的类型,这个类型是MIME类型(image/jpeg、image/gif、image/png)
3、`$_FILES[][‘size’]`:文件的大小,以字节为单位
4、`$_FILES[][‘tmp_name’]`:文件上传时的临时文件
5、`$_FILES[][‘error’]`:错误编码(值有0、1、2、3、4、6、7)0表示正确

$_FILES[][‘error’]详解

注意:MAX_FILE_SIZE必须在文件域的上面。

  1. move_uploaded_file(临时地址,目标地址)
  2. 代码
  3. <body>
  4. <?php
  5. if(!empty($_POST)) {if($_FILES['face']['error']==0){ //上传正确 //文件上传
  6. move_uploaded_file($_FILES['face']['tmp_name'],'./'.$_FILES['face']['name']);
  7. }else{echo '上传有误';echo '错误码:'.$_FILES['face']['error'];
  8. }
  9. }?>
  10. <form method="post" action="" enctype='multipart/form-data'>
  11. <input type="file" name="face">
  12. <input type="submit" name="button" value="上传">
  13. </form>
  14. </body>

与文件上传有关的配置
post_max_size = 8M:表单允许的最大值
upload_max_filesize = 2M:允许上传的文件大小
upload_tmp_dir =F:\wamp\tmp:指定临时文件地址,如果不知道操作系统指定
file_uploads = On:是否允许文件上传
max_file_uploads = 20:允许同时上传20个文件
更改文件名
方法一:通过时间戳做文件名
<?php $path='face.test.jpg';//echo strrchr($path,'.'); //从最后一个点开始截取,一直截取到最后 echo time().rand(100,999).strrchr($path,'.');  

方法二:通过uniqid()实现
$path='face.test.jpg';echo uniqid().strrchr($path,'.'),'<br>';   //生成唯一的ID echo uniqid('file_').strrchr($path,'.'),'<br>';   //带有前缀 echo uniqid('file_',true).strrchr($path,'.'),'<br>';  //唯一ID+随机数

验证文件格式
方法一:判断文件的扩展名(不能识别文件伪装)
操作思路:将文件的后缀和允许的后缀对比

  1. <body>
  2. <?php
  3. if(!empty($_POST)) { $allow=array('.jpg','.png','.gif'); //允许的扩展名
  4. $ext=strrchr($_FILES['face']['name'],'.'); //上传文件扩展名
  5. if(in_array($ext,$allow)) echo '允许上传'; else
  6. echo '文件不合法';
  7. }?>
  8. <form method="post" action="" enctype='multipart/form-data'>
  9. <input type="file" name="face">
  10. <input type="submit" name="button" value="上传">
  11. </form>
  12. </body>


注意:比较扩展名不能防止文件伪装。
方法二:通过$_FIELS[]['type']类型(不能识别文件伪装)
 

  1. <body>
  2. <?php
  3. if(!empty($_POST)) { $allow=array('image/jpeg','image/png','image/gif'); //允许的类别
  4. $mime=$_FILES['face']['type'];  //上传文件类型
  5. if(in_array($mime,$allow)) echo '允许上传'; else
  6. echo '文件不合法';
  7. }?>
  8. <form method="post" action="" enctype='multipart/form-data'>
  9. <input type="file" name="face">
  10. <input type="submit" name="button" value="上传">
  11. </form>
  12. </body>

方法三:php_fileinfo扩展(可以防止文件伪装,但是可以绕过)
    在php.ini中开启fileinfo扩展
extension=php_fileinfo.dll
注意:开启fileinfo扩展以后,就可以使用finfo_*的函数了

原理:
file_info 扩展是 PHP 的一个功能强大的扩展,用于确定文件的类型和属性。它提供了一种自动检测文件类型的方法,不仅仅依赖于文件扩展名,而是通过分析文件的内容和特征来确定其类型。
file_info 扩展的原理基于魔术字节(magic bytes)和魔术文件(magic file)。
魔术字节是文件内容中的一小段数据,可以用于识别文件的类型。不同类型的文件通常具有特定的魔术字节。例如,JPEG 图像文件的魔术字节通常是 0xFF 0xD8,ZIP 压缩文件的魔术字节通常是 "PK"。
魔术文件是一个包含了一系列规则的数据库文件,它描述了不同文件类型的魔术字节模式和其他特征。file_info 扩展使用这些规则来分析文件内容并匹配魔术字节,从而确定文件的类型。
当 file_info 扩展被激活并启用后,可以使用 finfo_open() 函数创建一个 finfo 对象,然后使用 finfo_file() 函数传入文件路径,或使用 finfo_buffer() 函数传入文件内容来获取文件的详细信息,包括文件类型、MIME 类型、编码等。

  1. <body>
  2. <?php
  3. if(!empty($_POST)) {//第一步:创建finfo资源
  4. $info=finfo_open(FILEINFO_MIME_TYPE);//var_dump($info); //resource(2) of type (file_info)
  5. //第二步:将finfo资源和文件做比较
  6. $mime=finfo_file($info,$_FILES['face']['tmp_name']);//第三步,比较是否合法
  7. $allow=array('image/jpeg','image/png','image/gif'); //允许的类别
  8. echo in_array($mime,$allow)?'合法':'不合法';
  9. }?>
  10. <form method="post" action="" enctype='multipart/form-data'>
  11. <input type="file" name="face">
  12. <input type="submit" name="button" value="上传">
  13. </form>
  14. </body>

漏洞利用


通常遇到任意文件漏洞,那肯定就是上传webshell 直接拿权限了。当然实战中可以能还好遇到各种限制,需要各种利用姿势才能拿到权限。

2、常见问题


2.1如何快速判断黑白名单?
上传一个特殊的后缀。比如:a.php.aaa。或者通过burpsuite 去遍历后缀名。
2.2 没有回显路径怎么办?
上传shell后没有回显路径,但是通过http history搜索shell的名字发现了完整的shell路径,因为传上去的文件,如图片这类的总归是显示出来的,这时候可以先在web应用到处点点,多加载一些数据包,然后再到http history搜索shell的名字。
常见的媒体格式类型如下:

•text/html:HTML格式
•text/plain:纯文本格式
•text/xml:XML格式
•text/css:CSS格式
•text/javascript:JS格式
•image/gif:GIF图片格式
•image/jpeg:JPG图片格式
•image/png:PNG图片格式
•image/svg+xml:SVG矢量图格式
•video/mpeg:MPEG动画格式
•application/xhtml+xml:XHTML格式
•application/xml:XML数据格式
•application/json:JSON数据格式
•application/atom+xml:Atom+XML聚合格式
•application/pdf:PDF文档格式
•application/msword:Word文档格式
•application/octet-stream:二进制数据流(如常见的文件下载)
•application/x-www-form-urlencoded:form表单被编码成key/value格式发送到服务器(表单默认提交数据的格式)
•multipart/form-data:POST 提交时伴随文件上传的表单

中间件解析漏洞查看:中间件解析漏洞(服务器配置错误)_apache中间件目录解析漏洞的原因-CSDN博客

文件上传绕过总结


一 硬怼


硬怼的话,主要是从下面这些方法入手去操作。
(1)fuzz后缀名
看看有无漏网之鱼(针对开发自定义的过滤可能有机会,针对waf基本不可能。更多的情况是php的站寻找文件包含或者解析漏洞乃至传配置文件一类的,但是对于这种也大可不必fuzz后缀名了)
(2)http头变量改造
首先要明确waf的检测特征,一般是基于某种特定的情况下,去针对相应的拦截。几个例子,文件上传的时候,大多数Content-Type都是application/multipart-formdata这种,name对于waf来说,如果针对这种规则,对xxe ,sql注入,上传,命令执行,内容等所有都去做一波扫描是及其浪费内存的,所以有可能针对不同的类型,做了不同的校验规则。此时通过对Content-Type进行修改,可能会绕过waf。其他的http头添加删除等也是类似。
(3)文件后缀构造
这个和第一个有相似的就是都针对后缀名进行改造,不同的在于这里可能会利用waf的截取特征,比如回车换行绕过waf的检测,但是对于后端来说接收了所有的传入数据,导致了绕过waf。
(4)其他方法
这种就比较杂了,但是又不属于迂回打击的一类,比如重写等方法。接下来就实战来试试
第一步,先来对waf的规则做一个简单的判断。这里我的习惯是从内容,后缀两个方向进行判断。简单来说,基本分为这几种情况
(1)只判断后缀(基本碰到的比较少了,因为很多时候白名单开发都可以完成)
(2)只判断内容(也比较少,因为一般的waf都会带后缀的判断)
(3)内容后缀同时判断(这种情况比较多,相对于来说会安全一点)
(4)根据文件后缀来判断内容是否需要检测(较多)
(5)根据Content-Type来判断文件内容是否需要检测
暂时只想到这么多,以后碰到了再单独记吧。
有了思路,那么接下来就好说了。举个例子我这里的情况
(1)传脚本后缀(被拦截,判断了后缀)
(2)传脚本后缀加不免杀代码(被拦截,可能后缀内容同时拦截)
(3)传非脚本名(可自己fuzz一个能过waf的任意后缀,里面加恶意内容,被拦截。也就是说同时会对内容和后缀进行判断)
说说我这里的情况,会对内容和后缀进行拦截。检测到上传jsp文件,任意内容都会被拦截。
先来fuzz一波能利用的后缀名,这里可以包括中间件的一些配置文件。希望不大,一点都不出意外,全部被拦截了。

一.filename改造
(2) 名字特殊符号替换以及构造异常闭合(符号方法很多自己天马星空,我这里就写几个就行了,但是要注意你改造了得让后端识别到,乱改造识别不到等于白搭)
filename='shell.jspx.jsp'
filename=shell.jspx.jsp
filename=shell.jspx.jsp'
"filename"=shell.jspx;
(3)重写
filename=shell.jpg;filename=shell.jspx;
filename=shell.jspx;filename=shell.jpg;
(4)大小写变化
FileName=shell.jspx.jsp'
(5)参数污染
FileName=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaashell.jspx.jsp'
FileName =shell.jspx(加空格)
filename===="shell.jspx.jsp1"(加等号)
FileName =shell.jspx(前后加空格,中间也可以加特殊符号fuzz)
(6)文件名字编码(filename一般为后端接收参数,编码了可能识别不到,这个就看情况)
filename=\u0073\u0068\u0065\u006c\u006c\u002e\u006a\u0073\u0070
(7)回车换行(有时候确实挺好用的,任意位置都可以试一下)
1.FileName=shell.jspx.
jsp
2.File
Name=shell.jspx.jsp'
二 name改造
name也可以任意改造,改造的方法和filename差不多,就不重复发了,主要是思路重要。
其他的比如奇奇怪怪的正则需要用到的特殊字符都可以在文件名中fuzz一下,看看能否打断waf规则,也就是把我们fuzz后缀的再跑一次,或者再找点其他的正则字母,这里就不重复写了。

http头部格式上传相关绕过
有一些用畸形相关的,不太推荐一来就试,fuzz的可以带一下,这种属于天时地利人和占据才用,毕竟底层的规定好的合规变了就不能识别,但是也说不准fuzz出问题了呢。fuzz本来就是一个天马行空的过程,好了,继续来看。
(1)Content-Disposition
溢出绕过
Content-Disposition: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa form-data; name="file"; filename=shell.jpg;filename=shell.jspx;
回车换行绕过(注意不要把固定字段打散了,)
Content-Disposition:
form-data; name="file"; filename=shell.jpg;filename=shell.jspx;
双写绕过(写两次)
Content-Disposition: form-data; name="file"; filename=shell.jpg;filename=shell.jspx;
Content-Disposition: form-data; name="file"; filename=shell.jpg;filename=shell.jspx.jpg;
还有一些参数污染加减空格啥的,和上面filename类似,就不重复写了。
(2)boundary
加减空格或者前面加恶意的参数
boundary =---------------------------8472011224916008542288311250
&boundary =---------------------------8472011224916008542288311250
123& boundary =---------------------------8472011224916008542288311250
多个污染(他是用来分割的,他变了下面的也要变一下)
boundary =---------------------------8472011224916008542288311251
boundary =---------------------------8472011224916008542288311252
回车换行污染
分割污染(简单来说就是他自定义了一些分割部分,我们可以把我们的恶意参数提交到其他的分割部分)见下图第一个,视情况而定。其他的常用方式和上面都可以重复的
(3)Content-Type
直接删除
修改类型为application/text或者 image/jpeg等等
回车换行
溢出
参数污染
重复传入Content-Type
大小写变换
设置charset
Content-Type: multipart/form-data;charset=iso-8859-13
列举几个
ibm869
ibm870
ibm871
ibm918
iso-2022-cn
iso-2022-jp
iso-2022-jp-2
iso-2022-kr
iso-8859-1
iso-8859-13
iso-8859-15
还有其他的方式,其实和上面的思路差不多

http头部其他绕过
这一块就比较多了,编码,长度等等,都可以试一下,具体的方法和上面的差不多。这里就用参考链接pureqh老哥的几个东西了。
1.Accept-Encoding 改变编码类型
Accept-Encoding: gzip
Accept-Encoding: compress
Accept-Encoding: deflate
Accept-Encoding: br
Accept-Encoding: identity
Accept-Encoding: *
下面截取的图片是我本次的,就不弄其他的了,长度那一块,主要是说内容方面相关的。

2.修改请求方式绕过
post改为get put等其他的请求方式(这一块主要是针对waf的拦截特性)
3.host头部绕过
对host进行回车,换行
修改host头部
host跟链接
host改为127.0.0.1
删除host

二 迂回打击


说是迂回打击,但是其实就是利用一些通用的手段,或者中间件的特性去绕过waf,甚至说寻找到了真实ip去直接绕过云waf等方法。这里我就简单总结一些,不全面的话忘体谅。这一块主要是内容相关的了。
基于http的绕过
这种属于硬怼,方法如下:
1.免杀马
这种是万能的,只要能免杀就能如履平地,但是现在的waf规则更新太快了,熬了一夜去弄了个免杀,第二天踩了蜜罐上去就被抓,蓝方产品支持加入规则,一点也不美滋滋,但是这也是一条YYDS的道路
2.分块传输
说实话这玩意儿我从来没有成功过,但是面试问的挺多的,有一次有个面试官还专门跟我提了这个所以我这里列举一下。但是分块参数+参数污染组合利用貌似效果还是不错
3.修改长度字段
和分块参数有点类似,作用是这样,有些时候做参数大数据污染的时候,waf判断数据过长直接丢弃,有些判断长度和内容相差太多也直接丢弃。这时候可以把两者结合起来使用,达到超长数据绕过waf的检测,同时数据送到了后端
4.修改传输编码
和分块传输类似,自己手动去改,burp那个插件工具我是一次都没成功过
5.基于网站系统特性添加字段
比如ASP专属bypass-devcap-charset,添加这些字段去绕过waf的检测(这也是我看到但是没机会实战,记录一下)
6.修改头部+内容结合
修改头部为其他格式,再把内容头加其他格式,例如图片,中间插入恶意代码,类似图片马
7.增加多个boundary
这样子打乱了恶意内容,有点类似分开传输,欺骗waf的检测,逃逸后面的代码。
8.文件名写入文件
windows下利用多个<<<<去写入文件,详情可以看参考链接。
还有一些其他的方法,这一种也是类似于对waf欺骗,过着直接利用免杀硬过waf的。jsp免杀不会,就不献丑免杀了。
其他绕过
这种绕过就是一般适用于云waf了。咋说呢,这种我碰到的不怎么多,因为一般碰到的云waf基本都很强,注入上传类的绕过现在越来越难了,xss还好一点,但是不走钓鱼的话xss也没用太大的用处,毕竟可以一把梭最舒服。来看看吧,检测全球ping就行。
1.寻找真实ip
这个方法网上太多了,说下我常用的
(1)利用ssl证书寻找
(2)利用子域名寻找
(3)利用公司其他业务寻找(跑C端看运气,和子域名一样)
(4)利用信息泄露寻找(github,google,目录文件,js代码等)
(5)利用一些云网站或者专门查找cdn的网站,链接在家里电脑上,这电脑没有,就自己去找吧
(6)利用已知工具
(7)搜索引擎(fofa,夸克等,看以前收集的业务)
(8)利用http返回信息
(9)找邮箱弱口令,然后你懂的
(10)找朋友,你懂的。
2.利用子域名去打
有些网站,可能外面做了防护,子域名没加waf,而子域名又在白名单,迂回去锤就行了。
3.利用头部绕过
基本碰不到了,修改host为本地ip,现在已经绝迹了,突然想起来写一下。
4.找设备
找一些vpn一类的设备碰碰运气
其他的就不说了吧,头痛。总结下这个思路
(1)直接寻找waf保护后的目标地址,进行亲身拥抱(绕过waf去打)
(2)寻找waf后目标的子女子孙亲儿子(被waf加白的一些资产)去挑拨离间。


不安全的过滤


1、没有任何过滤

2、前端过滤

开发者在前端使用JavaScript做了白名单验证,如果文件名不符合规则那么就不允许被上传。 但是这种方式很轻易就被绕过了。

3、文件类型过滤

4、文件类型过滤+ php_fileinfo扩展

5、黑名单过滤

如果基于黑名单过滤的话,绕过的方式比较多,可使用uploads-labs 靶场进行练习各种绕过方式
如下是黑名单绕过的常见姿势:(具体绕过需要根据后端代码的实际情况)
●上传特殊可解析后缀:如php2、php3、shtml、phtml等(需要配置http.conf 文件)
●上传.htaccess 文件
●后缀大小写
●点绕过
●空格绕过
●::$DATA 绕过
●双后缀名绕过
●大小写绕过
●配合解析漏洞
示例如下:

6、白名单绕过

白名单相对于黑名单要安全的多,但也不是绝对的安全。
●%00截断(php版本<5.3 php.ini这个配置文件中magic_quotes_gpc必须为off)
●0x00 截断(0x00是字符串的结束标识符,攻击者可以利用手动添加标识符的方式来将后面的内容弄进行截断)
●文件包含(通过上传图片马(copy a.jpg /b + a.php /a xx.jpg))
●逻辑绕过(如条件竞争)

7、条件竞争

这里先将文件上传到服务器,会判断是否在白名单后缀名里面,如果是则rename修改名称,如果不是则unlink删除文件,因此可以通过条件竞争的方式在unlink之前,访问webshell。 
这里是这样操作的,先通过move_uploaded_file把文件保存了,然后再去判断后缀名是否合法,合法就重命名,如果不合法再删除。重是重点在于,在多线程情况下(也就是在burp suite 爆破的时候进行shell.php的访问),就有可能出现还没处理完,我们就访问了原文件,这样就会导致被绕过防护,从而进行访问 shell.php。
1、首先准备好要上传的数据包,稍后配合第二步进行手动发送或者自动发送

2、使用burp不断的访问。
如果flag.php访问成功,则会在当前目录下生成shell.php 文件那就说明写入成功了。

8、解压缩getshell

渗透的时候,有时候会需要后台通过上传压缩包进行部署的功能,上传成功后自解压到指定目录。
我们写一个phpinfo 文件然后压缩成zip 格式压缩包,然后上传自解压会释放php 文件

安全建议


1、 最有效的,将文件上传目录直接设置为不可执行,对于Linux而言,撤销其目录的'x'权限;实际中很多大型网站的上传应用都会放置在独立的存储上作为静态文件处理,一是方便使用缓存加速降低能耗,二是杜绝了脚本执行的可能性;
2、 文件类型检查:建议使用白名单方式,结合MIME Type、后缀检查等方式(即只允许允许的文件类型进行上传);此外对于图片的处理可以使用压缩函数或resize函数,处理图片的同时破坏其包含的HTML代码;
3、 文件重命名(使用随机数改写文件名)和隐藏上传文件路径,使得用户不能轻易访问自己上传的文件;
4、 单独设置文件服务器的域名。
5、增加防护设备如免费的安全狗、D盾,虽然可以绕过但是起码起到一定的作用,主要还是从代码层面去防护。当然有条件可以增加大厂的waf等安全设备。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/841778
推荐阅读
相关标签
  

闽ICP备14008679号