zl程序教程

您现在的位置是:首页 >  其他

当前栏目

PHP 文件上传漏洞代码

2023-02-18 16:46:22 时间

文件上传在PHP中经常被使用到,例如上传一个图片,上传一个文本等,文件上传如果在编写时过滤不够严格则很有可能导致漏洞的产生,如下代码是针对文件上传漏洞的总结,学习这些问题代码可以更好的查缺补漏弥补问题。

只验证MIME类型: 代码中验证了上传的MIME类型,绕过方式使用Burp抓包,将上传的一句话小马*.php中的Content-Type:application/php,修改成Content-Type: image/png然后上传.

<?php
	header("Content-type: text/html;charset=utf-8");
	define("UPLOAD_PATH", "./");

	if(isset($_POST['submit']))
	{
		if(file_exists(UPLOAD_PATH))
		{
			// 判断 content-type 的类型,如果是image/png则通过
			if($_FILES['upload_file']['type'] == 'image/png')
			{
				$temp_file = $_FILES['upload_file']['tmp_name'];
				$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
				if (move_uploaded_file($temp_file, $img_path))
					echo "上传完成.";
				else
					echo "上传出错.";
			}
		}
	}
?>

<body>
	<form enctype="multipart/form-data" method="post">
        <input class="input_file" type="file" name="upload_file">
        <input class="button" type="submit" name="submit" value="上传">
    </form>
</body>

白名单的绕过: 白名单就是允许上传某种类型的文件,该方式比较安全,抓包上传php后门,然后将文件名改为.jpg即可上传成功,但是有时候上传后的文件会失效无法拿到Shell.

<?php
	header("Content-type: text/html;charset=utf-8");
	define("UPLOAD_PATH", "./");

	if(isset($_POST['submit']))
	{
		if(file_exists(UPLOAD_PATH))
		{
			$allow_ext = array(".jpg",".png",".jpeg");

			$file_name = trim($_FILES['upload_file']['name']); // 取出文件名
			$file_ext = strrchr($file_name, '.');
			$file_ext = str_ireplace('::$DATA', '', $file_ext); //去除字符串::$DATA
			$file_ext = strtolower($file_ext);                  // 转换为小写
			$file_ext = trim($file_ext);                        // 首尾去空

			if(in_array($file_ext, $allow_ext))
			{
				$temp_file = $_FILES['upload_file']['tmp_name'];
				$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
				 if (move_uploaded_file($temp_file,$img_path))
				 	echo "上传完成: {$img_path} <br>";
				 else
				 	echo "上传失败 <br>";
			}
		}
	}
?>

<body>
	<form enctype="multipart/form-data" method="post">
        <input class="input_file" type="file" name="upload_file">
        <input class="button" type="submit" name="submit" value="上传">
    </form>
</body>

白名单验证文件头: 本关主要是允许jpg/png/gif这三种文件的传输,且代码中检测了文件头的2字节内容,我们只需要将文件的头两个字节修改为图片的格式就可以绕过.

通常JPEG/JPG: FF D8 | PNG:89 50 | GIF:47 49 以JPEG为例,我们在一句话木马的开头添加两个11也就是二进制的3131,然后将.php修改为.jpg,使用Brup抓包发送到Repeater模块,将HEX编码3131改为FFD8点Send后成功上传JPG.

