PHP任意文件上传CVE-2015-2348 漏洞分析

03 Apr 2015 - Tr3jer_CongRong

当时发现这个Vulnerability Summary for CVE-2015-2348漏洞公告的时候,果然是字符截断导致的,存在于PHP高版本中,多数安全研究者都在说漏洞怎么怎么鸡肋的,经过测试表明,还是要看利用的场景而异说明是不是真的很鸡肋.

漏洞分析:

move_uploaded_file()函数,这个函数是将上传的文件移动到新位置.

move_uploaded_file ( string $filename , string $destination )

源代码:ext/standard/basic_functions.c

这个函数拿php-5.3.x & php5.6.x作比较,发现高版本此函数的长度比较判断被干掉了……

if (strlen(path) != path_len) {
	RETURN_FALSE;
}

if (strlen(new_path) != new_path_len) {
	RETURN_FALSE;
}

strlen(new_path)为move_uploade_file()-$destination参数的长度(\0截断的话只计算截断前面的字符长度),在PHP中,所有的变量都是用一个结构_zval来保存的,而new_path_len对应着结构体中的int len,由于是二进制保存的,所以计算长度的时候空字符以及后面的字符都会包含在内.

那么当空字符截断的时候,由于长度不等,strlen(new_path) != new_path_len即为FALSE,低版本的截断上传则无效.

漏洞证明:

Windows7 & PHP5.4.x

upload.php

<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>upload</title>
</head>
<body>
	<form action="" method="post" enctype="multipart/form-data">
		 <input type="file" name="file">
		 <input type="submit">
	</form>
</body>
<?php 
echo "<pre>";
var_dump($_FILES);
echo "</pre>";
	move_uploaded_file($_FILES['file']['tmp_name'], "tmp/eval.php\x00.jpg");
?>
</html>

可以看出上传一个.jpg格式的木马在经过move_uploaded_file()转存的时候,截断生效了,截断上传一直都是个简单粗暴的漏洞,但是无脑黑们对这个漏洞不怎么感冒,认为谁会将转存的地址通过$_POST之类的接收呢?

鸡肋吗?

个人觉得这个漏洞并不是一文不值的,换个角度,以程序员的角度思考下,什么情况下$destination参数需要外部接收呢?

demo.php

<html>
<meta content="text/html" charset="utf-8">
<body>

<form action="" method="post" enctype="multipart/form-data">
<label>选择图片:<label>
<input type="file" name="file" />
<input type="hidden" name="address" value="<?php echo time(); ?>">
<br />
<input type="submit" value="Submit" />
<br />
</form>

</body>
<?php
error_reporting(0);

$upload_name=$_FILES['file']['name'];
$type=substr($upload_name,strrpos($upload_name,'.')+1);

if($type == "jpg" || $type == "png" || $type == "gif"){

	$address=$_POST['address'].".".$type;

	if (move_uploaded_file($_FILES['file']['tmp_name'],"tmp/".$address)) {
		echo "图片地址:tmp/".$address;
	}
	
}else{
echo "上传类型错误!";
}

?>
</html>

抓包截断,将time()生成的数字后面加上.php和一个空字符:

截断上传成功后从文件名上也会发现漏洞存在于此函数:

只是个简单的测试demo,因场景而异漏洞还是会存在的,毕竟\0是C的特性,当演变成了漏洞后,就要看开发者是否认真对待.

  • $destination不要使用$_GET或者$_POST来接收
  • $_FILES[‘file’][‘name’]不受影响,推荐作为$destination参数
  • 漏洞涉及的版本存在于PHP5.4.38-5.6.6