前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >PHP 文件上传代码审计

PHP 文件上传代码审计

作者头像
王 瑞
发布于 2022-12-28 09:18:06
发布于 2022-12-28 09:18:06
1.1K00
代码可运行
举报
运行总次数:0
代码可运行

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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编码后释放.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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上传即可绕过.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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>

上传之目录穿越:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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'] . "\n";
			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模块不断上传,然后我们不断的访问刷新该地址即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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来拒绝非图片文件上传,并且验证文件后缀,基于黑名单验证,一般会使用白名单验证。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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>";
		    	}
                }
        }
    }
}
?>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
HTTPS证书申请及windows server部署
StartSSL免费DV证书 沃通(Wosign)免费DV证书 这两个已经被苹果拉入黑名单。我10月份在StartSSL申请的证书还可以使用。但是近期申请的已经不被信任,苹果设备上使用近期申请的证书都已经无法打开(亲测)。 如果证书无效或不被信任在微信企业号里面打开的时候会提示 网络出错,请轻触重新加载 -1202的提示。 -1202的错误的意思就是kCFURLErrorServerCertificateUntrusted
forrest23
2018/08/03
3.2K0
Cloudflare免费ssl证书设置
cloudflare是一家国外的 CDN 加速服务商,还是很有名气的。提供免费和付费的加速和网站保护服务。cloudflare提供了不同类型的套餐,即使是免费用户,cloudflare 提供的功能也是很全面的。
爱游博客
2019/08/07
23.6K0
Cloudflare免费ssl证书设置
关于免费SSL证书的那些事儿
根据 Let’s Encrypt CA 的统计,截至 2017 年 11 月,Firefox 加载的网页中启用 HTTPS 的比例占 67%,比去年底的 45% 有巨大提升。浏览器开发商如 Mozilla, Google 准备采取下一步措施:将所有 HTTP 网站标记为不安全。 随着 HTTPS 的普及,给网站加个 SSL 证书已经是大势所趋而且很有必要了。 目前已经存在不少免费好用的 SSL 证书,因此,本文就来盘点一下关于免费 SSL 证书的那些事儿。 一、Let’s Encrypt 官方网站:htt
Zip
2018/07/20
2K0
域名启用 HTTPS 加密功能更安全,国内免费 SSL 证书对比
屈建山
2017/08/07
3.8K0
WordPress整站轻松开启HTTPS
Jianbo
2017/12/29
4K0
WordPress整站轻松开启HTTPS
服务器不支持ssl怎么回事,客户端和服务器不支持一般 SSL 协议版本或加密套件 解决方法…
此网站无法提供安全连接 www.huichengff.com 使用了不受支持的协议。
全栈程序员站长
2022/09/05
16.8K0
IP也可以申请SSL证书开启https?其实已经有许多靠谱又便宜的国产品牌支持
公网IP作为常用的互联网访问,对于IP安全性目前协议上传输是非常重要的。网站需要IP地址实现HTTPS加密,那么IP能申请SSL证书吗?答案是可以的,目前,JoySSL已经支持IP安装SSL证书实现HTTPS保护了。
学长陶小桃
2022/10/26
13.6K0
IP也可以申请SSL证书开启https?其实已经有许多靠谱又便宜的国产品牌支持
申请UK2商家Comodo PositiveSSL免费SSL证书完整过程
对于我们用户来说,即便网站没有使用SSL也没有多大的关系,但是从安全角度考虑,尤其是用户交互的网站平台是必须要使用SSL安全证书的,这样数据在传输过程中可以起到安全作用。应该是在去年的时候,Google提出来如果有网站采用SSL(HTTPS)地址模式,同等条件下会优先排名展示,至此包括国内的主流搜索引擎提供商也类似的都加入这些因素。
老蒋
2021/12/27
1.4K0
申请UK2商家Comodo PositiveSSL免费SSL证书完整过程
分享一个免费SSL证书申请网站,给网站开启https协议
这些天,由于公司的业务需求,接触到了 ssl 证书和 https 协议。博客前几篇文章也分享了在 WEB 服务器上安装 SSL 证书,为网站开启 https 协议的教程,感兴趣的童鞋可以前往查看相关文
张戈
2018/03/23
3.9K0
分享一个免费SSL证书申请网站,给网站开启https协议
IIS 使用 Let’s Encrypt 证书部署 HTTPS 站点
Let’s Encrypt(https://letsencrypt.org ) 是可以签发免费 SSL / TLS 证书的 CA 机构,它是为普及 HTTPS 而发起的,推动了基础 DV SSL 证书的普及。其证书已经被 Mozilla、Google、Microsoft 和 Apple等主流浏览器支持,只需要 web 服务器配置好 HTTPS 证书,浏览器会在加载时验证 web 服务器 HTTPS证书是否有效。 使用 Let’s Encrypt 一个很重要的理由是免费,避免 ISP 劫持;还有申请速度快、无需注册账户等优点。在对比了众多免费 CA 后,Let’s Encrypt是比较方便和理想的,它提供了基础 DV SSL证书,只提供了数据加密;不验证身份,无法向用户证明网站的所有者。但即使这样也满足了基本需要了。
Savalone
2020/02/11
4.5K0
IIS 使用 Let’s Encrypt 证书部署 HTTPS 站点
HTTPS证书知识扫盲
现在搞网站域名不加个HTTPS就显得不专业,特别在使用JWT进行认证的接口一定要加HTTPS为你的接口增加一层安全屏障。今天就来聊聊配置HTTPS的关键SSL证书,也被称为CA证书。
码农小胖哥
2020/09/28
1.8K0
免费SSL证书申请及部署实践
网络上关于如何签发免费SSL证书的博文一大片,但是真正操作起来的能让新手不迷惑的却很少,很多操作步骤受限于国内无法访问外网的阻碍,导致无法真正实施成功。 实际上,关于申请免费SSL证书主要涉及两大部分: 第一,如何快速申请到一个免费的DV证书(通常免费的证书都是DV证书,DV证书对于个人或者测试用途足够了)。 第二,成功申请到证书之后如何配置部署,使得网站可以通过HTTPS访问。
编程随笔
2022/09/08
4.9K0
免费SSL证书申请及部署实践
给网站安装免费的通配SSL证书,轻松实现HTTPS并自动续签
出于安全考虑,现在大部分的网站都已经配置了SSL证书,直观的感觉就是现在大部分的网站都是HTTPS,而不是HTTP了。甚至,Chrome 从90版本开始,就已经是默认请求HTTPS:
Mintimate
2024/08/14
2.5K1
给网站安装免费的通配SSL证书,轻松实现HTTPS并自动续签
DNSPod十问濮灿:中国网站的SSL证书即将断供?
问答时间:2020年8月20日 嘉宾简介: 濮灿:沃通电子认证有限公司总经理,360政企安全云安全事业部总经理、360未来安全研究院云安全研究院院长。从事多年技术开发和技术团队建设,对Linux内核、网络协议栈、高性能网络、DNS解析服务、WEB安全、SDP、SDWAN等多个技术方向有开发和项目经验。擅长网络安全和云安全方向,曾任上海牙木通讯有限公司研发总经理,盛大创新院高级研究员,上海聚流软件科技有限公司CEO,现主要负责360云安全和虚拟化安全的技术架构和产品市场战略。 主持人简介: 吴洪声(人称
腾讯云DNSPod团队
2020/08/21
2.9K0
证书签发机构StartCom也被曝签发假证书
位于以色列埃拉特的证书颁发机构StartCom 之前已经被大部分浏览器取消信任,但前几天被曝光签发了假的证书,包括最高信任级别的 EV 证书。 StartCom StartCom 是一家位于以色列埃拉特的证书颁发机构。2016年10月24日,Mozilla在其安全博客上宣布,由于在对证书颁发机构沃通(360 旗下)数个问题的调查中发现它收购了StartCom,而交易双方并未披露此事,Mozilla将从Firefox 51开始,停止信任2016年10月21日后签发的证书。2016年9月1日,Google也
FB客服
2018/02/28
1.8K0
证书签发机构StartCom也被曝签发假证书
ssl证书怎么申请?网站ssl证书申请
随着搜索引擎巨头百度、谷歌等大力倡导网站https加密访问,以及加强互联网信息安全建设的需要,越来越多的网站、系统开始实现https加密。网站实现https加密需要用到SSL证书,那么网站SSL证书如
沃通WoTrus数字证书
2023/02/27
18.2K0
ssl证书怎么申请?网站ssl证书申请
使用Certify来自动申请并配置Let’s Encrypt免费SSL证书到IIS8
越来越多的网站在启用HTTPS,也就是SSL加密通讯连接访问。特别是去年开始BAT在国内的推广和应用要求。要知道部署发布一个苹果iOS企业应用,下载服务器就必须使用HTTPS协议。
崔文远TroyCui
2019/02/26
2.9K0
使用Certify来自动申请并配置Let’s Encrypt免费SSL证书到IIS8
分享几个免费SSL证书申请网站,给网站开启HTTPS协议
全民 HTTPS 时代来临,分享几个免费 SSL 证书申请网站,给网站开启 HTTPS 协议。 一、腾讯云 申请非常简单,打开页面并登陆之后点击申请证书,填写要申请的域名之后在解析一个 DNS 记录用于验证,十分钟不到就可以颁发证书了!如果是使用 dnspod 或腾讯云解析的域名,则可以全自动验证,坐等颁发即可。 申请地址:点击直达 二、阿里云 和腾讯云基本一致,使用的是赛门铁克的免费 1 年单域名证书,申请页面较为难找,而且在购买页面,略显麻烦,点开购买地址后选择【免费 DV SSL】并提交 购买地址:点
沈唁
2018/05/24
4.1K0
如何获取免费的 SSL 证书?
SSL 证书作为保障网站安全的关键工具,其重要性不言而喻。它不仅能加密数据传输,保护用户隐私,还能提升网站的可信度和搜索引擎排名。对于预算有限的个人站长和小型企业来说,免费的 SSL 证书无疑是保护网站安全的最佳选择,本文将简单介绍SSL证书的加密原理和几种获取免费证书的方式。
星尘安全
2025/05/15
4070
如何获取免费的 SSL 证书?
免费申请通配符域名SSL证书
域名 SSL 证书是数字证书的一种,用于校验服务器身份,同时具有数据加密传输的特性。在开启 SSL 证书之后不仅能够避免敏感信息如帐号密码等遭窃听,同时还能启用 http/2 等高级特性。在最新版的 Chrome 浏览器中,所有非加密链接的网站更是被标识为了不安全。同时我们可以看到,百度、淘宝、京东等网站也早已开启全站 https 加密连接,全民 SSL 时代即将来临。
reizhi
2022/09/26
22.3K0
免费申请通配符域名SSL证书
推荐阅读
相关推荐
HTTPS证书申请及windows server部署
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档