PHP文件上传漏洞

本文用到的代码地址:https://github.com/Levones/PHP_file_upload_vlun,好评请给小星星,谢谢各位大佬!

0x00 漏洞描述

​ 在实际开发过程中文件上传的功能时十分常见的,比如博客系统用户需要文件上传功能来上传自己的头像,写博客时需要上传图片来丰富自己的文章,购物系统在识图搜索时也需要上传图片等,文件上传功能固然重要,但是如果在实现相应功能时没有注意安全保护措施,造成的损失可能十分巨大,为了学习和研究文件上传功能的安全实现方法,我将在下文分析一些常见的文件上传安全措施和一些绕过方法。

​ 我按照最常见的上传功能–上传图片来分析这个漏洞。为了使漏洞的危害性呈现的清晰明了,我将漏洞防御措施划分为几个不同的等级来作比较

0x01 前端HTML页面代码

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>file_upload_test
</title>
<body>
<form enctype="multipart/form-data" action="upload_1.php" method="POST" />
<input type="hidden" name="MAX_FILE_SIZE" value="100000" />
选择你要上传的图片:
<br />
<input name="uploaded" type="file" /><br />
<br />
<input type="submit" name="Upload" value="上传" />
</form>
</body>
</html>

前端的实现代码均为以上。界面如下图:

0x01 零防御的PHP上传代码

源代码 upload_0.php

<?php
if (isset($_POST['Upload'])) {$target_path = "uploads/";$target_path = $target_path . basename( $_FILES['uploaded']['name']);if(!move_uploaded_file($_FILES['uploaded']['tmp_name'], $target_path)) {echo '<pre>';echo '您的图片上传失败.';echo '</pre>';} else {echo '<pre>';echo $target_path . '文件已经成功上传!';echo '</pre>';}
}
?>

这段PHP代码对上传的文件没有任何的过滤,只是将上传的文件直接存储到了网站uploads文件夹下,此时如果我们上传一个一句话木马并通过浏览器访问加上参数的地址或者使用中国菜刀直接连接,就可以为所欲为了。

//一句话木马
<?php eval($_GET['cmd']);?>

0x01 初级防护-验证文件类型

源代码 upload_1.php

<?php
if( isset( $_POST[ 'Upload' ] ) ) {$target_path  = "uploads/";$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );//识别文件类型$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&( $uploaded_size < 100000 ) ) {if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {echo "<pre>图片上传失败</pre>";}else {echo "<pre>{$target_path} 图片上传成功!</pre>";}}else {echo "<pre>只允许上传jpg或者png格式的图片文件,且文件大小不能超过100k</pre>";}
}
?>

防御方法

初级防御的代码在审查用户上传的文件时加入了“Content-Type”验证,代码会自动识别文件类型并将文件类型以表单的形式进行验证,如果“Content-Type”是image/jpeg或者image/png时文件可以上传 成功,算是初级防御。

绕过方法

用BurpSuite截断代理修改数据包的相关字段即可完成绕过,本例上传的文件时shell.php,代码会将此文件的Content-Type识别为application/x-php,直接将application/x-php改为mage/jpeg即可绕过验证,而且对于文件大小的限制也是可以直接修改”MAX_FILE_SIZE”的方式突破限制从而上传更大的文件。

修改前

修改后

0x02 一般防护-验证文件后缀

源代码 upoad_2.php

<?php
if( isset( $_POST[ 'Upload' ] ) ) {$target_path  = "uploads/";$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );//记录文件信息$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];$uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];//识别文件后缀if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&( $uploaded_size < 100000 ) &&getimagesize( $uploaded_tmp ) ) {if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {echo "<pre>图片上传识别.</pre>";}else {echo "<pre>{$target_path} 图片上传成功!</pre>";}}else {echo "<pre>只能上传格式为jpg和png的图片.</pre>";}
}
?>

相比较于前一种比价简单的验证content-type的防护方式,一般级别的防护措施换成了验证文件后缀的方式,顺便多说一句,在为了安全性设置一些限制时,使用白名单永远比设置黑名单要安全的多,因为总会有=各种方式绕过黑名单的方式或者是一些针对不同服务器系统或着服务器的特殊解析原理而造成的一些安全隐患。以下是获取文件后缀的代码:

$uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1)

通过本语句获取文件名中最后一个“.”后的字符识别上传的文件名的后缀,并将后缀存储在一个变量中。

if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&( $uploaded_size < 100000 ) &&getimagesize( $uploaded_tmp ) )

