pass-11 黑名单-%00截断-get

黑盒测试

1.php上传失败,只允许上传如下格式,看来这次采用白名单了

upload-labs(11-15)_php

前端没有js脚本

upload-labs(11-15)_白名单_02

Content-Type: image/png,上传失败

没有头绪,看看提示吧,说上传路径可控?

upload-labs(11-15)_php_03

果然url栏写着路径,但是不知道有什么用

upload-labs(11-15)_白名单_04

源码分析

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

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}

1.strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。

  • 源码中是查找.在文件名中最后出现的位置

2.substr() 函数 返回其中一部分的字符串。

  • substr(string,start,length):start为开始截取部分

3.将后缀名给$file_ext

4.如果$file_ext在白名单内,就上传到规定的路径,文件命名为上传路径+10-99+年月日时分秒+$file_ext

看着无懈可击,有没有突破路径呢?

百度了一下,这道题要用00截断

%00截断

绕过原理

move_uploaded_file函数的底层实现类似于C语言,遇到0x00会截断,把后面部分删掉

截断条件:

1、php版本小于5.3.4,(现在php版本都8了)

2、php.ini的magic_quotes_gpc为OFF状态

注意

POST和GET不一样

%00 截断在 GET 中被 url 解码之后是空字符。

但是在 POST 中 %00 不会自动被 url 解码,用bp先对%00进行解码

这也是为什么上传后没有截断,因为是post上传

白盒测试

因为重命名是上传路径+10-99+年月日时分秒+$file_ext

在上传路径处进行修改

上传1.php,burpsuite进行修改

upload-labs(11-15)_白名单_05

表面上重命名成了:1.php%EF%BF%BD/6120210806180147.gif

保存的时候由于截断,变成了1.php

连接成功

upload-labs(11-15)_php_06

pass-12 黑名单-%00截断-post

黑盒测试

同样只允许上传图片类型

upload-labs(11-15)_白名单_07

抓包发现文件路径变成post上传了

upload-labs(11-15)_php_08

进行修改,同时将%00先解码

upload-labs(11-15)_php_09

文件名表面上是123.php%EF%BF%BD/3020210806184837.jpg

但是由于截断作用,实际上是123.php

连接成功

upload-labs(11-15)_字符串_10

源码分析

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_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类型文件!";
    }
}

除了改成POST上传,和pass-11没什么区别

pass-13白名单-文件包含漏洞-上传图片马-读文件前两个字节

黑盒测试

看看要求:

upload-labs(11-15)_字符串_11

意思是说这个系统有文件包含漏洞,让我们上传三种格式的图片马并连接成功

做图片马

做一个图片马的四种方法详细步骤

此外,在文件头加GIF89A也能绕过,目前还不清楚原理

/etc/shadow爆出绝对路径

upload-labs(11-15)_白名单_12

上传,连接shell

upload-labs(11-15)_上传_13

php版本要高一点,5.3以上,不然报错很难受

源码分析

function getReailFileType($filename){
    $file = fopen($filename, "rb");
    $bin = fread($file, 2); //只读2字节
    fclose($file);
    $strInfo = @unpack("C2chars", $bin);    
    $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);    
    $fileType = '';    
    switch($typeCode){      
        case 255216:            
            $fileType = 'jpg';
            break;
        case 13780:            
            $fileType = 'png';
            break;        
        case 7173:            
            $fileType = 'gif';
            break;
        default:            
            $fileType = 'unknown';
        }    
        return $fileType;
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_type = getReailFileType($temp_file);

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

1.fopen( )函数以二进制只读的方式打开文件

2.fread() 函数读取文件,只读两个字节,赋值给$bin

3.关闭文件

4.unpack() 函数是将二进制解包成其他形式,unpack("C2chars", $bin)是输出为两个无符号字符型

5.intval() 函数将两个字符转成10进制(默认)

6.比较10进制数来确定文件后缀$file_type

7.重命名为10-99+年月日时分秒+$file_type

因为存在文件包含漏洞,所以做一个图片马可以轻松绕过

pass-14 白名单-文件包含漏洞-上传图片马-getimagesize( )

黑盒测试

上传GIF成功绕过,连接成功

upload-labs(11-15)_白名单_14

但是jpg和和png文件包含时报错

upload-labs(11-15)_白名单_15

源码分析

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

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

1.getimagesize( )函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。

比如上传的gif文件(只加了个文件头GIF89A)

0=>2573;
1=>16188;
2=>1;
3=>width="2573" height="16188";
channels=>3;
mime=>image/gif;

上传的png图像

0=>473;
1=>406;
2=>3;
3=>width="473" height="406";
bits=>8;
mime=>image/png;

上传的jpg图像

0=>1920;
1=>1080;
2=>2;
3=>width="1920" height="1080";
bits=>8;
channels=>3;
mime=>image/jpeg;

2.image_type_to_extension根据指定的图像类型返回对应的后缀名。

  • 1 = GIF
  • 2 = JPG
  • 3 = PNG
  • 4 = SWF

3.stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写),此处是查找后缀名在白名单中的位置

4.如果在白名单中,就保留扩展名,进行重命名:10-99+年月日时分秒+扩展名上传

pass-15 白名单-文件包含漏洞-上传图片马-exif_imagetype()

黑盒测试

和pass-14,pass-13没什么不同,均绕过

upload-labs(11-15)_文件上传_16

源码分析

function isImage($filename){
    //需要开启php_exif模块
    $image_type = exif_imagetype($filename);
    switch ($image_type) {
        case IMAGETYPE_GIF:
            return "gif";
            break;
        case IMAGETYPE_JPEG:
            return "jpg";
            break;
        case IMAGETYPE_PNG:
            return "png";
            break;    
        default:
            return false;
            break;
    }
}

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

1.使用exif_imagetype( )函数判断一个图像的类型

  • exif_imagetype() 读取一个图像的第一个字节并检查其签名。
  • 如果发现了恰当的签名则返回一个对应的常量,否则返回 false
  • 返回值和 getimagesize() 返回的数组中的索引 2 的值是一样的,但本函数快得多。
  • 返回值是整型1,2,3等,不是IMAGETYPE_GIF,IMAGETYPE_JPEG