转载

Exploitng JNDI Injection In Java

https://www.veracode.com/blog/research/exploiting-jndi-injections-java

跟着这文章调了一遍, 之前一度以为在jdk 8u191之后, JNDI注入也就只能打打反序列了,看了这文章后发现了一种新的场景。

之前JNDI注入都是依靠于getObjectFactoryFromReference时,

如果目标classpath里找不到指定的class时,会从远程codebase中下载class字节码, 然后实例化。

在出现了trustCodebaseURL的限制之后 已经不再能够从codebase中下载字节码。 但是可以loadClass目标classpath下存在的类。

Tomcat 8

依赖包pom.xml

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>8.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.el/com.springsource.org.apache.el -->
<dependency>
    <groupId>org.apache.el</groupId>
    <artifactId>com.springsource.org.apache.el</artifactId>
    <version>7.0.26</version>
</dependency>

JNDIClient.java

import javax.naming.Context;
import javax.naming.InitialContext;

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

        String uri = "rmi://localhost:1097/Object";
        Context ctx = new InitialContext();
        ctx.lookup(uri);
    }
}

调用的方法

public Object lookup(Name var1)throws NamingException {
    if (var1.isEmpty()) {
        return new RegistryContext(this);
    } else {
        Remote var2;
        try {
            var2 = this.registry.lookup(var1.get(0));
        } catch (NotBoundException var4) {
            throw new NameNotFoundException(var1.get(0));
        } catch (RemoteException var5) {
            throw (NamingException)wrapRemoteException(var5).fillInStackTrace();
        }
        return this.decodeObject(var2, var1.getPrefix(1));
    }
}

该方法对RMI registry发请求,反序列获取到ReferenceWrapper_Stub

然后把反序列得到的ReferenceWrapper_Stub传给decodeObject

