漏洞危害
文件上传漏洞的危害非常严重,攻击者通过利用这类漏洞可以对受影响的Web应用程序造成严重的后果。文件上传漏洞的主要危害有:
- 远程代码执行:文件上传漏洞可能导致远程代码执行,这意味着攻击者可以在服务器上执行任意恶意代码。通过上传包含恶意代码的文件,攻击者可以在服务器端执行系统命令,完全控制目标服务器。这样的攻击可能导致服务器崩溃、敏感数据泄露、用户信息被窃取等严重后果。
- 服务器控制:通过上传恶意文件,攻击者可以获取对服务器的非授权访问,进而完全控制服务器。攻击者可以修改服务器配置、删除重要文件、安装后门、远程访问服务器等,对服务器进行长期控制。
- 数据泄露:攻击者可以上传恶意脚本或web shell,通过执行脚本获取服务器上的敏感数据,如数据库信息、用户凭据、用户个人信息等。泄露敏感数据可能导致隐私泄露、用户信任受损等问题。
- 网站篡改:攻击者可以上传恶意文件,修改网站内容,展示虚假信息或广告,损害网站的声誉和可信度。
- 恶意传播:攻击者可以上传恶意文件,将Web应用程序作为恶意文件的传播媒介,通过操纵上传文件在其他用户间传播恶意内容。
- 服务拒绝:攻击者可以通过上传大量文件或恶意文件,占用服务器资源,导致服务器性能下降,甚至服务拒绝,影响正常用户的访问。
- 网站黑名单:当攻击者使用Web应用程序上传恶意文件或执行违法活动时,Web应用程序的IP地址可能会被列入黑名单,导致其无法正常访问其他网站或服务。
综上所述,文件上传漏洞的危害严重,可能导致服务器受到完全控制、数据泄露、网站篡改、服务拒绝等问题。
修护思路
修复文件上传漏洞是保障Web应用程序安全的重要措施。修复文件上传漏洞的一些主要思路和建议有:
- 文件类型验证:在接收上传文件时,对文件类型进行验证是非常重要的。开发者应该明确指定允许上传的文件类型,并严格校验上传文件的MIME类型,而不仅仅依赖于文件的扩展名来判断文件类型。可以使用白名单的方式,只允许特定的文件类型通过验证。
- MIME类型验证:除了验证文件类型,还应该对文件的MIME类型进行验证。攻击者可能会伪造请求头中的Content-Type字段,欺骗服务器,因此服务器应该对上传文件的MIME类型进行独立验证,确保其与文件实际内容相符。
- 文件名安全处理:不应该将用户上传的文件名直接用于保存文件,而是对文件名进行安全处理。最好使用随机生成的文件名,并确保文件名不包含特殊字符和路径分隔符,以防止目录遍历攻击。
- 禁止执行权限:上传的文件应该被存储在不可执行的目录下,确保恶意文件不会被执行。对于上传的图片等非可执行文件,最好禁止其执行权限。
- 病毒扫描:在接收上传文件后,对文件进行病毒扫描是一种有效的安全措施。可以使用常见的病毒扫描引擎对上传的文件进行扫描,确保不会上传含有病毒或恶意代码的文件。
- 安全的存储位置:上传的文件应该存储在非Web可访问目录下,防止攻击者直接访问上传的文件。开发者可以在配置文件中指定存储目录,并确保Web服务器没有权限直接访问该目录。
- 后端验证:不仅仅依赖前端的验证,后端服务器也应该对上传文件进行验证。即使前端验证被绕过,后端也能进行有效的检查和过滤。
- 定期检查:定期检查服务器上的上传文件,及时发现异常或可疑文件,并进行处理。删除不必要的上传文件,确保服务器的安全性。
- 安全上传组件:使用经过安全测试和验证的上传组件或库,这些组件通常会提供更多的安全特性和选项,有助于预防文件上传漏洞。
- 安全审计:进行安全审计,定期对Web应用程序进行安全测试和漏洞扫描,及时发现并修复潜在的文件上传漏洞。
修复文件上传漏洞需要综合多种措施,从前端到后端,从验证到存储,确保上传文件的安全性和合法性。开发者应该重视文件上传功能的安全性,并在设计和实现过程中遵循安全最佳实践。定期对Web应用程序进行安全审计和漏洞扫描,及时修复潜在的漏洞,保障Web应用程序的安全性。
漏洞利用过程
第五关
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 = 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
文件,查看提示,上传目录存在php文件(readme.php
)。也就是上传目录下已经有 PHP
文件了。
因为 .ini
后缀的文件可以上传,所以我们可以上传一个.user.ini文件来改变服务器配置。
具体方法:上传一个.user.ini
,再上传一个图片马,包含起来进行getshell。不过前提是含有 .user.ini
的文件夹下需要有正常的php文件,否则也不能包含了(正常的文件即本身自带的 readme.php
)。
.user.ini
是一个PHP配置文件,用于在特定目录中配置和覆盖PHP的运行时设置。该文件类似于.htaccess
文件,但是作用范围更为局限,只对所在目录及其子目录生效。.user.ini
文件可以包含一系列PHP配置指令,用于修改PHP运行时的各种设置。
ini
auto_prepend_file = shell.png
auto_prepend_file
是一个PHP配置指令,用于指定一个在每个PHP文件执行之前自动包含的文件。在这里,shell.png
是被指定的文件名,意味着在每个PHP文件执行之前,PHP会自动包含shell.png
这个文件。
这样的配置可能是一个文件包含漏洞的后果,攻击者可能成功地上传一个伪装成图片的恶意PHP脚本,然后通过.user.ini
文件中的指令,让PHP在每个执行的PHP文件之前都自动包含该文件。这样,恶意代码将被执行,并可能导致远程代码执行或其他安全问题。
第一步:首先上传一个 .user.ini
文件
第二步:然后再上传一个一句话木马文件,名字需要与 .user.ini
配置的文件名相同
图 5-1. 成功上传并执行
第六关
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",".ini");
$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 . '文件夹不存在,请手工创建!';
}
}
经过与第五关对比我们发现没有过滤大小写
缺少代码
php
$file_ext = strtolower($file_ext); //转换为小写
将文件后缀名改为 .Php
绕过。
图 6-1. 抓包修改
图 6-2. 成功上传并执行
第七关
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",".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
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 . '文件夹不存在,请手工创建!';
}
}
没有进行首尾去空
缺少代码
php
$file_ext = trim($file_ext); //首尾去空
我们可以进行空格绕过,修改后缀名为 .php[空格]
。
空格绕过修改文件后缀名为 .php
的原理是利用文件系统对文件名的解析方式。在某些文件系统中,如果文件名包含空格,空格后的内容将被视为文件名的一部分,而不会被视为后缀名。这就意味着,当文件名包含空格并以 .php
结尾时,服务器可能会将其当作 .php
文件来解析,从而执行其中的 PHP 代码。
图 7-1. 抓包修改
图 7-2. 成功上传并执行
第八关
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",".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); //首尾去空
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 . '文件夹不存在,请手工创建!';
}
}
缺少代码
php
$file_name = deldot($file_name)
没有删除文件名末尾后的点
所以进行点绕过。
修改文件后缀名为 .php.
修改文件后缀名为 .php.
利用的是某些操作系统和Web服务器的解析机制。在一些操作系统和服务器配置中,当文件名以 .php.
结尾时,服务器可能会将其当作 .php
文件来解析,而忽略掉后面的多余点号。
假设一个Web应用程序允许用户上传图片文件,并对文件名进行简单的验证,只检查是否以 .jpg
、.png
等图片后缀名结尾。攻击者可以上传一个名为 image.jpg
的图片文件,而不会被拦截。然后,攻击者在文件名后面添加多余的点号和 .php
,即 image.jpg..php
,这样文件名看起来仍然是 image.jpg
,但实际上是 image.jpg..php
。由于服务器可能会忽略掉后面的多余点号,因此它可能会将该文件当作 .php
文件来解析。
图 8-1. 抓包修改
图 8-2. 成功上传并执行
第九关
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",".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 . '文件夹不存在,请手工创建!';
}
}
缺少代码
php
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
上传后缀名添加::$DATA
::$DATA
是一个流传输,可以把后面的数据当成流处理和.空格类似。在某些操作系统中,如果在文件名后添加 "::$DATA
",系统会将它视为一个特殊的数据流,而不是文件的一部分。例如,Windows操作系统中的NTFS文件系统支持使用 "::$DATA
" 表示一个特殊的数据流,这种数据流通常不会被用户直接访问。而在一些Web服务器中,可能没有对上传文件名进行严格的处理,导致可以通过在文件名后添加 "::$DATA\
" 来绕过文件类型检查。
图 9-1. 抓包修改
图 9-2. 成功上传并执行