文件上传常用绕过方式

JavaScript前端验证绕过

JavaScript 验证就是所谓的客户端验证,也是最脆弱的一种验证。直接修改数据包或禁用 JavaScript.enable 即可绕过。

例如upload的第一题,在我们点击发送时,还没经过代理就直接弹窗报错,就考虑存在前端验证

文件上传常用绕过方式_第1张图片

我们只需要将前端验证的布尔值改为false,重新加载后就能绕过前端验证,上传成功

文件上传常用绕过方式_第2张图片

MIME类型绕过

MIME类型检测是通过检查数据包的Content-Type字段中的值来判断上传文件是否合法的,因此在文件上传时把数据包中的Content-Type值更改为image/png等图片类型格式就可以达到绕过的目的。

content-type 验证绕过:

在HTTP协议消息头中,使用Content-Type来表示请求和响应中的媒体类型信息。它用来告诉服务端如何处理请求的数据,以及告诉客户端(一般是浏览器)如何解析响应的数据

在某些题目中会对文件类型进行过滤,这时候我们就可以上传一个合法文件,然后通过修改content-type标头来实现绕过

常见的媒体格式类型如下:

  • HTML文档标记:text/html

  • 普通ASCII文档标记:text/html

  • JPEG图片标记:image/jpeg

  • GIF图片标记:image/gif

  • png图片标记:image/png

以application开头的媒体格式类型:

  • js文档标记:application/javascript

  • xml文件标记:application/xml

  • application/xhtml+xml :XHTML格式

  • application/xml: XML数据格式

  • application/atom+xml :Atom XML聚合格式

  • application/json: JSON数据格式

  • application/pdf:pdf格式

  • application/msword : Word文档格式

  • application/octet-stream : 二进制流数据(如常见的文件下载)

  • application/x-www-form-urlencoded :

    中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)

另外一种常见的媒体格式是上传文件之时使用的:

  • multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式

更多详情可见:https://www.runoob.com/http/http-content-type.html

application/x-www-form-urlencoded

https://www.runoob.com/php/php-file-upload.html

multipart/form-data

pass_2

upload靶场文件上传第二题就是MIME类型绕过

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']            
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}
$is_upload = false; - 用于标记文件是否成功上传,默认为 false。
$msg = null; - 用于保存上传过程中的错误消息,默认为 null。

if (isset($_POST['submit']))
检查是否通过表单提交了文件上传请求。
if (file_exists(UPLOAD_PATH))
检查 UPLOAD_PATH 目录是否存在,如果不存在报错UPLOAD_PATH 文件夹不存在,请手工创建!"。

if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))
检查上传的文件类型是否为 JPEG、PNG 或 GIF

如果上传的文件类型不是 JPEG、PNG 或 GIF 图片。
设置错误消息为 "文件类型不正确,请重新上传!"。

$temp_file = $_FILES['upload_file']['tmp_name'];
获取上传文件的临时路径。
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']
拼接上传文件的最终保存路径。
if (move_uploaded_file($temp_file, $img_path))
将临时文件移动到指定的目标路径。
$is_upload = true;
文件成功上传,将 $is_upload 设置为 true。
如果移动文件失败,设置错误消息为 "上传出错!"。

审计完源码之后,得知,本关只检查文件的类型,所以我们可以通过burp抓包对文件类型,也就是content-type类型进行更改,成功上传

文件上传常用绕过方式_第3张图片

后缀解析

在文件上传中,php,phps,php3,php5,pht等都有可能被解析成php文件,当然这得取决apache的配置。我们在apache的配置文件httpd.conf中添加,以下配置

AddType application/x-httpd-php .php .php3 .php4 .php5 .pht .phps .phptml
文件上传常用绕过方式_第4张图片

我们可以先注释掉这这段语句,然后创建一个.php5的文件去浏览器访问

可以看到没有任何的回显,只是在源码中显示了PHP语句

文件上传常用绕过方式_第5张图片

然后放出来重启phpstudy就可以解析为PHP文件了

文件上传常用绕过方式_第6张图片

利用这个性质,我们就可以在upload-labs的第三题绕过限制

