常见的上传漏洞原理及一些简单绕过方法总结。
文件上传漏洞概述
几乎每个网站中都会有上传文件的地方,上传头像、视频、压缩包等等......代码写的不严谨,没有对上传的文件的文件名、文件类型等等进行严格限制的话,攻击者如果成功上传了恶意脚本文件就可以达到控制网站等目的。
常见上传漏洞分类客户端javascript校验
JavaScript校验是在浏览器进行的,也就是在数据发送到服务器之前,因此很容易绕过。
Document
上面的js代码在上传文件时进行了文件后缀和文件大小的限制。

我们选择的文件不在允许的文件后缀中就会弹出此提示框,绕过也很简单。
在浏览器设置中禁用JavaScript即可。


这样便可以绕过前端js验证,如果后端代码没有做限制的话即可上传成功。
服务端content-type字段校验(MIME)
关于MIME的介绍请自行百度百度,限制的地方是数据包中的content-type字段。
php上传代码:
<?php
if(isset($_GET['a'])){
echo ' ';
$a = $_GET['a'];
if($a==1){
//获取文件的大小
$file_size=$_FILES['myfile']['size'];
if($file_size>2*1024*1024) {
echo "文件过大,不能上传大于2M的文件";
exit();
}
$file_type=$_FILES['myfile']['type'];
echo $file_type;
if($file_type!="image/jpeg" && $file_type!='image/pjpeg') {
echo "文件类型只能为jpg格式";
exit();
}
//判断是否上传成功(是否使用post方式上传)
if(is_uploaded_file($_FILES['myfile']['tmp_name'])) {
//把文件转存到你希望的目录(不要使用copy函数)
$uploaded_file=$_FILES['myfile']['tmp_name'];
//我们给每个用户动态的创建一个文件夹
$user_path=$_SERVER['DOCUMENT_ROOT']."/up";
//判断该用户文件夹是否已经有这个文件夹
if(!file_exists($user_path)) {
mkdir($user_path);
}
//$move_to_file=$user_path."/".$_FILES['myfile']['name'];
$file_true_name=$_FILES['myfile']['name'];
$move_to_file=$user_path."/".$file_true_name;
//echo "$uploaded_file $move_to_file";
if(move_uploaded_file($uploaded_file,iconv("utf-8","gb2312",$move_to_file))) {
echo $_FILES['myfile']['name']."上传成功";
} else {
echo "上传失败";
}
} else {
echo "上传失败";
}
}
}
?>
代码中只允许image/jpeg和image/pjpeg这两种类型,其他类型则上传失败。

我们可以拦截数据包,修改content-type字段即可绕过MIME验证。


成功上传。
文件内容头校验
一些上传的地方会检测文件内容头判断是不是允许上传的文件类型PHP文件上传,不同类型的文件文件头也是不一样的。例如jpg图片:

验证文件头的代码:
<?php
class FileTypeValidation
{
// 文件类型,不同的头信息
private static $_fileFormats = Array(
'jpeg' => 'FFD8FF',
'jpg' => 'FFD8FF',
);
/**
* 检查文件类型
*
* @param string $filePath 文件路径
* @param string $fileExt 文件扩展名
*
* @return boolean
*/
public static function validation($filePath, $fileExt)
{
// 文件格式未知
if (!isset(self::$_fileFormats[$fileExt]))
{
return false;
}
$length = strlen(self::$_fileFormats[$fileExt]);
$bin = self::_readFile($filePath, $length);
$fileHead = @unpack("H{$length}", $bin);
// 判断文件头
if (strtolower(self::$_fileFormats[$fileExt]) == $fileHead[1])
{
return true;
}
return false;
}
/**
* 读取文件内容
*
* @param string $filePath 文件路径
* @param integer $size
*
* @return string
*/
private function _readFile($filePath, $size)
{
$file = fopen($filePath, "rb");
$bin = fread($file, $size);
fclose($file);
return $bin;
}
}
if(isset($_GET['a'])){
echo ' ';
$a = $_GET['a'];
if($a==1){
//获取文件的大小
$file_size=$_FILES['myfile']['size'];
if($file_size>2*1024*1024) {
echo "文件过大,不能上传大于2M的文件";
exit();
}
$file_type=$_FILES['myfile']['type'];
echo $file_type;
if($file_type!="image/jpeg" && $file_type!='image/pjpeg') {
echo "文件类型只能为jpg格式";
exit();
}
//判断是否上传成功(是否使用post方式上传)
if(is_uploaded_file($_FILES['myfile']['tmp_name'])) {
//把文件转存到你希望的目录(不要使用copy函数)
$uploaded_file=$_FILES['myfile']['tmp_name'];
$x = FileTypeValidation::validation($uploaded_file, 'jpg');
if(!$x){
echo "文件头验证失败";
exit();
}
//我们给每个用户动态的创建一个文件夹
$user_path=$_SERVER['DOCUMENT_ROOT']."/up";
//判断该用户文件夹是否已经有这个文件夹
if(!file_exists($user_path)) {
mkdir($user_path);
}
//$move_to_file=$user_path."/".$_FILES['myfile']['name'];
$file_true_name=$_FILES['myfile']['name'];
$move_to_file=$user_path."/".$file_true_name;
//echo "$uploaded_file $move_to_file";
if(move_uploaded_file($uploaded_file,iconv("utf-8","gb2312",$move_to_file))) {
echo $_FILES['myfile']['name']."上传成功";
} else {
echo "上传失败";
}
} else {
echo "上传失败";
}
}
}
?>
上面代码验证了文件头符合jpg类型的才可以上传成功。我们先随便上传一个文件。

