最近学习了师傅寻找的一些JNDI漏洞的利用链受益匪浅,自己也尝试关于JNDI漏洞利用做一些挖掘,目前JNDI在利用过程我想到了两个问题。
- 测试每一个JNDI Bypass 利用链都需要手动更改URL很不方便,能否我去请求一个地址,让目标将我所有的链跑一遍?
- JNDI利用过程中可以通过反序列化利用,能否自动化探测反序列化利用链?
自动测试Bypass 利用链
为了让这种方式更加通用,我们首先考虑的是JDK原生的实现ObjectFactory
的类,那么我注意到了下面几个类。
- com.sun.jndi.rmi.registry.RegistryContextFactory
- com.sun.jndi.ldap.LdapCtxFactory
RegistryContextFactory
调用分析
通过getURLs
从Reference获取url列表并封装为数组,URLsToObject
中对数组中的URL列表发起RMI
请求,所以RegistryContextFactory满足我们的需求。
public Object getObjectInstance(Object var1, Name var2, Context var3, Hashtable<?, ?> var4) throws NamingException {
//判断是否为引用对象并且factoryClassname为RegistryContextFactory
if (!isRegistryRef(var1)) {
return null;
} else {
//从引用对象中获取URL列表并循环发起调用
Object var5 = URLsToObject(getURLs((Reference)var1), var4);
if (var5 instanceof RegistryContext) {
RegistryContext var6 = (RegistryContext)var5;
var6.reference = (Reference)var1;
}
return var5;
}
}
- getURLs获取URL必须满足RefAddr是StringRefAddr类型且Type属性为URL才会保存。
private static String[] getURLs(Reference var0) throws NamingException {
int var1 = 0;
String[] var2 = new String[var0.size()];
Enumeration var3 = var0.getAll();
//从RefAddr中获取url并保存到数组中
while(var3.hasMoreElements()) {
RefAddr var4 = (RefAddr)var3.nextElement();
//只有RefAddr是StringRefAddr类型,且Type属性为URL才会保存
if (var4 instanceof StringRefAddr && var4.getType().equals("URL")) {
var2[var1++] = (String)var4.getContent();
}
}
if (var1 == 0) {
throw new ConfigurationException("Reference contains no valid addresses");
} else if (var1 == var0.size()) {
return var2;
} else {
//返回URL数组
String[] var5 = new String[var1];
System.arraycopy(var2, 0, var5, 0, var1);
return var5;
}
}
- URLsToObject中创建
rmiURLContextFactory
对象并调用getObjectInstance
。getObjectInstance
中判断传入的object类型如果是数组则调用getUsingURLs
.
private static Object URLsToObject(String[] var0, Hashtable<?, ?> var1) throws NamingException {
rmiURLContextFactory var2 = new rmiURLContextFactory();
return var2.getObjectInstance(var0, (Name)null, (Context)null, var1);
}
public Object getObjectInstance(Object var1, Name var2, Context var3, Hashtable<?, ?> var4) throws NamingException {
if (var1 == null) {
return new rmiURLContext(var4);
} else if (var1 instanceof String) {
return getUsingURL((String)var1, var4);
} else if (var1 instanceof String[]) {
//数组类型
return getUsingURLs((String[])((String[])var1), var4);
} else {
throw new ConfigurationException("rmiURLContextFactory.getObjectInstance: argument must be an RMI URL String or an array of them");
}
}
- getUsingURLs创建
rmiURLContext
并循环调用lookup发起RMI调用直到获取一个对象并返回。
private static Object getUsingURLs(String[] var0, Hashtable<?, ?> var1) throws NamingException {
if (var0.length == 0) {
throw new ConfigurationException("rmiURLContextFactory: empty URL array");
} else {
rmiURLContext var2 = new rmiURLContext(var1);
try {
NamingException var3 = null;
int var4 = 0;
while(var4 < var0.length) {