而在if的逻辑判断中,需要上一条语句截取到的文件后缀为“jpg”,“jpeg”或者“png”,切且上传的文件大小不得大于10000b,如果只有这个限制方法的话,可以直接使用burpsuite进行00截断,从而使得在文件后缀验证时通过但是在文件转储的时候忽略掉00之后的内容从而实现后缀欺骗,具体方式如下:

  • 假设网站只能上传图片文件并在后台欧了后缀的限制
  • 此时你要上传一个shell.php的一句话木马
  • 将”shell.php”改为”shell.php 1.png”
  • 使用burpsuite截断代理,拦截数据包
  • 将”shell.php 1.png”发送至decoder模块,从text模式转换为hex编辑模式,找到”shell.php 1.png”中空格对应的hex值“20”,将20改为00
  • 从hex模式恢复为text并将修改过的字符串替换原来报文中的”shell.php 1.png”
  • 发送报文,操作成功后会显示文件上传成功

操作成功后会显示文件上传成功,在php版本小于5.3.4的版本中,当Magic_quote_gpc选项为off时,可以在文件名中使用%00截断,所以可以把上传文件命名a1.php%00.png进行绕过,我们用bp抓包检测一下文件类型。 可以发现文件类型是png成功绕过前端,并且到服务器文件会被解析成php文件,因为00后面的被截断了,服务器不解析。

但是在本例中,00截断的方法不再有效,因为if条件中还有一个getimagesize()函数,此函数会自动识别上传的图片的文件头,长宽,mime类型等信息,因此如果上传的文件不是图片将无法上传。绕过这个限制的方法是制作图片马,我是在win环境下制作的,只需准备一个图片大小较小的jpg或者png格式的图片,打开cmd使用命令:

copy 1.jpg/b+shell.php 2.jpg

来合成一张图片马,如果用二进制编辑器打开此文件会发现一句话木马写到了文件的后面,把这样的文件上传时,由于文件头仍然是jpg的文件头,getimagesize()函数也会正确的返回图片的大小和文类型,因此通这种方式可以绕过getimagesize()函数的限制,再结合00截断即可上传木马并在服务器端将文件解析为php脚本,从而正确执行。

但是如果服务器的PHP版本较高,则无法通过此方法进行漏洞的利用,需要结合文件包含漏洞进行利用。

0x03 无解的防护-全方面限制

当然安全只是相对的,没有绝对的安全,一下代码对输入的文件进行了多种方式的审查并进行了重新编码,是目前比较完善了安全防御措施。

源代码 upload_2.php

<?php
if( isset( $_POST[ 'Upload' ] ) ) {// 检查tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];$uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];$target_path   = 'uploads/';$target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;$temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );$temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;//判断是否是一张图片if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&( $uploaded_size < 100000 ) &&( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&getimagesize( $uploaded_tmp ) ) {//重新制作一张图片,抹去任何可能有危害的数据if( $uploaded_type == 'image/jpeg' ) {$img = imagecreatefromjpeg( $uploaded_tmp );imagejpeg( $img, $temp_file, 100);}else {$img = imagecreatefrompng( $uploaded_tmp );imagepng( $img, $temp_file, 9);}imagedestroy( $img );//文件转储if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {$html .= "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";}else {$html .= '<pre>Your image was not uploaded.</pre>';}//删除所有暂时文件if( file_exists( $temp_file ) )unlink( $temp_file );}else {//无效文件$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';}
}
// 添加抗csrf验证
generateSessionToken();
?>

上述代码的安全措施:

  • 添加了sessionToken,验证会话身份,用于防止csrf攻击
  • 使用md5( uniqid() . $uploaded_name )函数,uniqid()函数是根据当前的时间,生成一个唯一的id,跟大多数随机函数一样,基于时间的随机函数在一定条件下也是可以差生碰撞的,因此本例中采用了md5()函数来保证生成id的唯一性,而且由于md5()函数对上传的文件名进行了重命名,因此无法使用00截断的方式来上传php或者其他恶意脚本文件。
  • 以白名单的方式限制上传的文件后缀
  • 限定上传的文件大小不得超过10000
  • 通过imagecreatefromjpeg()和imagecreatefrompng()函数将上传的图片文件重新写入到一个新的图片文件中,这两个函数会自动将图片中的有害元数据抹除,因此即使黑客上传了一张图片马也会被这个函数过滤成一个纯正的图片。
  • imagedestroy( $img )将用户上传的源文件删除
  • unlink( $temp_file )删除过滤过程中产生的任何临时文件

0x04 个人总结

web漏洞种类繁多,利用方法奇葩而有趣,值得研究和学习

