一.定义:
文件上传漏洞是用户上传了一个可执行脚本,然后通过该文件获得访问服务器的权限的漏洞。
二.原理:
大部分的网站都有文件上传的功能,比如上传png,php,exe等文件,一些能被网站给过滤,但大部分的web过滤不够严格,导致在上传位置上,可以上传一些带脚本的文件,web端对php进行解析,可以执行任意脚本的文件。
三.文件上传的校验流程
四.文件上传漏洞的成功前提:
1.网站有文件上传的功能 |
2.文件类型能够上传 |
3.知道上传的路径 |
4.上传的文件可以执行或被包含 |
五.文件上传漏洞的类型:
(1)js检查(客户端校验)
随便上传一个文件,它是否会有提示,什么类型可以上传。一般禁用java script或删除代码。
(2)后端黑名单绕过
扩展名绕过(客户端校验)
一般上传一个后缀名为 .txt的木马, 利用burpsuite改后缀名为 .php / .jsp /.asp
后缀 大小写绕过
后缀显示非法时,可将 .php 改为 .Php 或 .PhP
后缀双写绕过
web代码中会将可能存在漏洞的脚本文件后缀名进行过滤,大部分情况下只能过滤一层,可改为 .pphphp
空格绕过
正常的php脚本不可以绕过,但是后面加上一个空格即可绕过,比如 .php改为 . php
点绕过
在后面加一个点,比如 .php改为 .php.
.htaccess解析
.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法,即,在一个特定的文档目录中放置一个包含一个或多个指令的文件,以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以ApacheAllowOverride指令来设置。
方法:直接上传这个文件,看是否可以获得权限,改后缀为 .htaccess
::$DATA绕过
在window的时候如果文件名+"::D A T A " 会 把 : : DATA"会把::DATA"会把::DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名.利用burpsuite抓包,改包,将 .php改为 .php::DATA
(3)后端白名单绕过
00截断绕过
0x00截断原理:
0x00是十六进制表示方法,是ascii码为0的字符,在有些函数处理时,会把这个字符当做结束符。系统在对文件名的读取时,如果遇到0x00,就会认为读取已结束。这个可以用在对文件类型名的绕过上。但要注意是文件的16进制内容里的00,而不是文件名中的00.就是说系统是按16进制读取文件(或者说二进制),遇到ascii码为零的位置就停止,而这个ascii码为零的位置在16进制中是00,用0x开头表示16进制,也就是所说的0x00截断。%00是被服务器解码为0x00发挥了截断作用。
通过post传进来,用Burp抓包,在二进制中找到文件名,在文件名后的位置的二进制数值进行修改为00,因为post不会像get对%00进行自动解码。
通过get传进来的,代码中,文件路径采用字符串拼接的方式,因此可以利用%00截断绕过
用Burp抓包,在数据包第一行的url请求文件名后加上%00,
mime文件绕过
客户端软件,区分不同种类的数据,例如web浏览器就是通过MIME类型来判断文件是GIF图片,还是可打印的PostScript文件。web服务器使用MIME来说明发送数据的种类,web客户端使用MIME来说明希望接收到的数据种类。利用burpsuite抓包改包。
(4)其他绕过
条件竞争
网站允许上传文件,然后检查上传文件是否包含webshell、是否是指定的文件类型。如果不是,那么删除该文件。在删除之前访问上传的php文件,从而执行上传文件中的php代码。绕过方法: 先进行文件上传,后进行判断与删除。利用时间差进行webshell上传。
二次渲染
上传两次,通过上传同一个文件,进行对比,在相同点添加脚本代码。
(5)绕过WAF
详解在:干货 | 最全的文件上传漏洞之WAF拦截绕过总结-腾讯云开发者社区-腾讯云
以上皆是个人看法,如有不对请多指正。谢谢!!!
六.例题:
来自buuctf n1book
先来看一下源代码,基本知道考点为 zip路径穿越 和 1.php.xxx
-
<?php header("Content-Type:text/html; charset=utf-8"); // 每5分钟会清除一次目录下上传的文件 //会包含文件pclzip.lib.php,感觉这个php里面是有一些针对zip包进行解压等的操作的 require_once('pclzip.lib.php'); //要是没上传文件,就输出上传页面 if(!$_FILES){ echo '省略的HTML' show_source(__FILE__); }else{ $file = $_FILES['file']; //限制上传的文件名不为空 if(!$file){ exit("请勿上传空文件"); } $name = $file['name']; $dir = 'upload/'; $ext = strtolower(substr(strrchr($name, '.'), 1)); $path = $dir.$name; //strrchr($name, '.') //strrchr() 函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。 //比如上传的文件名为$name=1.php.txt,这里strrchr($name, '.') 执行结果为.txt //substr(strrchr($name, '.'), 1) //substr字符串截取,从下标为1开始截取,那就是把点略过,截取后为txt //通过strtolower函数将所有字符转换为小写,赋值给ext变量 //如果我们上传的文件名为1.txt,那么path变量就为upload/1.txt //检查是否为目录 function check_dir($dir){ $handle = opendir($dir); while(($f = readdir($handle)) !== false){ if(!in_array($f, array('.', '..'))){ if(is_dir($dir.$f)){ check_dir($dir.$f.'/'); }else{ $ext = strtolower(substr(strrchr($f, '.'), 1)); if(!in_array($ext, array('jpg', 'gif', 'png'))){ unlink($dir.$f); } } } } } //创建目录 if(!is_dir($dir)){ mkdir($dir); } $temp_dir = $dir.md5(time(). rand(1000,9999)); if(!is_dir($temp_dir)){ mkdir($temp_dir); } //这里应该就是最难的了,大概就是将目录名拼接一个随机数,读到这里,基本上就知道需要路径穿越了 因为我们不知道随机数值,所以就算绕过上传,解析也是一大关(路径不难找,就是解析难) //首先进行后缀的校验,把刚刚拿到的,最后一个.后面的字符串和这里的zip、jpg、gif、png进行对比校验 if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){ if($ext == 'zip'){ //使用PclZip进行解压缩 $archive = new PclZip($file['tmp_name']); //遍历解压缩后的每个目录 foreach($archive->listContent() as $value){ $filename = $value["filename"]; //一段较为简单的正则,就是匹配每个文件结尾的位置,是否是.php if(preg_match('/\.php$/', $filename)){ exit("压缩包内不允许含有php文件!"); } } if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) { check_dir($dir); exit("解压失败"); } check_dir($dir); exit('上传成功!'); }else{ move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']); check_dir($dir); exit('上传成功!'); } }else{ exit('仅允许上传zip、jpg、gif、png文件!'); } }
pclzip()
产生zip压缩档;列出压缩档内容 listContent( ) :列出压缩档中的内容,包括档案的属性与目录;extract([options list]) :解压缩PKZIP中的档案或目录。
strrchr()
查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。比如上传的文件名为$name=1.php.txt,这里strrchr($name, '.') 执行结果为.txt。
substr(strrchr($name, '.'), 1)substr字符串截取,从下标为1开始截取,那就是把点略过,截取后为txt通过strtolower函数将所有字符转换为小写,赋值给ext变量,如果我们上传的文件名为1.txt,那么path变量就为upload/1.txt
绕过路径检测
通过路径穿越来到web的根目录下,返回两级目录,一级是upload,另一级是随机值(根据源代码的)使用 ../../
apache换行解析漏洞
影响范围:2.4.0-2.4.29版本
原因:合法后缀配置文件中的正则表达式中$不仅匹配字符串结尾位置,还可以匹配\n或\r,在解析php时,1.php\x0A将按照.php进行解析,而’.php\x0A’ != ‘.php’,可能过滤时过滤了.php但没有过滤.php\x0A从而实现绕过。配置文件:过滤后缀名.phpapache 2.4.7 无法上传hhh.php,上传hhh.php.xxx可以绕过。
新建一个文件,文件名长度要为18,因为构造的的为/../../hhh.php.xxx
压缩为zip
利用010 打开并修改文件名
之后直接上传即可得到flag