路径遍历漏洞深度解析:从原理到实战修复

📅 2026/6/25 14:34:33
路径遍历漏洞深度解析:从原理到实战修复
1. 项目概述与背景最近在梳理一些历史漏洞案例准备给团队做一次内部的安全意识培训。翻看资料时宏景eHR的DisplayExcelCustomReport接口任意文件读取漏洞CNVD-2023-08743引起了我的注意。这个漏洞本身原理不复杂但它的出现和利用过程非常典型地反映了在传统B/S架构企业管理软件中开发人员对文件路径安全校验的忽视以及攻击者如何利用这种疏忽进行信息探测和渗透。这类漏洞往往被归类为“低危”但其实际危害尤其是在内网环境中可能远超预期。它就像一把能打开服务器文件系统“后窗”的钥匙虽然不一定能直接拿到系统权限但足以让攻击者窥探到大量敏感信息为后续的攻击铺平道路。这个漏洞影响的是宏景eHR人力资源管理系统。这类系统在企业内部通常存放着员工最核心的个人信息如身份证号、薪资、家庭住址、银行账户等。一旦服务器的敏感文件被读取后果不堪设想。复现这个漏洞不仅能让我们理解其技术原理更重要的是去思考在代码审计和日常开发中我们该如何避免犯下类似的错误以及作为防守方该如何快速发现和修复这类问题。接下来我会从一个安全研究者的角度带你一步步拆解这个漏洞从环境搭建到漏洞利用再到深度分析和修复建议把整个过程掰开揉碎了讲清楚。2. 漏洞原理深度解析2.1 核心漏洞点未过滤的路径穿越符这个漏洞的核心在于DisplayExcelCustomReport接口通常对应一个Servlet或ASPX页面在处理report或file这类参数时直接拼接了用户输入且未对路径进行任何规范化或安全校验。攻击者可以通过输入包含路径穿越序列如../或..\的字符串来访问Web应用目录之外的文件。我们来模拟一下漏洞代码可能的样子。假设后端处理逻辑是用Java写的伪代码如下String userReportName request.getParameter(report); // 用户可控输入 String baseReportPath /opt/hjehr/webapps/reports/; String fullPath baseReportPath userReportName .xls; // 直接拼接 File reportFile new File(fullPath); // ... 读取文件内容并输出 ...如果攻击者传入report参数值为../../../etc/passwd那么拼接后的fullPath就变成了/opt/hjehr/webapps/reports/../../../etc/passwd.xls。经过操作系统路径解析后../会向上回退目录最终实际访问的路径就变成了/etc/passwd.xls。虽然加了.xls后缀但很多文件读取操作是二进制流读取后缀名不影响内容获取或者服务器配置了某些MIME类型映射导致文件内容被直接返回。为什么这种错误会发生过度信任客户端输入开发者潜意识里认为前端下拉框或固定链接传递过来的文件名是“安全”的却忽略了HTTP请求可以被轻易篡改。对路径解析机制理解不足认为在Web目录下拼接路径就是安全的没有意识到../这类序列会被操作系统内核解析从而跳出Web根目录。缺乏最小权限原则应用可能以较高权限如root或SYSTEM运行使得读取系统关键文件成为可能。2.2 漏洞利用的影响与危害评估很多人觉得任意文件读取Arbitrary File Read, AFR不如远程代码执行RCE危害大这种看法是片面的。在内网渗透中AFR往往是获取立足点的关键一步。通过这个漏洞攻击者可以读取哪些敏感文件呢系统配置文件/etc/passwd,/etc/shadow(Linux)c:\windows\system32\config\SAM(Windows) 用于获取系统用户哈希尝试破解或进行哈希传递攻击。应用配置文件数据库连接文件如web.config,application.properties,config.php里面通常明文或弱加密存储着数据库的IP、端口、用户名和密码。拿到数据库权限几乎等于拿到了整个系统的数据。日志文件应用日志、访问日志可能记录调试信息、其他用户的会话ID、甚至明文密码如果开发不小心记录了。源代码文件通过读取.java,.class,.jsp,.asp等文件可以进行白盒审计寻找更严重的二次漏洞如反序列化、SQL注入等。敏感数据文件对于eHR系统可能存在的员工信息导出文件、薪资表备份文件等。实操心得在实际渗透测试中我通常会先用这个漏洞尝试读取/proc/self/cwd/../WEB-INF/web.xmlLinux或类似路径来定位Web应用的绝对路径和配置文件位置这比盲目猜解要高效得多。一旦拿到数据库密码整个内网渗透的剧本就可能完全改写。3. 漏洞复现环境搭建与准备3.1 环境选择与搭建要点要复现这个漏洞你需要一个存在漏洞的宏景eHR系统环境。强烈建议在完全隔离的虚拟机或实验网络中进行切勿在生产环境或连接互联网的机器上尝试。通常有两种途径获取环境官方历史版本安装包通过网络资源寻找特定版本如受漏洞影响的8.0、9.0等老版本的安装包。安装过程可能比较复杂涉及数据库如SQL Server初始化、中间件如Tomcat、WebLogic配置等。漏洞靶场环境一些在线漏洞演练平台或开源漏洞靶场项目如Vulhub、VulnApp可能集成了该漏洞环境。这是最快捷、最安全的方式。这里以在本地虚拟机搭建一个简化模拟环境为例。我们不需要完整的宏景eHR只需模拟其漏洞接口。步骤1创建模拟Web应用使用Spring Boot快速搭建一个模拟接口。RestController public class VulnController { GetMapping(/DisplayExcelCustomReport) public void readFile(HttpServletRequest request, HttpServletResponse response) throws IOException { String reportName request.getParameter(report); // 模拟漏洞直接拼接用户输入未过滤 String filePath /reports/ reportName; File file new File(filePath); if (file.exists() !file.isDirectory()) { Files.copy(file.toPath(), response.getOutputStream()); response.setContentType(application/octet-stream); } else { response.getWriter().write(File not found.); } } }将应用打包运行其Web根目录假设为/app。步骤2准备“敏感”文件在虚拟机中创建一些用于测试的“敏感”文件。# Linux 环境 echo 模拟的/etc/passwd内容 /etc/passwd_test echo 模拟的数据库配置jdbc:mysql://localhost:3306/hjehr?userrootpasswordAdmin123 /app/WEB-INF/classes/application.properties # 在Web根目录下创建reports目录并放一个正常文件 mkdir -p /app/reports echo 这是正常的报表内容 /app/reports/normal.xls注意事项确保你的Java应用有权限读取/etc/passwd_test这类系统文件通常以root或非root用户运行权限不同。真实环境中宏景eHR可能部署在Windows Server IIS .NET环境下路径分隔符是\路径穿越符是..\。原理完全相通。3.2 工具准备复现过程主要使用浏览器和命令行工具即可但一些专业工具能提升效率浏览器Chrome或Firefox用于手动构造URL测试。Burp Suite渗透测试必备工具。它的Repeater模块可以方便地修改和重放HTTP请求Intruder模块可用于对路径进行模糊测试或爆破。cURL命令行下的HTTP客户端适合脚本化测试和验证。编码工具有时需要对特殊字符进行URL编码如../编码为%2e%2e%2f或双重编码以绕过一些简单的过滤。4. 漏洞手工复现与利用过程4.1 基础利用读取Web目录外文件假设我们的模拟应用运行在http://192.168.1.100:8080。第一步访问正常功能首先我们访问正常的报表链接观察其参数格式http://192.168.1.100:8080/DisplayExcelCustomReport?reportnormal这可能会成功下载/app/reports/normal.xls文件。第二步构造恶意参数现在我们尝试利用路径穿越读取系统文件。将参数修改为http://192.168.1.100:8080/DisplayExcelCustomReport?report../../../etc/passwd_test发送这个请求。第三步分析结果成功情况服务器返回了/etc/passwd_test文件的内容。响应头Content-Type可能是application/octet-stream二进制流或根据文件后缀猜测的类型。这直接证明了漏洞存在。失败情况返回“File not found”或类似错误。这可能是因为路径计算错误需要的../个数不对。需要根据Web根目录到目标文件的相对路径来计算。例如如果应用实际路径是/opt/tomcat/webapps/ROOT/要读到/etc/passwd可能需要../../../../etc/passwd。存在基础过滤服务器端对参数进行了一些处理比如删除../或限制了后缀。技巧如何确定穿越深度可以采用“爬目录”的方式。先尝试读取一个已知的、位于Web应用内的文件比如WEB-INF/web.xmlJava应用或global.asax.NET应用。通过不断增减../的个数直到成功读取到这个已知文件就能精准定位出Web应用的绝对路径。例如report../../WEB-INF/web.xmlreport../../../WEB-INF/web.xml... 一旦成功你就知道了从参数位置到Web根目录需要回退多少层。4.2 绕过常见过滤机制开发人员或WAF可能会实现一些简单的过滤我们需要尝试绕过。场景1过滤../字符串后端代码可能做了replace(../, )。我们可以尝试双写绕过..././过滤一次中间的../后剩下的./和前面的.结合可能又形成了../实际上更常用的是....//过滤掉中间的../变成..//在某些解析中可能被当作../。但更可靠的是下面两种。URL编码绕过将../编码为%2e%2e%2f。如果过滤发生在解码前则可能绕过。双重URL编码绕过%252e%252e%252f%被编码为%25。如果应用进行多次解码则可能生效。使用..\Windows如果服务器是Windows尝试使用反斜杠。使用绝对路径在某些配置错误的场景下直接传递绝对路径如/etc/passwd也可能生效。场景2后缀名限制接口可能强制添加或检查.xls后缀。我们的payload是../../../etc/passwd拼接后变成../../../etc/passwd.xls文件不存在。空字节截断在旧版本PHP或某些特定解析器中%00空字节可以截断后面的字符串。尝试../../../etc/passwd%00.xls。但Java和.NET高版本中此方法通常无效。利用文件系统特性在Linux下可以尝试读取/etc/passwd/后面加斜杠但通常不行。更有效的方法是目标文件可能本身就有.xls,.xml,.properties等后缀如我们之前创建的application.properties。尝试读取../../../WEB-INF/classes/application.properties即使加上.xls后缀因为文件真实存在也可能被读取。实操过程记录使用Burp Suite浏览器正常访问用Burp抓包。将请求发送到Repeater。修改report参数依次尝试以下payload观察响应长度和内容的变化../../../etc/passwd_test ..%2f..%2f..%2fetc%2fpasswd_test ..\..\..\windows\system32\drivers\etc\hosts (Windows路径) ../../WEB-INF/web.xml ....//....//....//etc/passwd_test如果响应体变长且内容包含预期的文本如“root:x:0:0”或“jdbc:mysql”则说明利用成功。5. 漏洞深入利用与信息收集成功实现任意文件读取后攻击就进入了“信息收集”阶段。目标是找到能进一步突破的“钥匙”。5.1 关键敏感文件定位清单以下是一张针对常见系统的敏感文件读取清单在实际测试中可按顺序尝试系统平台文件路径可能包含的敏感信息Linux/Unix/etc/passwd系统用户列表可用于用户名枚举/etc/shadow用户密码哈希需root权限/proc/self/environ当前进程环境变量可能含路径、密钥/proc/self/cmdline启动当前进程的命令行含完整路径/home/[用户名]/.bash_history用户历史命令可能含密码、密钥/root/.ssh/id_rsaroot用户的SSH私钥[WEBROOT]/WEB-INF/web.xmlJava Web应用配置含数据库连接等[WEBROOT]/config/database.phpPHP应用数据库配置WindowsC:\Windows\System32\config\SAM本地用户账户数据库需SYSTEM权限C:\boot.ini系统启动配置旧系统C:\Windows\win.ini系统基础配置C:\Windows\System32\inetsrv\config\applicationHost.configIIS配置文件含站点路径、权限[WEBROOT]\web.config.NET应用配置文件含连接字符串C:\Users\[用户名]\Desktop\passwords.txt用户桌面文件猜解用户名通用/应用相关[WEBROOT]/WEB-INF/classes/application.propertiesSpring Boot配置[WEBROOT]/META-INF/context.xmlTomcat数据源配置[WEBROOT]/config.json各种前端/Node.js应用配置../logs/error.log应用错误日志含堆栈跟踪、SQL语句../备份文件.zip可能存在的数据库备份文件注意读取/proc/self/mem或/dev/mem等内存设备文件通常会导致进程卡死或服务器负载飙升在测试中应避免。5.2 从信息泄露到权限提升假设我们通过漏洞成功读取到了WEB-INF/classes/application.properties内容如下spring.datasource.urljdbc:mysql://192.168.1.200:3306/hjehr_prod spring.datasource.usernamehjehr_user spring.datasource.passwordHJEhr#2023Prod!攻击链就此延伸数据库渗透直接使用得到的凭据连接内网数据库服务器192.168.1.200。可以导出所有员工数据甚至尝试通过数据库的特定功能如MySQL的INTO OUTFILE向Web目录写入木马获取Webshell。横向移动数据库密码往往在其他系统中复用。可以尝试用此密码登录管理后台、VPN、邮箱等系统。代码审计读取到的Java类文件.class可以通过反编译工具如JD-GUI查看源码寻找更深的漏洞如SQL注入、反序列化点等。我踩过的坑有一次测试中读取到的配置文件里密码是加密的。不要轻易放弃常见的加密方式如AES、DES密钥可能硬编码在代码里。继续读取相关的ConfigUtil.class等文件反编译后可能找到解密逻辑从而还原出明文密码。6. 漏洞修复方案与安全开发建议6.1 临时缓解与根本修复临时缓解措施运维层面WAF规则在Web应用防火墙WAF或网关设备上添加规则拦截请求参数中包含../、..\、%2e%2e等路径穿越序列的请求。权限最小化修改运行Web服务的系统账户如Tomcat的tomcat用户确保其只有Web应用目录及必要资源的读取权限无法读取/etc/、/windows/等系统目录。虚拟补丁如果使用的是Java Servlet过滤器或.NET HTTP模块可以在请求到达漏洞接口前对相关参数进行过滤和拒绝。根本修复方案开发层面修复的核心原则是对用户输入的文件名部分进行严格的白名单校验并规范化最终路径确保其位于允许的目录内。Java示例修复后public void readFile(HttpServletRequest request, HttpServletResponse response, String reportParam) throws IOException { String userReportName request.getParameter(reportParam); // 1. 白名单校验只允许字母、数字、下划线、短横线组成的文件名 if (!userReportName.matches(^[a-zA-Z0-9_-]\\.xls$)) { // 假设后缀必须是.xls response.sendError(HttpServletResponse.SC_BAD_REQUEST, Invalid report name.); return; } // 2. 定义安全的基准目录 Path safeBaseDir Paths.get(/opt/hjehr/webapps/reports/).normalize().toAbsolutePath(); // 3. 拼接用户输入并立即规范化 Path userPath Paths.get(userReportName).normalize(); // 注意这里不要使用resolve因为如果userPath是绝对路径会替换掉safeBaseDir Path resolvedPath safeBaseDir.resolve(userPath).normalize().toAbsolutePath(); // 4. 关键安全校验确保最终路径在基准目录之下 if (!resolvedPath.startsWith(safeBaseDir)) { response.sendError(HttpServletResponse.SC_FORBIDDEN, Access denied.); return; } File reportFile resolvedPath.toFile(); // ... 后续文件操作 ... }关键点解释normalize()方法会移除路径中的.和..解析符号链接取决于参数。例如/reports/../etc/passwd经过normalize()后会变成/etc/passwd。我们在校验startsWith之前调用它就能让../失效。startsWith()这是最核心的检查确保最终要访问的文件路径其前缀是我们指定的安全基准目录。任何试图跳出该目录的尝试都会被拦截。白名单优先如果业务上文件名是固定的几个使用白名单如从数据库已发布的报表列表中校验比黑名单过滤../要安全得多。6.2 安全开发规范建议为了避免此类漏洞团队应在开发流程中嵌入安全要求输入校验对所有用户输入的、用于文件系统操作的参数文件名、路径实施严格的白名单校验。路径安全API使用安全的API进行路径操作。在Java中使用java.nio.file.Path及其normalize()、startsWith()方法在Python中使用os.path.normpath()后检查前缀。上下文传递尽量避免在参数中传递文件路径。使用文件ID、索引等标识符在服务端通过映射表获取真实路径。代码审计将“路径遍历”作为代码审计的必查项。重点关注File,FileInputStream,Paths.get,include,require等函数或指令的参数是否用户可控。安全培训让开发人员理解路径遍历漏洞的原理和危害而不仅仅是记住要过滤../。7. 防御视角下的检测与响应7.1 如何发现环境中的此类漏洞作为防守方蓝队除了定期进行漏洞扫描还可以通过以下方式主动发现日志分析在Web访问日志中搜索包含../、..\、%2e%2e等特征的URL请求。这些很可能是攻击者的探测行为。HIDS监控部署主机入侵检测系统监控Web服务进程如java, tomcat, w3wp.exe对敏感文件如/etc/shadow,web.config的读取操作。一旦发现异常读取立即告警。RASP防护在应用层部署运行时应用自我保护可以精准地拦截对FileInputStream等类的危险调用当检测到参数包含路径穿越并试图跳出安全目录时实时阻断并记录。7.2 事件应急响应流程如果通过监控发现疑似利用该漏洞的攻击应立刻启动应急响应隔离如果可能暂时隔离受影响的主机或网络段。取证保存完整的攻击请求日志、服务器上的相关文件访问记录。评估根据攻击者读取的文件评估信息泄露的范围和严重程度如是否读到数据库密码。修复按照上述方案紧急修复漏洞。溯源检查攻击IP、攻击路径看是否已发生进一步入侵如数据库被连接。通知如果确认泄露了用户个人信息需根据相关法律法规启动通知流程。加固全面检查其他系统是否存在类似问题并实施权限最小化等加固措施。这个漏洞的复现和分析过程再次印证了安全领域那句老话“漏洞往往出现在最意想不到的简单逻辑里。” 作为开发写完代码后多问一句“这个参数用户完全可控吗”作为运维部署系统时多思考“这个账户真的需要这么高的权限吗”很多风险其实就能被扼杀在萌芽状态。安全不是某个阶段的任务而应该是贯穿整个软件生命周期的一种思维方式。