PHP文件上传漏洞原理以及防御姿势相关推荐

  1. 网安基础学习之“文件上传漏洞原理与实现”

    网安基础学习之"文件上传漏洞原理与实现" 近期新闻头条上报出了"长沙市场监管局网站被上传了黄色页面",经过长沙市公安局网技支队的排查,该门户网站后台编辑器存在* ...

  2. 文件上传漏洞原理与实例测试

    0x00 什么是文件上传 为了让用户将文件上传到网站,就像是给危机服务器的恶意用户打开了另一扇门.即便如此,在今天的现代互联网的Web应用程序,它是一种常见的要求,因为它有助于提高业务效率.企业支持门 ...

  3. php文件上传漏洞攻击与防御

    转自:http://www.cnblogs.com/crazylocust/p/6759529.html 前言 从一年前开始学习web安全以来,一直都是在吸收零碎的知识,不断地看书与一些前辈的文章,中 ...

  4. 文件上传漏洞攻击与防御

    前言 从一年前开始学习web安全以来,一直都是在吸收零碎的知识,不断地看书与一些前辈的文章,中间也经过一些实践,学习相关的工具,但是却没真真正正地在脑中形成一套完整的体系.从不久前就想着要写一些博客, ...

  5. 文件上传漏洞-原理篇

    目录 第1章 文件上传漏洞基础 1.1 漏洞概述 1.2 漏洞成因 1.3 漏洞危害 1.4 漏洞利用姿势 第2章 文件上传漏洞检测与绕过 2.1 前端检测和绕过 2.2 服务器端检测和绕过 第3章 ...

  6. 文件上传漏洞原理/方式/防护

    文件上传漏洞是获取服务器权限最快也是最直接的一个漏洞 原理 文件上传漏洞是指用户上传可执行脚本文件 , 并通过脚本文件控制Web服务器 利用方式 文件上传漏洞的利用分为客户端和服务端 客户端主通过JS ...

  7. 文件上传漏洞原理和利用

    文件上传 1.前段选择文件,提交文件 2.浏览器形成POST MultiPart报文发送到服务器 3.服务器中间件接收到报文,解析后交给后端代码进行处理 4.后端代码将上传的文件内容写入到临时文件中( ...

  8. 文件上传漏洞揭密剖析(一)-文件上传漏洞原理

    前言 文件上传漏洞(File Upload Vulnerability)是一种常见的 Web 应用程序漏洞,通常存在于需要用户上传文件的应用程序中,如论坛.电子商务网站.博客.社交网站等.攻击者可以通 ...

  9. 【文件上传漏洞-01】文件上传漏洞概述、防御以及WebShell基础知识补充

    目录 1 文件上传漏洞概述 2 文件上传漏洞防御.绕过.利用 2.1 黑白名单策略 3 WebShell基础知识补充 3.1 WebShell概述 3.2 大马与小马 1 文件上传漏洞概述 概述:文件 ...

最新文章

  1. 0x66.图论 - Tarjan算法与无向图连通性
  2. ActiveMQ消息的延时和定时投递
  3. 利用隐藏神经元解决异或问题的小型示例程序
  4. 前端小课堂 js:what is the function?
  5. POJ - 1328 Radar Installation(贪心+思维)
  6. eclipse怎么导入一个Java项目(莫要错过,最详细教程!)
  7. css连续的纯数字或字母强制换行
  8. 设计类毕业生求职指南!手把手帮你从零开始找到工作!
  9. leapftp,leapftp怎么上传文件
  10. 如何获取ppt的背景图片
  11. 动画 | 什么是红黑树?(基于2-3树)
  12. Web前端工程师学习路线
  13. gmoj 6841. 【2020.11.5提高组模拟】淘淘蓝蓝之树林
  14. 计算机文档排版的心得体会,排版工作心得体会(共6篇) .docx
  15. 武汉大学 计算机学院 衰落,为何武汉大学每年都被黑,武汉大学真的没有实力吗?...
  16. excel TEXT 函数
  17. 线段树,方差,数学(Variance,玲珑杯 Round#5 H lonlife 1063)
  18. JPress企业站主题-jpressicu使用教程
  19. 中山大学计算机技术考研科目,2017年中山大学0812计算机科学与技术考研专业目录及考试科目...
  20. javascript第二天课程

热门文章

  1. HDFS——editLog文件
  2. 计算机网络上机座次表
  3. Arduino Uno 学习第一课,编程准备
  4. 【开源GPS追踪】 之 硬件开源
  5. 移花接木,Delphi中主窗体的转移。
  6. 操作系统 内存管理总结
  7. MATLAB 运行完电脑自动关机
  8. ubuntu系统ffmpeg录音
  9. 素材 | 本期10套优质素材推荐下载
  10. 大数据24小时:Snh48推出全球首款明星人工智能,阿里云将在印度建成首个数据中心