文件上传学习
一、编写一个文件上传的页面
代码参考菜鸟教程:https://www.runoob.com/php/php-file-upload.html
结构:
test
|-----upload # 文件上传的目录
|-----form.html # 表单文件
|-----upload_file.php # php 上传代码
创建文件上传表单
<html> <head> <meta charset="utf-8"> <title>文件上了个传</title> </head> <body> <form action="upload_file.php" method="post" enctype="multipart/form-data"> <label for="file">文件名:</label> <input type="file" name="file" id="file"><br> <input type="submit" name="submit" value="提交"> </form> </body> </html>
创建文件上传脚本
<?php if ($_FILES["file"]["error"] > 0) { echo "错误:: " . $_FILES["file"]["error"] . "<br>"; } else { echo "上传文件名: " . $_FILES["file"]["name"] . "<br>"; echo "文件类型: " . $_FILES["file"]["type"] . "<br>"; echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>"; echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "<br>"; // 判断当前目录下的 upload 目录是否存在该文件 // 如果没有 upload 目录,你需要创建它,upload 目录权限为 777 if (file_exists("upload/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " 文件已经存在。 "; } else { // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下 move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "文件存储在: " . "upload/" . $_FILES["file"]["name"]; } } ?>
测试:
二、对上传的文件进行验证
1.文件名验证
<?php // 允许上传的图片后缀 $allowedExts = array("gif", "jpeg", "jpg", "png"); $temp = explode(".", $_FILES["file"]["name"]); echo $_FILES["file"]["size"]; $extension = end($temp); // 获取文件后缀名 if ((($_FILES["file"]["type"] == "image/gif") || ($_FILES["file"]["type"] == "image/jpeg") || ($_FILES["file"]["type"] == "image/jpg") || ($_FILES["file"]["type"] == "image/pjpeg") || ($_FILES["file"]["type"] == "image/x-png") || ($_FILES["file"]["type"] == "image/png")) && ($_FILES["file"]["size"] < 204800) // 小于 200 kb && in_array($extension, $allowedExts)) { if ($_FILES["file"]["error"] > 0) { echo "错误:: " . $_FILES["file"]["error"] . "<br>"; } else { echo "上传文件名: " . $_FILES["file"]["name"] . "<br>"; echo "文件类型: " . $_FILES["file"]["type"] . "<br>"; echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>"; echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "<br>"; // 判断当前目录下的 upload 目录是否存在该文件 // 如果没有 upload 目录,你需要创建它,upload 目录权限为 777 if (file_exists("upload/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " 文件已经存在。 "; } else { // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下 move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "文件存储在: " . "upload/" . $_FILES["file"]["name"]; } } } else { echo "非法的文件格式"; } ?>
测试功能:上传一个PHP文件试试
发现不允许PHP文件上传
2.文件头验证
<?php function getReailFileType($filename){ $file = fopen($filename, "rb"); $bin = fread($file, 2); //只读2字节 fclose($file); $strInfo = @unpack("C2chars", $bin); $typeCode = intval($strInfo['chars1'].$strInfo['chars2']); $fileType = ''; switch($typeCode){ case 255216: $fileType = 'jpg'; break; case 13780: $fileType = 'png'; break; case 7173: $fileType = 'gif'; break; default: $fileType = 'unknown'; } return $fileType; } $is_upload = false; if(isset($_POST['submit'])){ $temp_file = $_FILES["file"]["tmp_name"]; $file_type = getReailFileType($temp_file); if($file_type == 'unknown'){ echo"文件未知,上传失败!"; }else{ $img_path = $img_path = "upload/" . $_FILES["file"]["name"]; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; echo "成功上传"; } else { echo"上传出错!"; } } } ?>
测试:
尝试上传异常gif文件失败
将.php文件添加头GIF89a,成功上传
三、文件上传结合解析漏洞
1.apache解析漏洞
(1)从右往左解析
Apache解析文件的时候是从右往左开始解析文件名,
比如test.php.abc.def
由于从右往左解析,遇到不支持的文件后缀名的时候继续往左解析,所以的话就会把该文件解析成test.php
(2)Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。
比如:1.php上传被过滤之后可以burp抓包,然后更改为1.php\x0a可以上传成功。
(3).htaccess文件实现
如果当你上传文件的时候发现你的脚本文件被阻止上传,你可以尝试上传.htaccess文件。在.htaccess文件里面将你的文件扩展名映射到可执行MIME类型。只需要添加一句话:
AddType application/x-httpd-php .html(则html文件也能执行.php文件)
AddType application/x-httpd-php .txt(则普通的文本文档也能执行.php文件)
2.IIS解析漏洞
(1)IIS6.0
路径解析
原理:在.asp、.asa目录下的任意文件都会以asp格式解析
文件解析(;截断)。
原理:在IIS 6.0下,服务器默认不解析;后面的内容。
上传test.asp;.jpg会被解析为asp。
解析文件类型
原理:IIS 6.0默认的可执行文件除了asp还包含asa、cer、cdx,会将这三种扩展名文件解析为asp文件。
(2)IIS7.5
利用条件:
Fast-CGI运行模式
php.ini里cgi.fix_pathinfo=1
取消勾选php-cgi.exe程序的"invoke handler only if request is mapped to"。也就是取消勾选模块映射中的请求限制。
利用方式:在上传的文件名后面加上/.php,可以被作为PHP文件解析。
3.nginx解析漏洞
上传一个图片马,当中间件的版本是nginx的时候,增加/.php后缀,被解析成PHP文件。
比如上传1.png。访问upload/1.png/.php会把1.png当作php文件执行。
将.php结尾的文件都会交给fastcgi来执行,/uploadfiles/nginx.png/a.php也会交给fastcgi解析,fastcgi在解析".php"文件时发现文件并不存在,这时php.ini配置文件中的cig.fix_pathinfo就发挥作用了,ccgi.fix_pathinfo是默认开启的,当php遇到文件路径/aaa.xxx/bbb.yyy/ccc.zzz时,若/aaa.xxx/bbb.yyy/ccc.zzz不存在,则会去掉最后的/ccc.zzz,然后判断/aaa.xxx/bbb.yyy是否存在,若存在,则把/aaa.xxx/bbb.yyy文件当作文件/aaa.xxx/bbb.yyy/ccc.zzz解析,若/aaa.xxx/bbb.yyy仍不存在,则继续去掉/bbb.yyy,以此类推。那么fastcgi到底能解析哪些文件呢,这个由php-fpm.conf中的security.limit_extensions参数决定的,
四、文件上传绕过
1.绕过黑名单验证
黑名单:不允许上传出现在名单中的文件后缀
白名单:只允许上传出现在名单中的文件后缀
方法:
- 大小写绕过
- 添加点空格点绕过
- 特殊后缀名比如phps、php5、pht等没有出现在黑名单中的文件类型
- 上传.htaccess文件,添加解析类型
- .ini没禁 所以可以上传.user.ini文件 内容是 auto_prepend_file=1.jpg让所有php文件都“自动”包含1.jpg文件
- 没有对后缀名中的’::$DATA’进行过滤 在php+windows的情况下文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名 且保持"::$DATA"之前的文件名
- %00截断:在URL中%00表示ASCII码中的0,而0作为特殊字符保留,表示字符结束,当url中出现%00时就会认为读取已结束,而忽略后面上传的文件或图片,只上传截断前的文件或图片
2.绕过白名单验证
- 上传图片马,如果是验证文件头的话,在图片马的开头添加文件头即可,然后结合文件包含漏洞去利用
3.二次渲染绕过
- 如果是先将文件上传,然后再进行渲染的话,存在一个条件竞争问题,可以一直发包,利用渲染的空挡去解析
- 先上传图片,然后下载,将两者进行对比,在没有发生变化的位置进行插入PHP代码,然后去上传。
五、文件上传防御
1.运行阶段的防御
- 文件上传的目录设置为不可执行。只要web容器无法解析该目录下面的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响。
- 判断文件类型。在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单方式,黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。
- 使用随机数改写文件名和文件路径。文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用了随机数改写了文件名和路径,将极大地增加攻击的成本。再来就是像shell.php.rar.rar和crossdomain.xml这种文件,都将因为重命名而无法攻击。
- 单独设置文件服务器的域名。由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传crossdomain.xml、上传包含Javascript的XSS利用等问题将得到解决。
- 使用安全设备防御。文件上传攻击的本质就是将恶意文件或者脚本上传到服务器,专业的安全设备防御此类漏洞主要是通过对漏洞的上传利用行为和恶意文件的上传过程进行检测。恶意文件千变万化,隐藏手法也不断推陈出新,对普通的系统管理员来说可以通过部署安全设备来帮助防御。
2.开发阶段防御
对文件上传漏洞来说,最好能在客户端和服务器端对用户上传的文件名和文件路径等项目分别进行严格的检查。客户端的检查虽然对技术较好的攻击者来说可以借助工具绕过,但是这也可以阻挡一些基本的试探。服务器端的检查最好使用白名单过滤的方法,这样能防止大小写等方式的绕过,同时还需对%00截断符进行检测,对HTTP包头的content-type也和上传文件的大小也需要进行检查。
3.维护阶段防御
- 系统上线后运维人员应有较强的安全意思,积极使用多个安全检测工具对系统进行安全扫描,及时发现潜在漏洞并修复。
- 定时查看系统日志,web服务器日志以发现入侵痕迹。定时关注系统所使用到的第三方插件的更新情况,如有新版本发布建议及时更新,如果第三方插件被爆有安全漏洞更应立即进行修补。
- 对于整个网站都是使用的开源代码或者使用网上的框架搭建的网站来说,尤其要注意漏洞的自查和软件版本及补丁的更新,上传功能非必选可以直接删除。除对系统自身的维护外,服务器应进行合理配置,非必选一般的目录都应去掉执行权限,上传目录可配置为只读。