文件上传常见验证:
本质是为了验证文件格式
直接:查看文件后缀
间接:通过文件类型和文件头识别
后缀名
黑名单
明确不让上传的格式后缀:
asp php jsp aspx cgi war
缺陷:
有一定几率执行出相同效果(与搭建平台有关系):php5、Phtml...
白名单(略安全)
明确可以上传的格式后缀:
jpg png zip rar gif ...
类型
上传时的数据包信息:
MIME信息
Content-Type: image/png
MIME的值:image/png image/gif image/jpeg ......
jsp的MIME值:application/octet-stream
通过判断MIME值判断文件格式
绕过:burp抓包修改Content-Type
文件头(文件内容)
内容的头部信息
phg的头部:塒NG!
gif头部:GIF89a
绕过:burp抓包修改文件头
源码中文件上传过程(html+php)
利用 $_FILES 函数 MIME传来的信息
<html>
...
<form enctype='multipart/form-data' method='post' action="">
<p>上传图片</p>
<input class="input_file" type="file" name="upload_file"/> //name=就是下面$_FILES要的接收名,保持一致
<input class="button" type="submit" name="submit" value="上传"/>
</form>
</html>
$_FILES 可以接收多个值,第一个[]表示从前端来的接收名,第二个[]表示接收的图片信息
<?php
$name=$_FILES['upload_file']['name']
$type=$_FILES['upload_file']['type']
$type=$_FILES['upload_file']['size']
?>
黑名单
$deny_ext=array('.asp','.php','.jsp'); // 拒绝的后缀名
trim(..) 替换为空的函数,移除字符串两侧的空白字符或其他预定义字符,就是去空格
strrchr(a,'.'); // 删除a中.前面的数据,只留后缀,若多个点默认最后一个点
特殊解析后缀
php5
.htaccess解析
控制文件解析方式的配置文件,多种写法自行百度
<FilesMatch "自定义.gif">
SetHandler application/x-httpd-php #在当前目录下,如果匹配到自定义.gif文件,则被解析成PHP代码执行
先上传这个文件,再上传注入后门的图片文件
图片文件名为 自定义.gif 就会自动被php解析
预防 : 黑名单.htaccess
大小写绕过
$file_ext = strtolower($file_ext); //转换为小写
点绕过
$file_name = deldot($file_name); //删除文件名末尾的点
点'.'被上传到windows服务器会被windows自动恢复
空格绕过
trim()收尾去空
$file_ext = trim($file_ext); //收尾去空
若无此函数,在数据包中 filename="XXX.php " 末尾加个空格即可绕过黑名单,在windows中又会自动去掉空格还原成php格式
空格' '和点'.'都会被windows自动恢复
::$DATA绕过
必须是Windows,必须是php
php在windows时如果文件名加了::DATA会把::DATA会把::DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名 目的就是不检查后缀名
配合解析漏洞
双后缀名绕过
代码将字符串里的php替换为空
一次过滤
a.pphphp ——> a.php
循环过滤(递归过滤)
a.pphphp ——> a.
因此可以重复被拦截条件,后缀加'. .' ,一次过滤只过滤掉了一个空格一个点' .' 还留下了个点
围绕黑名单的绕过
$deny_ext = array('.asp','.aspx','.php','.jsp'); // php5
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空
过滤次数(是一次过滤还是循环(递归)过滤),一次过滤 a.pphphp ——> a.php
str_ireplace(a,b,c) 替换a为b,在c变量中 同样利用一次过滤 a.pphphp
白名单
MIME绕过
%00截断
截断:后面没有了
地址上面的加
源码:
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; // get获取
绕过:
数据包里的save_path加上 1.php%00 保存路径便成了
$img_path=../upload/1.php%0046841348644.jpg
%00......后面没有了,文件名即为1.php
$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; //post获取需要将%00进行URL解码
get数据会自动url加密和解码,普通文本的空格自动解码为%20,%20也能知道是空格
post不会解码只会加密
目的是url编码的%00
普通文本的%00会被编码为%25%30%30,post不会解码便读取不出%00,要让加密后为%00就得写解码的%00
0x00截断
仅文件命名上面%00不同