文件上传漏洞学习
文件上传漏洞学习
文件上传漏洞是指用户上传了一个可执行脚本文件,并通过此文件获得了执行服器端命令的能力。在大多数情况下,文件上传漏洞一般是指上传 WEB 脚本能够被服务器解析的问题,也就是所谓的 webshell 问题。完成这一攻击需要这样几个条件,一是上传的文件能够被 WEB 容器执行,其次用户能从 WEB 上访问这个文件,最后,如果上传的文件被安全检查、格式化、图片压缩等功能改变了内容,则可能导致攻击失败。
文件上传漏洞类型判断
以下记录upload-labs靶场通关记录
靶场包含的漏洞类型
Pass-01 前端校验白名单
此关为前端验证文件类型白名单,源码如下
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name) == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
法一 抓包改名绕过
将写好的一句话木马 1.php 修改文件名为 1.jpg 然后上传,用bp抓包,在数据包中重新修改文件名为 1.php 完成绕过
法二 改前端js
直接修改前端js函数,在控制台Console中重写checkFile函数,白名单中加入.php即可
注:如果在element编辑框内修改代码,白名单中添加php,修改的代码并不会生效,因为chrome已经在内存里加载了这段代码,要重新加载代码只能靠刷新,然而刷新会丢失我们所作的修改。
Pass-02 后端校验Content-Type
此题仅为后端校验数据包的Content-Type,抓包修改即可
Content-Type: image/jpeg
Pass-03 后端.asp,.aspx,.php,.jsp黑名单
此关为后端黑名单,不允许上传.asp,.aspx,.php,.jsp后缀文件
采用 .php3 等后缀绕过即可
注:需在配置文件中支持解析的php文件后缀名才可以解析
Pass-04 后端黑名单预处理不完善 .htaccess绕过
此关为后端黑名单,过滤大多数脚本后缀,但未过滤特殊文件 .htaccess (Apache服务器特有的分布式配置文件)
该文件可以理解为是一个httpd.conf
<FilesMatch "4.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
代码意思为 将 4.jpg 当作 php 文件执行
之后将写好的 4.php 文件重命名为 4.jpg 成功上传
使用蚁剑成功连接
Pass-05 后端黑名单预处理不完善 .user.ini绕过
此次后端黑名单比上一关新增了 .htaccess
多次尝试未果后查看提示 上传目录存在php文件(readme.php)
根据网络题解先上传.user.ini
auto_prepend_file=5.jpg
再上传带有一句话木马的 5.jpg
这样readme.php 就可以自动包含5.jpg文件 蚁剑连接readme.php即可
但本地环境存在问题未成功,可能是服务器未使用CGI/FastCGI模式 ,暂时未解决
Pass-06 后端黑名单预处理不完善 大小写绕过
与Pass-05不同为删除以下语句
$file_ext = strtolower($file_ext); //转换为小写
所以可以采用 .PHp 后缀直接绕过
Pass-07 后端黑名单预处理不完善 空格绕过
与Pass-05不同为删除以下语句
$file_ext = trim($file_ext); //首尾去空
所以上传 7.php 在bp中抓包加空格
Pass-08 后端黑名单预处理不完善 .绕过
与Pass-05不同为删除以下语句
$file_name = deldot($file_name);//删除文件名末尾的点
所以上传 8.php 在bp中抓包加 .
Pass-09 后端黑名单预处理不完善 ::$DATA绕过
与Pass-05不同为删除以下语句
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
::$DATA为Windows下NTFS文件系统的一个数据流属性,此内容后续文本会当做数据流处理,依次绕过后缀名检测
Pass-10 后端黑名单预处理不完善 . .绕过
过滤了.和空格但只过滤了一次
. .即可绕过
Pass-11 字符串替换,双写绕过
源码中
$file_name = str_ireplace($deny_ext,"", $file_name);
即替换黑名单中字符串为空字符串,双写后缀 .pphphp
Pass-12 13 白名单 上传路径可控截断
利用条件:
- php版本小于5.3.4
- php的magic_quotes_gpc为OFF状态
%00截断原理
截断的核心,就是chr(0)这个字符 这个字符不为空(Null),也不是空字符(“”),更不是空格。 当程序在输出含有chr(0)变量时 chr(0)后面的数据会被停止,换句话说,就是误把它当成结束符,后面的数据直接忽略,这就导致漏洞产生
12源码如下
$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类型文件!";
}
}
可以看到白名单过滤,且save_path是通过get获得的
抓包如图
save_path末尾增加 7.php%00
filename改为 7.jpg 绕过后缀名白名单
合成后的路径为 ../upload/7.php%00/7.jpg
截断后等效为 ../upload/7.php
上传成功
13源码如下
$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 = $_POST['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类型文件!";
}
}
与12相同,区别仅在save_path是通过post获得的
同样采用%00截断,但这次需要在二进制中进行修改00,因为post不会像get对%00进行自动解码。
其他同理
Pass-14
源码如下
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 = "上传出错!";
}
}
}
后端校验文件前2字节判断文件类型,与后缀名无关
JPEG (jpg),文件头:FFD8FF
PNG (png),文件头:89504E47
GIF (gif),文件头:47494638
所以在数据包中加上文件头绕过即可
参考资料
https://github.com/c0ny1/upload-labs