【upload-labs】pass-09~pass-12详解

【upload-labs】pass-09~pass-12详解


【pass-09】过滤逻辑绕过

1、源码分析
#index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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)) {
            if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
                $img_path = $UPLOAD_ADDR . '/' . $file_name;
                $is_upload = true;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
    }
}

从上面的代码可以看出,前面几乎所有的过滤姿势都用上了,貌似没有办法绕过了。

但是细心审计代码发现,原来天机在deldot()函数中:

#common.php

function deldot($s){
	for($i = strlen($s)-1;$i>0;$i--){
		$c = substr($s,$i,1);  //substr(string,start,length)第一个字符从0开始
		if($i == strlen($s)-1 and $c != '.'){
			return $s;
		}

		if($c != '.'){
			return substr($s,0,$i+1);
		}
	}
}
?>

函数没啥问题,就是循环检查,如果末尾是点就向前移一位,知道不是点了就取该位置之前的字符串。

这里的漏洞在于,参数在进行层层过滤的最后没有再次使用deldot()函数来去除末尾的点。

所以可以构造文件名为muma.php. . ,也就是php后面加点空格点空格,这样经过上面代码层层过滤后变为.php.,可以成功绕过黑名单,并且利用windows特性,在保存文件时会自动去除后缀名末尾的点使得apache成功解析。

2、测试流程

老规矩,先尝试上传一个木马文件:

【upload-labs】pass-09~pass-12详解_第1张图片

用burp抓包,将后缀名改为.php. .

【upload-labs】pass-09~pass-12详解_第2张图片

重放,上传成功!

查看上传文件:

【upload-labs】pass-09~pass-12详解_第3张图片

果然windows很贴心的去除了php后面的点。

用菜刀连接即可。

小结:

这一关属于过滤的逻辑处理不当造成的漏洞,没有对后缀的点循环过滤。


【pass-10】双写绕过

1、源码分析
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

        $file_name = trim($_FILES['upload_file']['name']);  //去除首尾空格
        $file_name = str_ireplace($deny_ext,"", $file_name);  //将黑名单中的字符串替换为空
        if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $file_name)) {
            $img_path = $UPLOAD_ADDR . '/' .$file_name;
            $is_upload = true;
        }
    } else {
        $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
    }
}

由上述代码可知,后端将上传的文件名后缀会根据一个黑名单进行匹配,并且替换为空,而且是一次替换,所以将后缀名双写即可绕过。

2、测试流程

先尝试上传:

【upload-labs】pass-09~pass-12详解_第4张图片

用burp抓包,将后缀php双写:

【upload-labs】pass-09~pass-12详解_第5张图片

重放,上传成功!但是后缀被改成了hpp???什么情况?

仔细一想哦,替换的逻辑是从前往后替换的,.phphpp先将前面的php替换为空,然后剩下hpp

所以换一个双写的位置:.pphphp这样应该就没问题了

【upload-labs】pass-09~pass-12详解_第6张图片

果然成功!然后用菜刀连接即可。

小结:

这一关的过滤逻辑与前面不同,是将敏感的后缀名替换为空,然后不管合不合法也会上传成功,在实际开发使用中会产生大量的垃圾文件占用存储空间,而且也会存在潜在的安全隐患,比如如果存在文件包含漏洞,该木马文件依然会被利用。

而这一关只是将敏感的后缀一次性替换为空,所以双写即可绕过,不过要注意双写的位置和替换的逻辑顺序。


【pass-11】GET%00截断

1、源码分析
$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类型文件!";
    }
}

这里采用白名单机制,若后缀名为jpg、png、gif中的一种,则将文件改名并保存。

2、测试流程

先上传一个php文件,发现只允许上传png、jpg、gif后缀的文件,据此判断因该是白名单过滤。

【upload-labs】pass-09~pass-12详解_第7张图片

用burp抓包看一下:

【upload-labs】pass-09~pass-12详解_第8张图片

如图所示,发现上传文件的保存路径是用post请求中提交上去的,那就说明路径可控,可以尝试用%00截断绕过。

%00是什么?

%00是NULL的十六进制表示形式,在操作系统读取文件时,当遇到NULL时就会认为文件已经结束,后面的内容将不再读取。

%00截断绕过的条件:

1、php版本小于5.3.4

2、magic_quotes_gpc=Off

