记一次利用回车绕过拦截上传并代码审计原因

容器环境:Apache
解析器:PHP

Content-Disposition: form-data; name="myfile[]"; filename="phpinfo.php
.jpg"
Content-Type: image/png
Image description

获得shell后开始对代码进行解析

Image description

从上图可以看到这里将我们上传的图片文件绑定到了某个msgID之中。并且在上面还有引入一段

include("upload_file.php");

在代码下方并未找到存储文件的代码,所以存入文件的代码应该写在了upload_file.php

随即我们开始解析该文件代码

    <?php
      //指定儲存檔案的目錄
      $upload_dir =  "./download/";
			
      for ($i = 0; $i < 2; $i++)
      {		

        //若檔案名稱不是空字串,表示上傳成功,將暫存檔案移至指定之目錄
        if ($_FILES["myfile"]["name"][$i] != "")
        {
			$file_ext = pathinfo($_FILES["myfile"]["name"][$i], PATHINFO_EXTENSION);
			$file_ext = strtolower($file_ext);

			//modified by ling 20210118,判斷合法副檔名
			//if( $file_ext != 'php'){
			if (in_array($file_ext,array('pdf','ppt','pptx','doc','docx','jpg')))
			{

				  //搬移檔案
				  $temp_file = explode(".",$_FILES["myfile"]["name"][$i]);
				  $temp_name = date("YmdHis_"). $i ."." . $temp_file[1];
			  
				   $upload_file = $upload_dir .  $temp_name ;
				  if (move_uploaded_file($_FILES["myfile"]["tmp_name"][$i], $upload_file))
				  {
					  //顯示檔案資訊		
					 // echo "檔案名稱:" . $temp_file[0] . "<br>";
					  //echo "在資料夾的檔案名稱:" . $temp_name . "<br>";								
					  //echo "<hr>";
					  if( $i == "0" )
					  {
						 $temp_file1 = $temp_name;
						 $file1 = $temp_file[0];
					  }
					  if( $i == "1" )
					  {
									$temp_file2 = $temp_name;
									$file2 = $temp_file[0];
					   }
								
					} //if (move_uploaded_file
					else
					{
						echo "檔案上傳失敗 (" . $_FILES["myfile"]["error"] . ")<br><br>";
						echo "<a href='javascript:history.back()'>重新上傳</a>";
					} //else (move_uploaded_file

			} //if (in_array($file_ext

        } //if ($_FILES["myfile"]["name"]
		
      } //for ($i = 0; 

    ?>

利用for循环1 – 2,然后利用 $_FILES[“myfile”][“name”][$i] != “” 判断其中的文件名不为空。

利用 pathinfo 解析文件名,形成一个数组

Image description

$file_ext = pathinfo($_FILES["myfile"]["name"][$i], PATHINFO_EXTENSION);

通过这个 PATHINFO_EXTENSION 属性,判断是在取文件名的后缀。

$file_ext = strtolower($file_ext); 将后缀转换为统一小写

Image description

通过上图分析出当初这里采用的黑名单验证,后改为白名单验证,取后缀名称来与数组里面的白名单后缀匹配,当只有匹配到了后缀才会进入该代码区间。

我们通过层层打印

var_dump($file_ext);die;

Image description

发现打印出来的值的确是 jpg

Image description

那么他是如何绕过了in_array函数进行到下一步的呢?

随即我们打印下面这段代码的布尔值

var_dump(in_array($file_ext,array('pdf','ppt','pptx','doc','docx','jpg')));die;

得到结果 bool(true)

Image description

所以问题出现在何方?

原因

因为我们的后缀的确是 jpg结尾,并且也的确满足in_array函数的要求,但在下方保存文件名的时候出现了问题。

//搬移檔案
$temp_file = explode(".",$_FILES["myfile"]["name"][$i]);
				  
$temp_name = date("YmdHis_"). $i ."." . $temp_file[1];

$upload_file = $upload_dir .  $temp_name ;

其中我们最主要关注 explode(".",$_FILES["myfile"]["name"][$i]);

他的打印结果我们来看看。

Image description

其中最主要的是在第二段的取值: $temp_name = date("YmdHis_"). $i ."." . $temp_file[1];

他这里最大的缺陷在于取了数组中的固定值,这里如果他使用 end 函数,默认只取最后一个就能完美修复该漏洞。

例如:end 函数取值。

Image description

最终达到修复该漏洞

Image description

所以并不是我们的回车绕过了漏洞,而是他的内部取值出现了问题。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容