上传失败,这时最简单的方法就是制作一张图片马,把脚本代码插入到图片文件的后面,这样文件头依然是原文件的文件头。


成功上传。
后缀名黑名单校验
顾名思义后缀黑名单就是明确指出了哪些后缀不允许上传,同样也是有一些方法可以绕过的。
后缀黑名单代码:
<?php
if(isset($_GET['a'])){
echo ' ';
$a = $_GET['a'];
if($a==1){
$file_size=$_FILES['myfile']['size'];
if($file_size>2*1024*1024) {
echo "文件过大,不能上传大于2M的文件";
exit();
}
$file_type=$_FILES['myfile']['type'];
echo $file_type;
if($file_type!="image/jpeg" && $file_type!='image/pjpeg') {
echo "文件类型只能为jpg格式";
exit();
}
//判断是否上传成功(是否使用post方式上传)
if(is_uploaded_file($_FILES['myfile']['tmp_name'])) {
$file = $_FILES['myfile']['name'];
$hou = substr(strrchr($file, '.'), 1);
if($hou == "php"){
echo "不允许上传php";
exit();
}
//把文件转存到你希望的目录(不要使用copy函数)
$uploaded_file=$_FILES['myfile']['tmp_name'];
//我们给每个用户动态的创建一个文件夹
$user_path=$_SERVER['DOCUMENT_ROOT']."/websec/up";
//判断该用户文件夹是否已经有这个文件夹
if(!file_exists($user_path)) {
mkdir($user_path);
}
//$move_to_file=$user_path."/".$_FILES['myfile']['name'];
$file_true_name=$_FILES['myfile']['name'];
$move_to_file=$user_path."/".$file_true_name;
//echo "$uploaded_file $move_to_file";
if(move_uploaded_file($uploaded_file,iconv("utf-8","gb2312",$move_to_file))) {
echo $_FILES['myfile']['name']."上传成功";
} else {
echo "上传失败";
}
} else {
echo "上传失败";
}
}
}
?>
上传你的File吧
上面代码限制了不允许后缀为php的文件上传。

代码中没有统一大小写,这时我们可以使用大小写绕过。

windows文件名不区分大小写,文件中的代码依然可以正常解析执行。
也可以尝试00截断等方法。 00截断利用限制
PHP<5.3.29,且GPC关闭
后缀名白名单校验
很好理解,白名单是明确限制了只允许哪些后缀上传,其他的都不允许,一般白名单要比黑名单安全的多,但也可以结合解析漏洞、00截断等方法尝试绕过。
还有一些其他情况如检测文件内容、二次渲染等等。 同样绕过方法也还有很多,如上传特殊可解析后缀、上传.htaccess、双后缀名、伪协议等等,各种骚姿势可以根据情况自己慢慢挖掘。
不是每个上传点都存在可以绕过的上传漏洞,获得权限的方法不止一种,无法突破的也不要钻牛角尖。
(编辑:成都站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|