pass3

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array('.asp','.aspx','.php','.jsp');
        $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); //收尾去空

        if(!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;            
            if (move_uploaded_file($temp_file,$img_path)) {
                 $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

可以看到在以上源码中,拥有一个黑名单列表,里面的数据都不允许上传,这时候我们就可以用到同类型的后缀名进行绕过

看到利用更改后缀的方式,已经完成了第一步,上传文件

文件上传常用绕过方式_第7张图片

但是源码中对上传后文件的路径,是采取时间和随机数组合的形式,以此来阻止我们的代码注入

但是这道题会直接将路径显示到响应码中,所以我们可以直接在响应包的搜索后缀,获取路径

文件上传常用绕过方式_第8张图片

.htaccess 文件

概念

htaccess文件是Apache服务器中的一个分布式配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能

.htaccess 常见指令

SetHandler

SetHandler 指令可以强制所有匹配的文件被一个指定的处理器处理


SetHandler application/x-httpd-php
//这里是使png文件被当做PHP解析

此伪指令为具有特定文件扩展名的文件配置特定处理程序

AddType

AddType 指令可以将给定的文件扩展名映射到指定的内容类型。

语法:
AddType media-type extensive [extensive] ...

示例 1:

AddType application/x-httpd-php .gif

此时将会把 gif 为后缀的文件当做 php 文件解析。

示例 2:

AddType application/x-httpd-php png jpg gif

此时将会把 .png .jpg .gif 多个后缀的文件当做 php 解析。

方法一

利用SetHandler指令强制转换

提前准备好一个.htaccess文件,编写好内容之后,另存为所有文件(*.*)

文件上传常用绕过方式_第9张图片

根据.htaccess的性质,先上传.htaccess文件,这里是将所有png文件转换为PHP执行

文件上传常用绕过方式_第10张图片
文件上传常用绕过方式_第11张图片

.user.ini文件

php.ini是php的一个全局配置文件,对整个web服务起作用;

而.user.ini和.htaccess一样是目录的配置文件,.user.ini可以理解为用户自定义的一个php.ini,我们可以利用这个文件来构造后门和隐藏后门。

需要注意的是:

  • 在 .user.ini 风格的 INI 文件中只有具有 PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置可被识别。

  • .htaccess和.user.ini都只能用于访问本目录下的文件时进行覆盖。

  • .user.ini只能用于Server API为FastCGI模式下,而正常情况下apache运行不了。

php 配置项中有两个配置可以起到一些作用
auto_prepend_file = //prepend是文件执行前包含。

auto_append_file = //append是文件执行后包含,

可以查看官网:https://www.php.net/manual/zh/ini.list.php

文件上传常用绕过方式_第12张图片

这两个配置项的作用相当于一个文件包含,比如

auto_prepend_file = 1.jpg


// 1.php(任意php文件)

满足这三个文件在同一目录下,则相当于在1.php文件里插入了包含语句require('1.png'),进行了文件包含。

另一条配置auto_append_file包含在文件尾,如果遇到了 exit 语句的话就会失效,视情况而定。

pass5

这道题应该是后面新加的,之前的upload靶场不存在

做这道题之前,我们需要先将phpsudy调整为FastCGI模式,我这里是改成了nginx+php

注意PHP的版本要在5.3以上

文件上传常用绕过方式_第13张图片

接着传入.user.ini文件

文件上传常用绕过方式_第14张图片

然后上传一个php脚本文件,因为.user.ini文件相当于是一个文件包含

所以我就直接传入了一个生成shell文件的图片马

如果将一个一句话木马文件后缀改为jpg也是可以的,只不过需要传参访问

文件上传常用绕过方式_第15张图片

这里也需要注意:只有在访问php文件时,才会自动包含1.gif,所以作者给了提示在上传目录下是有一个readme.php文件的,所以直接访问此文件就可以包含上传的shell了

可以看到在我的上传路径下自动生成了一个shell文件

文件上传常用绕过方式_第16张图片

大小写绕过

windows对大小写不敏感,所以可以进行上传大小混写的php进行绕过

例如:在windows中txt与TxT是同一种文件类型

文件上传常用绕过方式_第17张图片

如果源码中没有将你传入的字符串进行小写转换后再对比黑名单的话,大写字母和小写字母肯定不相等,所以可以利用这一点进行绕过,又因为windows对大小写不敏感,所以.PhP文件被当成php文件解析

文件上传常用绕过方式_第18张图片

空格和点绕过

原理

在Windows环境下,a.php [空格]a.php.这两类文件都是不允许存在的,若这样命名Windows会默认除去空格或点

但是在传输的过程中,是可以识别这个空格和点的,我们就可以通过抓包在文件名后加一个空格或者点绕过黑名单。

文件上传常用绕过方式_第19张图片
文件上传常用绕过方式_第20张图片

pass10

第十关也是跟点和空格有关,因为本靶场中有一个自定义deldot函数,定义在common.php中

其主要目的是移除文件名末尾的点

例如$a = shell.php.

调用本函数后,就得到$a = shell.php

函数定义如下:
function deldot($s){
    for($i = strlen($s)-1;$i>0;$i--){
        $c = substr($s,$i,1);
        if($i == strlen($s)-1 and $c != '.'){
            return $s;
        }
​
        if($c != '.'){
            return substr($s,0,$i+1);
        }
    }
}
函数大致解释就是:
strlen函数返回的是字符串的长度
从指定字符串倒数第二个字符开始,向后截取一个字符赋给c,直到c不等于0时结束循环

但是这个函数有个漏洞,就是它只检测出现.的情况,如果我们抓包后再文件改为1.php. .

点与点之间存在一个空格,当检测到空格时满足不等于. 的条件,跳出函数

文件上传常用绕过方式_第21张图片

::$DATA绕过

概述

这里能绕过的原理是因为windows的NTFS文件系统的一个特性,windows都适用

在window的时候如果文件名+::$DATA会把::$DATA之后的数据当成文件流处理,不会检测后缀名

且保持::$DATA之前的文件名,他的目的就是不检查后缀名

也就是当我们访问a.php::$data时,也就是相当于访问a.php本身

当我们访问ab:a.php::$data时,也就是访问ab文件夹下的a.php文件本身

这个绕过方式的使用和前面两个一样,都是burp抓包改文件名就行了,例如把a.php改为a.php::$data,只要他没有做对应的过滤就能绕过黑名单检测

文件上传常用绕过方式_第22张图片
文件上传常用绕过方式_第23张图片
文件上传常用绕过方式_第24张图片

双写绕过

如果源码中存在将黑名单的后缀替换为空的函数,例如

$file_name = str_ireplace($deny_ext,"", $file_name);

str_ireplace函数不区分大小写
此时我们上传一个1.php文件,服务器会那黑名单中的内容来匹配,匹配到.php后,就会把原来的文件名“ a.php”后面的.php后缀删掉,此时这个文件虽然能成功上传,但是已经不会被解析为php了

而我们对应的绕过方式就是——双写后缀绕过

条件还是这个服务器只进行了一次过滤,例如我们上传1.php,burp抓包,将文件名改为1.pphphp”

进入文件上传流程,识别文件后缀,为“pphphp”

当识别到完整黑名单后缀后,即“pphphp”,将识别到的php删除,后缀中剩下的字符组合后,此时后缀“pphphp”就成了“php”,此时剩下的文件名——“1.php”,成功绕过

pass11

文件上传常用绕过方式_第25张图片

当然这个题目还有另外一个解,让我想另外一个解的原因是因为第十题的空格和点绕过,我发现前面很多题同样适用点空格点的绕过方式

另外一个解是:因为本题没有对.htaccess文件进行过滤,我们就可以让他去解析上传的文件,这样说来user.ini文件也可以,只不过题目不主要考察

%00截断

概述

0x00 %00 /00之类的截断,都是一个意思,只是不同表示而已

00在ascll码在中作为空字符(null)存在,在语言中,\0则是作为一个字符串的结束,所以当url中出现%00时就会认为文件读取已结束

文件上传常用绕过方式_第26张图片

我们就可以利用这个性质,绕过一些白名单限制

pass12

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}
strrpos(string , find [,start]) 函数查找字符串在另一字符串中 最后一次出现的位置(区分大小写)。
substr(string , start [,length])函数返回字符串的一部分(从start开始 [,长度为length])
img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

这一关白名单,最终文件的存放位置是以拼接的方式,可以使用%00截断

但需要php版本<5.3.4,并且magic_quotes_gpc关闭。

因为检测的最后一次出现的位置,所以我们可以在php后构造.jpg绕过strrpos函数

文件上传常用绕过方式_第27张图片

pass13

POST方式不会对里面的数据自动解码,抓包后需要在Hex中修改。这里加+的原因是因为hex中不能选中后确定位置,所以只是为了方便标识

文件上传常用绕过方式_第28张图片

将2b改为00后将,传入的后缀改为白名单后缀

文件上传常用绕过方式_第29张图片

最后上传成功

文件上传常用绕过方式_第30张图片

图片木马绕过

文件头欺骗

pass14

这种一般适用于绕过白名单检测,源码中会对上传文件的文件头进行检测,但是因为过滤不严格,只检查了前两个字节

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;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_type = getReailFileType($temp_file);

    if($file_type == 'unknown'){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

unpack()

在 PHP 中,unpack() 函数用于将二进制数据解包为数组。对于给定的二进制数据 $bin,@unpack("C2chars", $bin) 将会返回一个关联数组,其中键名为 chars1 和 chars2,对应着解包后的两个字节。

在这里,C 表示一个无符号字符 (一个字节),2 表示要解包两个这样的字符。因此,@unpack("C2chars", $bin) 会将 $bin 解包为两个字节,并将结果存储在 $strInfo 中。

文件上传常用绕过方式_第31张图片

intval()

在 PHP 中,intval() 函数用于将给定的值转换为整数类型。在这里,$strInfo['chars1'] 和 $strInfo['chars2'] 都是字符串类型的字节数据。

通过将这两个字节拼接在一起,然后将结果传递给 intval() 函数,可以将其转换为整数类型。

解题步骤

因为本题不涉及检查文件后缀,所以可以在我们的PHP文件开头单独一行添加GIF89a,这就是一个简单的gif文件头

文件上传常用绕过方式_第32张图片
文件上传常用绕过方式_第33张图片

根据上传的路径,最后会将我们php后缀的文件拼接变成检测文件头匹配的后缀

$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;

我们抓包后利用目录里的include.php,包含我们上传后的文件,或者直接利用蚁剑包含

文件上传常用绕过方式_第34张图片

或者我们可以在生成一个图片木马,就是在图片中嵌入构造的生成php文件的一句话木马

这里最好是利用命令行copy生成木马文件

copy tp.jpg/b + 1.php/a tm.jpg

在进行上传后,利用文件包含就可以生成一个shell文件,然后就可以直接连接文件了

文件上传常用绕过方式_第35张图片

getimagesize

其实也就是可以拆分理解get image size获取图像文件的大小以及其他信息

文件上传常用绕过方式_第36张图片
  • 索引 0 给出的是图像宽度的像素值

  • 索引 1 给出的是图像高度的像素值

  • 索引 2 给出的是图像的类型,返回的是数字,其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM

  • 索引 3 给出的是一个宽度和高度的字符串,可以直接用于 HTML 的 标签

  • 索引 bits 给出的是图像的每种颜色的位数,二进制格式

  • 索引 channels 给出的是图像的通道值,RGB 图像默认是 3

  • 索引 mime 给出的是图像的 MIME 信息,此信息可以用来在 HTTP Content-type 头信息中发送正确的信息,如: header("Content-type: image/jpeg");

pass15

这段代码的核心就是isImage自定义函数

  1. 首先,使用 file_exists($filename) 函数检查指定的文件是否存在。

  1. 如果文件存在,使用 getimagesize($filename) 函数获取文件的图像信息,并将获取到的图像类型赋值给变量 $info。

  1. 使用 image_type_to_extension($info[2]) 函数将图像类型转换为相应的文件扩展名,并将其赋值给变量 $ext。

  1. 使用 stripos($types, $ext) 函数查找 $types 字符串中是否包含 $ext 子字符串。stripos 函数是大小写不敏感的字符串查找函数,如果存在,则返回子字符串在字符串中第一次出现的位置索引;如果不存在,则返回 false。

  1. 如果 $ext 子字符串存在于 $types 字符串中(即返回值大于等于0),则返回 $ext,表示文件符合要求。否则返回 false表示不返回任何值。

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        $info = getimagesize($filename);
        $ext = image_type_to_extension($info[2]);
        if(stripos($types,$ext)>=0){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

其实跟第十四题一样的上传方式,上传一个图片马然后包含就好了

二次渲染

二次渲染就是在原本白名单检测的基础上,对上传的文件进行二次生成,虽然图片马可以上传,但是包含的漏洞无法解析。原因就是二次渲染将图片马里面的php脚本给删了。

pass17

代码审计

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $filename = $_FILES['upload_file']['name'];
    $filetype = $_FILES['upload_file']['type'];
    $tmpname = $_FILES['upload_file']['tmp_name'];

    $target_path=UPLOAD_PATH.'/'.basename($filename);

    // 获得上传文件的扩展名
    $fileext= substr(strrchr($filename,"."),1);

    //判断文件后缀与类型,合法才进行上传操作
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromjpeg($target_path);

            if($im == false){
                $msg = "该文件不是jpg格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".jpg";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagejpeg($im,$img_path);
                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }
  1. 首先获取上传文件的基本信息,包括文件名、文件类型和临时文件路径。

  1. 设置目标文件路径为指定的上传路径(UPLOAD_PATH)和上传文件的文件名。

  1. strrchr和subsut函数一起获取上传文件的扩展名。

  1. 判断上传文件的扩展名和文件类型是否符合要求,即文件扩展名为 "jpg",文件类型为 "image/jpeg"。

  1. 如果文件扩展名和文件类型符合要求,利用move_uploaded_file函数将临时文件移动到目标文件路径,成功则继续执行

  1. imagecreatefromjpeg函数,拆分理解image create from jpeg,用于从 JPEG 图像文件创建一个新的图像资源。失败返回false

  1. 检查生成的新图片是否是有效的 JPEG 图片。如果不是,将目标文件删除

  1. 如果生成的新图片是有效的 JPEG 图片,为新图片指定一个随机的文件名,避免文件冲突,并将新图片保存到指定路径。

  1. 删除原始的目标文件,并设置 $is_upload 为 true,表示上传成功。

解题步骤

建议直接上传gif文件,相较于其他两个格式的文件更加简单

首先因为我们知道上传的地址,所以我们可以将两个文件都利用010打开,然后进行比较(ctrl+M)

灰色部分就是没有经过二次渲染的地区,所以我们可以在此处插入一句话木马,绕过二次渲染

文件上传常用绕过方式_第37张图片

上传后再继续用记事本打开看看在不在

文件上传常用绕过方式_第38张图片

上传成功就行了,一定要注意文件包含的路径aaaaaaaaaaaaa

像下面就行了,注意路径,包含后是会在文件上传的根目录创建文件,并不是在upload文件夹下,有个傻逼在这里耗了两个小时

文件上传常用绕过方式_第39张图片

参考博客

(19条消息) upload-labs之第十六关_upload-labs16_阜城小柳的博客-CSDN博客

条件竞争

漏洞形成原因:

条件竞争漏洞:由于服务器端在处理不同的请求时是并发进行的,因此如果并发处理不当或相关操作顺序设计的不合理时,将会导致此类问题的发生

pass18

代码审计

$is_upload = false;
$msg = null;

if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_name = $_FILES['upload_file']['name'];
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_ext = substr($file_name,strrpos($file_name,".")+1);
    $upload_file = UPLOAD_PATH . '/' . $file_name;

    if(move_uploaded_file($temp_file, $upload_file)){
        if(in_array($file_ext,$ext_arr)){
             $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
             rename($upload_file, $img_path);
             $is_upload = true;
        }else{
            $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);
        }
    }else{
        $msg = '上传出错!';
    }
}
  • 创建一个允许上传的文件类型数组 $ext_arr 包含 'jpg', 'png', 'gif'。

  • 获取上传文件的名称和临时文件路径。

  • 使用 strrpos() 函数获得文件名中的最后一个点的位置,并使用 substr() 函数获取文件的后缀名。

  • 构建完整的上传路径 $upload_file,其中 UPLOAD_PATH 是一个常量,表示存储上传文件的目录。

  • 使用 move_uploaded_file() 函数将临时文件移动到上传路径,如果移动成功:

  • a. 使用 in_array() 函数检查文件扩展名是否在允许的列表中。

  • b. 如果在允许的列表中,生成一个新的文件名 $img_path,其中包含了一个随机数、日期和时间以及原始文件的扩展名。

  • c. 使用 rename() 函数将文件重命名为新的文件名。

  • d. 设置 $is_upload 为true,表示上传成功。

  • e. 如果文件扩展名不在允许的列表中,则使用 unlink() 函数删除上传的临时文件。

解题步骤

从源码来看,服务器先是将上传的文件保存下来,然后将文件的后缀名同白名单对比,如果是jpg、png、gif中的一种,就将文件进行重命名。如果不符合的话,unlink()函数就会删除该文件。

但是就算是这样,我们也可以直接上传一个图片马进行包含就好了啊

不过需要注意的是这道题并没有给出一个文件包含的环境,也就是说作者的目的是不让我们使用文件包含图片马来做

这时候就要引入条件竞争漏洞的知识:

服务器端在处理不同的请求时是并发进行的,要知道代码执行的过程是需要耗费时间的,如果我们能在上传的一句话木马文件被删除之前访问就行了呗。这个就是条件竞争漏洞绕过。

人力肯定不靠谱,我们可以利用burp多线程发包,不断在浏览器访问我们的webshell,会有一瞬间的访问成功。

#webshell文件内容
');
phpinfo();
?>

然后再利用一个从别人那里copy来的python脚本,一直访问我们上传的文件

import requests
url = "http://127.0.0.1/upload-labs-master/upload/tm.php"
while True:
    html = requests.get(url)
    if html.status_code == 200:
        print("OK")
        break

因为我们只需要访问就行了,所以不需要设置payload

文件上传常用绕过方式_第40张图片

线程可以设置大一点,如果是在虚拟机里运行,做好卡死的准备,就可以开始攻击了

文件上传常用绕过方式_第41张图片

等python文件访问成功后,就可以在文件上传的目录下生成一个shell.php文件

估计是请求多了,我关掉之后还在运行生成和删除我上传的shell文件,搞得我靶场环境环境直接崩掉了

文件上传常用绕过方式_第42张图片

pass19

//index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
    require_once("./myupload.php");
    $imgFileName =time();
    $u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
    $status_code = $u->upload(UPLOAD_PATH);
    switch ($status_code) {
        case 1:
            $is_upload = true;
            $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
            break;
        case 2:
            $msg = '文件已经被上传,但没有重命名。';
            break; 
        case -1:
            $msg = '这个文件不能上传到服务器的临时文件存储目录。';
            break; 
        case -2:
            $msg = '上传失败,上传目录不可写。';
            break; 
        case -3:
            $msg = '上传失败,无法上传该类型文件。';
            break; 
        case -4:
            $msg = '上传失败,上传的文件过大。';
            break; 
        case -5:
            $msg = '上传失败,服务器已经存在相同名称文件。';
            break; 
        case -6:
            $msg = '文件无法上传,文件不能复制到目标目录。';
            break;      
        default:
            $msg = '未知错误!';
            break;
    }
}

先包含了myupload.php文件,具体信息可以在目录里查看,挺多的

代码,通过创建一个 MyUpload 对象,先是将文件后缀跟白名单做了对比,然后检查了文件大小以及文件是否已经存在。文件上传之后又对其进行了重命名。

所以我们这里要传入图片马,并利用python脚本结合问价包含访问我们上传的文件

这里有点奇怪哈,不知道是不是环境的问题,上传文件时利用burp抓包后就会发现第一次改名的文件已经在上传路径下生成了,但是发包后又会生成一个二次改名后的文件,应该是我环境的问题

文件上传常用绕过方式_第43张图片
文件上传常用绕过方式_第44张图片

这里还是继续实验,条件竞争吧

成功后就可以连接成功了

文件上传常用绕过方式_第45张图片

你可能感兴趣的:(文件上传,文件包含,php,upload)