private Object decodeObject(Remote var1, Name var2)throws NamingException {
    try {
        Object var3 = var1 instanceof RemoteReference ? ((RemoteReference)var1).getReference() : var1;
        Reference var8 = null;
        if (var3 instanceof Reference) {
            var8 = (Reference)var3;
        } else if (var3 instanceof Referenceable) {
            var8 = ((Referenceable)((Referenceable)var3)).getReference();
        }

        if (var8 != null && var8.getFactoryClassLocation() != null && !trustURLCodebase) {
            throw new ConfigurationException("The object factory is untrusted. Set the system property 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
        } else {
            return NamingManager.getObjectInstance(var3, var2, this, this.environment);
        }

在decodeObject中, 给获取到的ReferenceWrapper_Stub调用getReference方法, getReference方法通过获取ReferenceWrapper_Stub的ref属性然后发请求, 反序列请求结果得到真正绑定到RMI Registry上的对象(ResourceRef), 然后传给NamingManager.getObjectInstance方法。

public static Object
    getObjectInstance(Object refInfo, Name name, Context nameCtx,
                      Hashtable<?,?> environment)
    throws Exception
{
.......................
        Reference ref = null;
    if (refInfo instanceof Reference) {
        ref = (Reference) refInfo;
    } else if (refInfo instanceof Referenceable) {
        ref = ((Referenceable)(refInfo)).getReference();
    }
    
        if (ref != null) {
        String f = ref.getFactoryClassName();
        if (f != null) {
            // if reference identifies a factory, use exclusively

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

首先类型转换将object转换为Reference对象, 然后ref.getFactoryClassName() 获取FactoryClassName

public final String getFactoryClassName(){
    String factory = super.getFactoryClassName();
    if (factory != null) {
        return factory;
    } else {
        factory = System.getProperty("java.naming.factory.object");
        return factory != null ? null : this.getDefaultFactoryClassName();
    }
}
public String getFactoryClassName(){
    return classFactory;
}

返回的是Reference对象的classFactory属性。

获取到之后又传递给了getObjectFactoryFromReference方法

static ObjectFactory getObjectFactoryFromReference(
    Reference ref, String factoryName)
    throws IllegalAccessException,
    InstantiationException,
    MalformedURLException {
    Class<?> clas = null;

    // Try to use current class loader
    try {
         clas = helper.loadClass(factoryName);
    } catch (ClassNotFoundException e) {
        // ignore and continue
        // e.printStackTrace();
    }
    // All other exceptions are passed up.

    // Not in class path; try to use codebase
    String codebase;
    if (clas == null &&
            (codebase = ref.getFactoryClassLocation()) != null) {
        try {
            clas = helper.loadClass(factoryName, codebase);
        } catch (ClassNotFoundException e) {
        }
    }

    return (clas != null) ? (ObjectFactory) clas.newInstance() : null;
}

然后loadClass, 再newInstance实例化该类。

因为newInstance必然只会调用无参构造方法,所以该class需要有定义一个无参的构造方法或者是根本无构造方法(在无任何构造方法的情况下会隐式生成一个无参构造方法), 如果没有无参构造方法newInstance就直接出错了。

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

在实例化该类后 还会调用这对象的getObjectInstance方法,

所以如果能在一些常用的库中找到有getObjectInstance方法 并且在该方法中有做一些危险的事情的话, 那么就有用了。

原文大佬找到了org.apache.naming.factory.BeanFactory类,实现了ObjectFactory接口。

那么必然实现了ObjectFactory接口的getObjectInstance方法,

public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)throws NamingException {
        if (obj instanceof ResourceRef) {
            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.getConstructor().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[] var18 = value.split(",");
                        i = var18.length;

                        for(int var20 = 0; var20 < i; ++var20) {
                            String param = var18[var20];
                            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);
                            }
                        }

在该方法中 可以明显的看到反射过程。

并且反射的类等东西都来自Reference对象。

反射的类来自ref.getClassName()

反射调用的方法 来自ref.get(“forceString”),如果forceString属性值中含有=号, 那么=号右边的值就为获取的方法, 左边值为hashmap的key, 如果属性值中没有等号就会获取该属性值的setter方法。

最后获取到一个StringRefAddr对象, 且该对象的addrtype属性值非factory,scope,auth,forceString,singleton时, 获取该对象的addrtype作为hashmap的key 从hashmap中取出之前存入的方法,

并且将该对象的contents属性作为反射调用方法时的值。

Class<?>[] paramTypes = new Class[]{String.class};
beanClass.getMethod(propName, paramTypes)

并且获取方法的时候,指定了该方法只能有一个String参数。

原文大佬在这里反射的是javax.el.ELProcessor类, 调用eval方法进行el注入 实现RCE.

public Object eval(String expression){
    return this.getValue(expression, Object.class);
}

Exploitng JNDI Injection In Java

TOMCAT 7

TOMCAT 7测试。

    <dependencies>
    <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-catalina</artifactId>
        <version>7.0.91</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.el/com.springsource.org.apache.el -->
    <dependency>
        <groupId>org.apache.el</groupId>
        <artifactId>com.springsource.org.apache.el</artifactId>
        <version>7.0.26</version>
    </dependency>
</dependencies>

Exploitng JNDI Injection In Java 出异常, 没有javax.el.ELProcessor这个类。

在TOMCAT>8.5版本中, 存在el包

Exploitng JNDI Injection In Java

在tomcat7中没有这个el包。

Exploitng JNDI Injection In Java

在tomcat8中, 依赖了tomcat-jsp-api包

Exploitng JNDI Injection In Java Exploitng JNDI Injection In Java

jsp-api包又依赖了el包。

在tomcat7中,

Exploitng JNDI Injection In Java

并没有依赖tomcat-jsp-api, 就没有了el包。

所以在tomcat7中 还需要再手动引入这个包。

Exploitng JNDI Injection In Java

tomcat el包和 javax.el包同时存在时

tomcat的el包名和javax.el的包名相同, 都为javax.el

Exploitng JNDI Injection In Java

存在两个javax.el.ELProcessor

在import这个类的时候, 具体引入的哪个类跟编译器先载入哪个jar包有关。

maven中, 哪个dependency在前就会导入哪个类。

pom.xml

<dependencies>
    <!-- https://mvnrepository.com/artifact/javax.el/javax.el-api -->
    <dependency>
        <groupId>javax.el</groupId>
        <artifactId>javax.el-api</artifactId>
        <version>3.0.1-b06</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.sun.el/el-ri -->
    <dependency>
        <groupId>com.sun.el</groupId>
        <artifactId>el-ri</artifactId>
        <version>1.0</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-catalina</artifactId>
        <version>8.5.34</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.el/com.springsource.org.apache.el -->
    <dependency>
        <groupId>org.apache.el</groupId>
        <artifactId>com.springsource.org.apache.el</artifactId>
        <version>7.0.26</version>
    </dependency>
</dependencies>

Exploitng JNDI Injection In Java javax.el包下的ELProcessor没法像tomcat el包下的ELProcessor一样EL注入调用方法, 直接就出错了。

pom.xml

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-catalina</artifactId>
        <version>8.5.34</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.el/com.springsource.org.apache.el -->
    <dependency>
        <groupId>org.apache.el</groupId>
        <artifactId>com.springsource.org.apache.el</artifactId>
        <version>7.0.26</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.el/javax.el-api -->
    <dependency>
        <groupId>javax.el</groupId>
        <artifactId>javax.el-api</artifactId>
        <version>3.0.1-b06</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.sun.el/el-ri -->
    <dependency>
        <groupId>com.sun.el</groupId>
        <artifactId>el-ri</artifactId>
        <version>1.0</version>
    </dependency>
</dependencies>

Exploitng JNDI Injection In Java

原文  http://www.yulegeyu.com/2019/01/11/Exploitng-JNDI-Injection-In-Java/
正文到此结束
Loading...