<?php
	header("Content-type: text/html;charset=utf-8");
	define("UPLOAD_PATH", "./");

	function getReailFileType($filename)
	{
	    $file = fopen($filename, "rb");
	    $bin = fread($file, 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;
	}

	if(isset($_POST['submit']))
	{
		if(file_exists(UPLOAD_PATH))
		{
			$temp_file = $_FILES['upload_file']['tmp_name'];
    		$file_type = getReailFileType($temp_file);
    		 if($file_type == 'unknown')
    		 {
		        echo "上传失败 <br>";
		    }else
		    {
		        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
		        if(move_uploaded_file($temp_file,$img_path))
		        	echo "上传完成 <br>";
		    }
		}
	}
?>

<body>
	<form enctype="multipart/form-data" method="post">
        <input class="input_file" type="file" name="upload_file">
        <input class="button" type="submit" name="submit" value="上传">
    </form>
</body>

绕过检测文件头: 这种方式是通过文件头部起始位置进行匹配的从而判断是否上传,我们可以通过在上传文件前面追加合法的文件头进行绕过,例如在文件开头部位加上GIF89a<?php phpinfo();?>即可完成绕过,或者如果是\xffxd8\xff我们需要在文件开头先写上%ff%d8%ff<?php phpinfo(); ?>然后,选择特殊字符,右击CONVERT->URL->URL-Decode编码后释放.

<?php
	header("Content-type: text/html;charset=utf-8");
	define("UPLOAD_PATH", "./");

	function getReailFileType($filename)
	{
	    $fh = fopen($filename, "rb");
	    if($fh)
	    {
	    	$bytes = fread($fh,6);
	    	fclose($fh);
	    	if(substr($bytes,0,3) == "\xff\xd8\xff" or substr($bytes,0,3)=="\x3f\x3f\x3f"){
	    		return "image/jpeg";
	    	}
	    	if($bytes == "\x89PNG\x0d\x0a"){
	    		return "image/png";
	    	}
	    	if($bytes == "GIF87a" or $bytes == "GIF89a"){
	    		return "image/gif";
	    	}
	    }
	    return 'unknown';
	}

	if(isset($_POST['submit']))
	{
		if(file_exists(UPLOAD_PATH))
		{
			$temp_file = $_FILES['upload_file']['tmp_name'];
    		$file_type = getReailFileType($temp_file);
    		echo "状态: {$file_type} ";
    		 if($file_type == 'unknown')
    		 {
		        echo "上传失败 <br>";
		    }else
		    {
		    	$file_name = $_FILES['upload_file']['name'];
	    		$img_path = UPLOAD_PATH . "/" . $file_name;
		        if(move_uploaded_file($temp_file,$img_path))
		        	echo "上传 {$img_path} 完成 <br>";
		    }
		}
	}
?>

<body>
	<form enctype="multipart/form-data" method="post">
        <input class="input_file" type="file" name="upload_file">
        <input class="button" type="submit" name="submit" value="上传">
    </form>
</body>

图像检测绕过: 通过使用图像函数,检测文件是否为图像,如需上传则需要保持图像的完整性,所以无法通过追加文件头的方式绕过,需要制作图片木马上传.

针对这种上传方式的绕过我们可以将图片与FIG文件合并在一起copy /b pic.gif+shell.php 1.php上传即可绕过.

<?php
	header("Content-type: text/html;charset=utf-8");
	define("UPLOAD_PATH", "./");

	function getReailFileType($filename)
	{
		// 检查是否为图像
		if(@getimagesize($filename))
		{
			if(@imagecreatefromgif($filename)){
				return "image/gif";
			}
			if(@imagecreatefrompng($filename)){
				return "image/png";
			}
			if(@imagecreatefromjpeg($filename)){
				return "image/jpeg";
			}
		}
	    return 'unknown';
	}

	if(isset($_POST['submit']))
	{
		if(file_exists(UPLOAD_PATH))
		{
			$temp_file = $_FILES['upload_file']['tmp_name'];
    		$file_type = getReailFileType($temp_file);
    		echo "状态: {$file_type} ";
    		 if($file_type == 'unknown')
    		 {
		        echo "上传失败 <br>";
		    }else
		    {
		    	$file_name = $_FILES['upload_file']['name'];
	    		$img_path = UPLOAD_PATH . "/" . $file_name;
		        if(move_uploaded_file($temp_file,$img_path))
		        	echo "上传 {$img_path} 完成 <br>";
		    }
		}
	}
?>

<body>
	<form enctype="multipart/form-data" method="post">
        <input class="input_file" type="file" name="upload_file">
        <input class="button" type="submit" name="submit" value="上传">
    </form>
</body>

上传条件竞争: 这里是条件竞争,先将文件上传到服务器,然后判断文件后缀是否在白名单里,如果在则重命名,否则删除,因此我们可以上传1.php只需要在它删除之前访问即可,可以利用burp的intruder模块不断上传,然后我们不断的访问刷新该地址即可

<?php
	header("Content-type: text/html;charset=utf-8");
	define("UPLOAD_PATH", "./");

	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);
	             echo "上传完成. <br>";
	    	}else
	    	{
	    		unlink($upload_file);
	    		echo "上传失败. <br>";
	    	}
	    }
	}
?>

<body>
	<form enctype="multipart/form-data" method="post">
        <input class="input_file" type="file" name="upload_file">
        <input class="button" type="submit" name="submit" value="上传">
    </form>
</body>