1. 示例代码
搭建环境:PHP(8.1.19)+Nginx(1.24.0)
<?php
define('UPLOAD_FOLDER',
sys_get_temp_dir().DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR);
function validateFilePath($fpath) {
if (str_contains($fpath, '..' . DIRECTORY_SEPARATOR)) {
http_response_code(403);
die('invalid path!');
}
}
function uploadFile($src, $dest) {
$path = dirname($dest);
if (!file_exists($path)) {
mkdir($path, 0755, true);
}
if (move_uploaded_file($src, $dest)){
echo "上传成功";
} else {
echo "上传失败";
}
}
function normalizeFilePath($fpath) {
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Windows')) {
return str_replace('\\', '/', $fpath);
}
return $fpath;
}
2. 漏洞位置
if (str_contains($fpath, '..'.DIRECTORY_SEPARATOR))
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Windows')) {
return str_replace('\\', '/', $fpath);
3. 漏洞原理
示例代码存在不安全的文件路径处理,导致攻击者可以上传恶意文件到服务器指定目录。
当该代码部署在Linux服务器上时,代码中使用的validateFilePath函数检测上传的文件路径是否包含“../”,后续调用normalizeFilePath函数判断请求的User-Agent,如果是“windows”,则尝试规范化文件路径将反斜杠‘\’转换为斜杠‘/’,攻击者可以结合以上不安全的检查方式,构造特殊文件名和伪造用户代理字符串,如利用“..\”绕过检查,上传恶意文件到指定目录下。
假设web根路径为
“/home/wwwroot/default/”,攻击者可以上传如下文件,进行绕过:
"..\..\..\home\wwwroot\default\shell.php"
4. 漏洞复现
访问web页面,上传文件,burpsuite抓取请求包
更改filename="..\..\..\home\wwwroot\default\phpinfo.php"
利用"..\"来绕过validateFilePath函数的路径遍历检查
伪造User-Agent请求头,利用normalizeFilePath函数将“\”变为“/”,
则实际传递的filename=“../../../home/wwwroot/default/phpinfo.php”
文件上传成功,并保存在”/home/wwwroot/default/phpinfo.php”
输入http://192.168.230.128/phpinfo.php可以访问该页面:
5. 修复方案
1.改进文件路径验证:确保对上传的文件路径进行全面且严格的验证,不要仅仅依赖于字符串包含检查来验证文件路径。而是使用更强大的路径验证方法,如使用 realpath 函数或正则表达式来检查和规范化文件路径。
2.不依赖用户代理字符串:不要根据用户代理字符串来决定文件路径的分隔符,而是根据实际的文件系统来处理文件路径。不要假定客户端的操作系统,而是在服务器上进行文件路径的安全处。
3.限制上传目录:确保文件上传仅允许上传到安全目录,不要允许上传到 Web 根目录或其他重要目录。使用白名单控制来限制上传目录。
4.文件名安全处理:对上传的文件名进行安全处理,例如删除特殊字符、编码或过滤危险字符。不允许文件名包含路径分隔符或相对路径。
相关知识
str_contains()函数
str_contains() 函数是 PHP 8.0 版本引入的一个内置函数,用于检查一个字符串中是否包含另一个字符串并返回一个布尔值来指示是否存在,若存在返回true,不存在则返回false。它的作用类似于 strpos() 函数,但更简单直观。其基本语法如下:
$haystack:要搜索的字符串,即要在其中查找是否包含$needle。
$needle:要查找的子字符串,即要在 $haystack 中检查是否存在。
它不需要额外的索引或位置查找,在处理字符串包含性检查时更简洁和易读,特别是在需要检查多个字符串的情况下。尽管 str_contains() 函数提供了方便的字符串包含性检查,但在某些情况下可能导致漏洞或不安全的行为,例如路径遍历攻击、特殊字符绕过等。为了防止这些潜在的漏洞,建议在安全关键的场景中谨慎使用 str_contains(),并结合其他安全性措施,如路径规范化、输入验证、字符编码处理和权限控制,来确保应用程序的安全性。