目录
文件上传漏洞存在的原因
产生原因
上传文件时,如果未对上传的文件进行严格的验证和过滤,就容易造成文件上传漏洞,上传脚本文件(包括asp、aspx、php、jsp等)
漏洞危害
恶意上传行为可能导致网站甚至整个服务器被控制。恶意的脚本文件被称为WebShell,WebShell具有强大的功能,如查看服务器目录、服务器中文件、执行系统命令等。
脚本及作用服务器类型
asp、aspx:IIS
php:apache
jsp:tomcat
JS检查绕过原理及方法
案例
upload-labs Pass-01
检查代码
<script type="text/javascript">
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;//查询元素的 name 属性
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型;
//substring截取字符串的某部分
//lastIndexOf是从字符串末尾开始检索,检索到子字符,则返回子字符在字符串中的位置
//截取最后一个.号后的字符串
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
//indexOf搜索从指定字符位置开始,并检查指定数量的字符位置。
//如果allow_ext不包含ext_name则执行if命令
if (allow_ext.indexOf(ext_name) == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
</script>
绕过方式
浏览器删除过滤脚本
- 查看源码并删除触发函数
- 选择上传脚本
- 上传
BurpSuite抓包
- 上传.png文件
- BurpSuite抓包
- 修改文件名
- 查看文件是否上传成功
- 登入大马
文件后缀绕过原理及方法
绕过方式1
修改相似文件名
- 例:php、php3、php5、phtml等
- 条件:服务器可以解析该文件类型
打开配置文件 -- httpd-conf(PHPStudy配置文件解析)
Ctrl+F -- 搜索字符串:AddType -- 找到解析配置的内容位置
添加我们需要的解析脚本后缀
案例
upload-labs Pass-03
选择上传文件
burpsuite抓包
修改包信息
将后缀名改为php5
上传脚本
通过BurpSuite重发器发送并获取重命名后的文件
验证大马是否上传成功
登入大马
检查代码
<code class="line-numbers language-php">$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array('.asp','.aspx','.php','.jsp');//黑名单
$file_name = trim($_FILES['upload_file']['name']);//获取上传文件的name属性
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');//strrchr:查找字符在指定字符串中从右面开始的第一次出现的位置,如果成功,返回该字符以及其后面的字符,如果失败,则返回 NULL
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //trim:除去字符串开头和末尾的空格或其他字符
if(!in_array($file_ext, $deny_ext)) {//in_array:检查数组中是否存在某个值
$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)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
</code>
绕过方式2
结合Apache文件解析机制
- Apache文件解析从右向左开始解析文件后缀,若后缀名不可识别,则继续判断直到遇到可解析的后缀为止。
案例
upload-labs Pass-04
选择上传文件
BurpSuite抓包
修改包信息并上传脚本
验证脚本
登入大马
检查代码
<li id="show_code">
<h3>代码</h3>
<pre>
<code class="line-numbers language-php">$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".ini");//黑名单
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');//strrchr:查找字符在指定字符串中从右面开始的第一次出现的位置,如果成功,返回该字符以及其后面的字符,如果失败,则返回 NULL
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
</code>
</pre>
</li>
绕过方法3
利用windows特性
- 在程序开发部署的时候,没有考虑到系统的特性会导致限制被绕过。利用Windows特性,可以在后缀名后加“.”或者“::$DATA”绕过
案例
upload-labs Pass-07:通过文件名后缀加空格绕过
上传脚本并抓包
修改包信息并发送到服务器
验证脚本上传
因为在windows服务器中会自动去除空格。所以访问的是重命名后的.php文件,没有空格
upload-labs Pass-08:通过文件名加后缀名“.”绕过
上传脚本并抓包
修改包信息并发送到服务器中
验证脚本
因为在windows服务器中会自动去除.所以访问的是hack.php文件,没有“.”
upload-labs Pass-09:通过文件名后缀加::$DATA绕过
上传脚本并抓包
修改包信息并发送到服务器中
验证脚本
因为在windows服务器中会自动去除::$DATA。所以访问的是重命名后的.php文件,没有::$DATA
检测代码对比
//Pass-07验证代码:通过添加空格可以绕过原因是代码没有对首尾去空
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = $_FILES['upload_file']['name'];
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
//Pass-08验证代码:通过添加空格可以绕过原因是代码没有对后缀名后面的"."进行删除
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
//Pass-09验证代码:通过添加空格可以绕过原因是代码没有对后缀名后面的"::$DATA"进行删除
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = trim($file_ext); //首尾去空
文件类型绕过原理及方法
绕过方式1
MIME绕过
案例
upload-labs Pass-02
选择上传文件
burpsuite抓包
修改文件名和文件格式
上传
验证上传脚本
检测代码
//后台代码对上传的文件格式进行了设置,只允许服务器规定的文件格式上传。$_FILES["file"]["type"]的值是从请求数据包中Content-Type中获取
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}
绕过方式2
文件头检测
案例
upload-lads Pass-14
上传脚本并抓包
将修改包信息发送到服务器
给脚本加上gif的头部值
验证脚本
http://xxx.xxx.xxx.xxx/upload-labs/include.php?file=upload/2320230224033250.gif访问图片马
检测代码
通过getReailFileType函数获取文件的文件头部值来识别文件类型
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读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;
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
绕过方法
- 在图片后添加脚本代码
- 在脚本文件开头补充图片对应的头部值
jpg:FF D8 FF E0 00 10 4A 46 49 46
gif:47 49 46 38 39 61
png:89 50 4E 47
文件截取绕过原理及方法
绕过方式
00截断
检测原理
- 由于00表示结束符,PHP会把00后面的所有字符删除
- 配置条件
- PHP版本 > 5.3.4
- magic_quotes_gpc(魔术引号)为OFF状态
绕过条件
- 用户文件被重命名了
- 文件上传路径是用户可控的
GET上传00截断
案例
upload-labs Pass-12
上传脚本并抓包
修改包信息并发送到服务器中
验证脚本
页面存在没有显示,通过查看靶机upload目录可以查看
检测代码
//从代码中可以发现红线处,对上传的文件名进行重新拼接,使用$_GET传参
//可以通过%00进行截断上传
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
POST上传00截断
案例
upload-labs Pass-13
上传脚本并抓包
修改包信息并发送到服务器中
- 修改文件名后缀确保符合白名单,然后在上传路径/upload后添加文件2.php以做00截断
- 进入Hex中找到之前修改的.php后修改为00
修改前
修改后
上传脚本成功
脚本验证
文件内容绕过原理及方法
检测原理
一些网站文件检测逻辑是先允许上传任意文件,然后检查文件内容是否包含可执行脚本,如果包含则删除。
绕过方法
利用成功上传到删除文件的时间差,上传一个.php文件,在未删除之前立即访问,则会自动生成一个新的php文件,新文件不会被删除
<?php
fputs(fopen('./1.php','w'),'<?php phpinfo();?>');
?>