去年冬天,漏洞一个做电商的补丁朋友半夜给我打电话,说服务器CPU跑满,漏洞网站卡死。补丁登录后台一看,漏洞Tomcat日志里全是补丁奇怪的请求——有人在利用AJP协议的文件包含漏洞往服务器上写webshell。他用的漏洞Tomcat 9.0.30,正好在CVE-2020-1938的补丁影响范围内。这个漏洞后来被叫做“幽灵猫”,漏洞攻击者可以通过8009端口读取应用目录下的补丁任意文件,甚至执行代码。漏洞他那个8009端口,补丁防火墙根本没拦,漏洞直接暴露在公网。补丁
这事让我意识到,漏洞很多人装完Tomcat就跑,从来不关心版本和补丁。Tomcat的漏洞不算少,但真正造成大规模入侵的,往往就是那几个没及时打的补丁。
翻一下Tomcat的安全公告列表,有几个漏洞是绕不过去的。除了前面说的CVE-2020-1938,还有CVE-2017-12615——Tomcat 7.0.0到7.0.79版本,在Windows环境下开启PUT方法后,攻击者可以直接上传JSP文件。这个漏洞利用起来简单到离谱,一个curl命令就能搞定。我当时在测试环境复现过,上传一个冰蝎马,三秒钟拿到shell。
再往前,CVE-2016-8735和CVE-2016-8735涉及JMX远程代码执行,影响Tomcat 9.0.0.M1到9.0.0.M11。这个漏洞需要JMX端口暴露,但很多运维图省事,直接把JMX开在公网,还没设认证。
还有个容易被忽略的:CVE-2022-42252。Tomcat 10.1.0-M1到10.1.0-M9、9.0.0.M1到9.0.67、8.5.0到8.5.83,当Tomcat前面有反向代理时,攻击者可以绕过请求过滤。这个漏洞不像文件上传那么直接,但配合其他漏洞能打出组合拳。
我见过最离谱的补丁操作:有人把Tomcat从9.0.30升级到9.0.31,然后问我为什么漏洞还在。因为CVE-2020-1938的修复版本是9.0.31没错,但他只替换了bin目录下的catalina.jar,其他jar包没动。Tomcat的补丁不是单个文件替换,是整个版本升级。
正确的做法是:先确认当前版本,去Apache Tomcat官网的Security页面查对应CVE的修复版本。比如CVE-2020-1938,9.0.x系列要升到9.0.31以上,8.5.x要升到8.5.51以上,7.0.x要升到7.0.100以上。升级时把整个webapps目录备份,conf目录下的server.xml、context.xml、web.xml单独备份,然后停服务,替换整个Tomcat目录,再把配置文件迁回去。
有个细节:如果你用了AJP协议,升级到9.0.31后,默认的secretRequired属性变成了true。这意味着你必须显式设置AJP的secret,否则连接会被拒绝。我那个朋友升级后网站反而起不来,就是因为这个。在server.xml的Connector里加上secret="你的随机字符串",同时前端反向代理也要配上。
有些老系统动不了,比如跑着十年前的SSH框架,升级Tomcat可能引发兼容性问题。这种情况下,至少要做三件事:第一,把AJP端口关掉,在server.xml里注释掉
我自己的习惯是,每季度扫一次Tomcat版本。用nmap扫8080和8009端口,看返回的Server头。如果显示Apache-Coyote/1.1,至少能确认是Tomcat。再结合版本特征判断具体版本号。这比等出事再查要主动得多。
补丁这事没有一劳永逸。Tomcat 9.0.x到现在还在更新,最新版本已经到9.0.98了。每次更新都可能修复新发现的漏洞。与其等漏洞被公开后再手忙脚乱,不如把版本更新纳入常规运维流程。我现在的做法是:测试环境先升,跑一周没问题,生产环境再跟。升级前用OpenVAS扫一遍,升级后再扫一遍,对比报告。虽然麻烦,但比半夜被叫起来处理入侵强。