magic_quotes_gpc(魔术引号开关)函数,当magic_quotes_gpc=On时在php中的作用是判断解析用户提示的数据,如包括有:post、get、cookie过来的数据的特殊字符(’、"、\、NULL)加上反斜线进行转义。

%00截断绕过的原理

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

这句php代码是用来设置上传文件保存的路径的,在正常情况下路径会这样设置../upload//随机数+时间qianxun.jpg

但是如果文件保存的路径可控,我们可以把路径改为../upload/1.php%00

然后传到后台拼接的逻辑为../upload/1.phpNULL/随机数+时间qianxun.jpg

但是在操作系统读取的时候,由于NULL的截断,所以只会读到../upload/1.php截至

此操作就相当于将上传的文件qianxun.jpg保存为../upload/1.php

因为qianxun.jpg只是改了后缀的木马文件,此时又因为截断绕过将文件重命名为1.php,所以可以成功被apache所解析。

开干!!

修改路径为../upload/1.php%00,重发:

【upload-labs】pass-09~pass-12详解_第9张图片

结果报错了,我们可以看一下报错信息,发现0被转义了。。。

可能是因为magic_quotes_gpc处于开启状态,为了复现漏洞,在php配置文件中更改magic_quotes配置为off并保存,然后重启apache。

【upload-labs】pass-09~pass-12详解_第10张图片

再次尝试:

【upload-labs】pass-09~pass-12详解_第11张图片

如图所示,上传成功!

查看上传的文件:

【upload-labs】pass-09~pass-12详解_第12张图片

用浏览器访问文件:

【upload-labs】pass-09~pass-12详解_第13张图片

小结:

%00截断使用的场景:

多数情况下都是抓包改文件名进行测试,在文件名后缀末尾加hex形式的%00,例如:filename=‘1.php[null].jpg’,但是在php中,$FILES['file']['name']在得到文件名时就已经被截断了,变为1.php所以无法逃过后端 if(in_array($file_ext,$ext_arr))的过滤。

还有一种情况:

如果文件名是通过url获取的,并且后台保存文件时是用$_REQUEST['file_name']$_GET['file_name']获取文件名拼接在路径后面的,比如:$file_path="upload/".$_REQUEST['file_name'].rand(10,99).date("YmdHis").".".$file_ext;,此时只需要将url参数中的值改为file_name=1.php%00.jpg,然后直接上传木马文件后缀改jpg;到后端的逻辑是,木马图片因为后缀是.jpg所以可以通过白名单过滤,好戏来了,$_REQUEST['file_name']得到的值为1.php%00.jpg,拼接在路径后就是upload/1.php[null].jpg+随机数+时间+后缀名,由于null的截断,所以最后保存的文件路径为upload/1.php。相当于将图片马直接改名为1.php,使apache成功解析!

再一种情况:

就是本关的情况,保存路径是从url传进去的,所以用户可控,直接将%00添加在路径中,保存时截断,效果异曲同工。


【pass-12】POST%00截断

1、源码分析
$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;  
        //将post传递的文件保存路径直接拼接到了最终的路径中
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        }
        else{
            $msg = "上传失败";
        }
    }
    else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}
2、测试流程

上传一个木马文件:

【upload-labs】pass-09~pass-12详解_第14张图片

用burp抓包:

【upload-labs】pass-09~pass-12详解_第15张图片

发现文件的保存路径是由post传递的。

尝试使用%00截断方法:

【upload-labs】pass-09~pass-12详解_第16张图片

将+的十六进制2b改为null的十六进制00,回车。

因为这一关文件的路径是通过post请求传递的,所以不能直接使用%00,因为后台不会自动解码,所以要添加hex形式的00。

更改前 更改后
【upload-labs】pass-09~pass-12详解_第17张图片 【upload-labs】pass-09~pass-12详解_第18张图片

【upload-labs】pass-09~pass-12详解_第19张图片

放包之后,如图所示上传成功!

用菜刀连接:

【upload-labs】pass-09~pass-12详解_第20张图片

成功!

小结:

这一关和上一关还是大同小异,只是路径的传递方式不同,也就意味着00的形式不同。上一关是GET传递,所以要用%00,这一关是POST传递,所以要用hex形式的00。

我觉得判断是否存在00截断绕过的可能,关键点在于后台上传文件保存的路径用户是否可控,无论是用户传入的文件名还是保存路径,是否直接拼接到了最终的路径中,若是那就危险了。


你可能感兴趣的:(php,安全,web)