• 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();?>即可完成绕过,或者如果是xffxd8xff我们需要在文件开头先写上%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) == "xffxd8xff" or substr($bytes,0,3)=="x3fx3fx3f"){
    	    		return "image/jpeg";
    	    	}
    	    	if($bytes == "x89PNGx0dx0a"){
    	    		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>
    

    上传之目录穿越:

    <?php
    	// upload/dir/loop/
    	header("Content-type: text/html;charset=utf-8");
    	if(isset($_POST['submit']))
    	{
    		$uploaddir = "upload/" . $_POST['UPLOAD_DIR'] . "/";
    		var_dump($uploaddir);
    		if(file_exists($uploaddir))
    		{
    			if(move_uploaded_file($_FILE['upfile']['tmp_name'], $uploaddir . "/" . $_FILE['upfile']['name']))
    				echo "文件上传完成: " . $uploaddir . $_FILE['upfile']['name'] . "
    ";
    			else
    				echo "上传失败.";
    		}
    		else
    		{
    			echo "文件目录不存在: /upload/file/";
    		}
    	}
    ?>
    
    <body>
    
    
    	<script type="text/javascript">
    		function checkFile()
    		{
    			var file = document.getElementsByName('upfile')[0].value;
    			if(file == null || file=="")
    				return false;
    		}
    	</script>
    
    	<form action enctype="multipart/form-data" method="post" name="upload">
            <input type="hidden" name="MAX_FILE_SIZE" value="204800">
            <input type="hidden" name="UPLOAD_DIR" value="file">
            选择文件:
            <input type="file" name="upfile">
            <input 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>
    

    文件上传: 通过判断文件Content-Type来拒绝非图片文件上传,并且验证文件后缀,基于黑名单验证,一般会使用白名单验证。

    <html>
    	<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>
    </html>
    
    <?php
    define("UPLOAD_PATH", "./tmp");
    $is_upload = false;
    
    if (isset($_POST['submit'])) {
        if (file_exists(UPLOAD_PATH)) {
        	# 判断文件MIME (Content-Type: image/jpeg) 参数,该参数是可以伪造的!
            if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png')) {
                $temp_file = $_FILES['upload_file']['tmp_name'];
                $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
                # 下面则是采用黑名单验证机制,用于验证文件后缀不在这些列表中的文件
                $deny_ext = array(".php",".PhP",".pHp",".phP",".html",".htm",".htaccess",".ini");
                $file_name = $_FILES['upload_file']['name'];  # 取出文件的名字
                $file_suffix = strrchr($file_name, '.');      # 取出FileName的后缀 .jpg
                $file_suffix = strtolower($file_suffix);      # 强制将后缀转换为小写
                $file_suffix = trim($file_suffix);            # 首尾去空
                if (!in_array($file_suffix, $deny_ext)){
                	if (move_uploaded_file($temp_file, $img_path)) {
    		            $is_upload = true;
    		            echo "上传图片成功: " . $img_path . "<br>";
    		    	} else {
    		           $is_upload = false;
    		            echo "上传图片失败!" . "<br>";
    		    	}
                    }
            }
        }
    }
    ?>
    

    版权声明: 本博客,文章与代码均为学习时整理的笔记,博客中除去明确标注有参考文献的文章,其他文章【均为原创】作品,转载请务必【添加出处】,您添加出处是我创作的动力!

    警告:如果您恶意转载本人文章,则您的整站文章,将会变为我的原创作品,请相互尊重!
  • 相关阅读:
    requests模块
    Html5五子棋
    html5学习之旅第一篇
    Vue.js学习和第一个实例
    electron安装到第一个实例
    mongodb学习-练习实例
    nosql学习一
    csv内存流文件流
    关于Vue中img的src属性绑定的一些坑
    java中的==操作符和equals函数
  • 原文地址:https://www.cnblogs.com/LyShark/p/13426101.html
Copyright © 2020-2023  润新知