当前位置: 首页 > news >正文

JNDI 注入原理解析

文章目录

        • JNDI基础
          • 概述
          • JNDI SPI
          • 命名服务
          • 目录服务
        • JNDI演示
          • 查询 DNS 服务
          • 查询 LDAP 服务条目
          • 动态协议切换
        • JNDI 注入漏洞

JNDI基础
概述

JNDI(Java Naming and Directory Interface)是Java提供的标准命名和目录接口,通过统一的API使应用程序能够访问各种命名和目录服务。它允许开发人员以统一的方式查找和访问资源,如用户、网络、机器和服务等。另一方面,Java通过JNI(Java Native Interface)实现与C/C++的互操作,弥补了自身在代码安全性和内存操作等方面的不足。尽管JNI功能强大,但自JDK 1.1起便存在,因此在使用时需特别注意可能带来的安全问题。

fb9d120d-81b6-41fe-9b3f-a2ac548a217c

JNDI SPI

SPI(Service Provider Interface,服务供应接口) 是 JNDI 中的一个关键组件,主要作用是为底层的具体目录服务提供统一的接入接口,从而实现目录服务的可插拔式安装。通过 SPI,不同的目录服务实现可以无缝集成到 JNDI 架构中,而无需修改应用层代码。

在 JDK 中,已内置支持多种常见的目录服务,包括:

  • RMI(Java Remote Method Invocation):Java 远程方法调用;
  • LDAP(Lightweight Directory Access Protocol):轻量级目录访问协议;
  • CORBA(Common Object Request Broker Architecture):通用对象请求代理架构,用于 COS 名称服务(Common Object Services);
  • DNS:域名服务。

此外,用户还可以从 Java 官网下载其他目录服务实现。由于 SPI 提供了统一的接口标准,第三方厂商也可以开发自己的私有目录服务实现,并轻松集成进系统中,提升了应用的灵活性和可扩展性。

命名服务

命名服务(Naming Server)是一种通过名称查找实际对象的服务。例如,RMI协议可以通过名称查找并调用远程对象,DNS协议则通过域名查找对应的IP地址。这些服务都属于命名服务。

在命名服务中,有几个关键概念:

Bindings:表示名称与对象之间的绑定关系。例如,在DNS中,域名与IP地址绑定;在RMI中,远程对象与名称绑定;在文件系统中,文件名与文件内容绑定。

Context:上下文表示一组名称到对象的绑定关系。一个上下文可以用于查找对应的对象,例如在文件系统中,一个目录就是上下文,可以在其中查找文件,子目录也可以看作子上下文(SubContext)。

References:对于无法直接存储的对象,它们通常通过引用的形式存储,类似于C/C++中的指针。引用包含获取实际对象所需的信息。例如,在文件系统中,通过文件描述符(fd)来引用文件,内核使用该引用找到磁盘中的文件。

目录服务

目录服务(Directory Service)是命名服务的扩展。除了名称到对象的映射,目录服务还允许对象具有属性(Attributes)。因此,目录服务不仅支持根据名称查找对象并获取其属性,还支持根据属性值进行搜索。

一些常见的目录服务包括:

  • LDAP:轻型目录访问协议
  • Active Directory:为Windows域网络设计,提供多个目录服务,如域名服务和证书服务
  • 其他基于X.500标准实现的目录服务
JNDI演示
查询 DNS 服务

该程序通过 JNDI 查询 DNS 服务,获取 example.com 的 A 记录(IPv4 地址)。它配置了 JNDI 环境,使用 DNS 服务器地址 114.114.114.114,并通过 DirContext 查询指定域名的 A 记录,最后输出查询结果。

// DNSClientSimple.java
import javax.naming.Context;
import javax.naming.directory.*;
import java.util.Hashtable;public class DNSClientSimple {public static void main(String[] args) {// 设置 JNDI 环境配置Hashtable<String, String> env = new Hashtable<>();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");env.put(Context.PROVIDER_URL, "dns://114.114.114.114");  // 使用 DNS 服务器地址try {// 创建 DirContextDirContext ctx = new InitialDirContext(env);// 查询指定域名的 A 记录(IPv4 地址)Attributes res = ctx.getAttributes("example.com", new String[] {"A"});// 输出查询结果System.out.println(res);} catch (Exception e) {e.printStackTrace();}}
}
查询 LDAP 服务条目
// Client.java
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.*;
import java.util.Hashtable;public class Client {public static void main(String[] args) {// 配置 JNDI 环境Hashtable<String, String> env = new Hashtable<>();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");env.put(Context.PROVIDER_URL, "ldap://localhost:8080");  // LDAP 服务器地址try {// 创建 DirContext 实例DirContext ctx = new InitialDirContext(env);// 查找指定的 LDAP 条目DirContext lookCtx = (DirContext) ctx.lookup("cn=john,ou=employees,dc=example,dc=com");// 获取该条目的属性Attributes res = lookCtx.getAttributes("");// 输出查询结果System.out.println(res);} catch (NamingException e) {e.printStackTrace();}}
}

这个程序通过 JNDI 查询 LDAP 服务器,查找特定条目 cn=john,ou=employees,dc=example,dc=com 的属性。程序先配置了 JNDI 环境,指定了 LDAP 服务器的地址(ldap://localhost:8080)。然后创建一个 DirContext 实例,并使用 lookup() 方法查找指定的 LDAP 条目。最后程序获取该条目的所有属性并输出。该程序实现了通过 JNDI 查询 LDAP 服务并检索指定员工条目的相关信息。

动态协议切换
// JNDIDynamicLDAP.java
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;public class JNDIDynamicLDAP {public static void main(String[] args) {if (args.length != 1) {System.out.println("Usage: lookup <username>");return;}Hashtable<String, String> env = new Hashtable<>();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");env.put(Context.PROVIDER_URL, "ldap://localhost:389");try {DirContext ctx = new InitialDirContext(env);DirContext lookCtx = (DirContext) ctx.lookup("cn=" + args[0] + ",ou=employees,dc=example,dc=com");System.out.println(lookCtx.getAttributes(""));} catch (NamingException e) {e.printStackTrace();}}
}

这个程序的目的是通过用户输入的域名,动态查询并获取该域名的相关信息。用户在命令行中提供一个域名(比如 example.com),程序将使用 JNDI 查询该域名的 DNS 记录,并输出其 A 记录(即 IPv4 地址)。这样,用户可以根据输入的不同域名获取相应的 DNS 信息。

我们还可以通过在查找参数中直接指定协议,实现协议的动态切换。

java JNDIDynamic "ldap://localhost:389/cn=alice,ou=users,dc=example,dc=com"

这正是 JNDI 注入漏洞的根本原因所在。攻击者可以构造恶意的服务端响应,引导客户端通过 JNDI 加载并解析远程代码,从而实现远程命令执行(RCE)。JDK 默认支持多种协议的自动识别与切换,并为每种协议提供了对应的工厂类,例如:

  • LDAPcom.sun.jndi.ldap.LdapCtxFactory
  • RMIcom.sun.jndi.rmi.registry.RegistryContextFactory
  • DNScom.sun.jndi.dns.DnsContextFactory
  • CORBAcom.sun.jndi.cosnaming.CNCtxFactory
JNDI 注入漏洞

JNDI 注入(JNDI Injection) 是一种利用 Java Naming and Directory Interface(JNDI)接口的漏洞进行攻击的技术。攻击者通过恶意构造 JNDI 查询,尝试通过 JNDI 访问应用程序的命名服务或目录服务,通常用于获取敏感信息、执行远程代码或进行拒绝服务攻击(DoS)。

http://www.xdnf.cn/news/320383.html

相关文章:

  • 力扣HOT100之链表:146. LRU 缓存
  • 信息论12:从信息增益到信息增益比——决策树中的惩罚机制与应用
  • 三角网格减面算法及其代表的算法库都有哪些?
  • “430”“531”光伏政策变革下,安科瑞如何 “保驾护航”?
  • Oracle OCP认证考试考点详解083系列11
  • windows10系统:如何使用电脑控制手机上多个应用程序(app)?
  • Oracle Goldengate并行复制
  • JS进阶DAY2 构造函数数据常用函数
  • 基于深度学习的交通标志识别系统
  • 如何根据HardFault中断抛出的寄存器值排查数组越界
  • 【EasyPan】loadDataList方法及checkRootFilePid方法解析
  • 阿里云服务器-宝塔面板安装【保姆级教程】
  • 如何将B站(哔哩哔哩)的视频下载到电脑
  • 二叉查找树,平衡二叉树(AVL),b树,b+树,红黑树
  • 实验一:Linux静态路由
  • 如何利用 Elastic Load Balancing 提升应用性能与可用性?
  • VScode一直处于循环“正在重新激活终端“问题的解决方法
  • 软件设计师2025
  • 隐私计算技术及其在数据安全中的应用:守护数据隐私的新范式
  • Word如何制作三线表格
  • ABB机器人基础课件及培训视频教程
  • RabbitMQ中Exchange交换器的类型
  • 国产Word处理控件Spire.Doc教程:在Java中为Word文本和段落设置边框
  • 【Pandas】pandas DataFrame rolling
  • ✨WordToCard使用分享✨
  • 2-C#控件
  • [数据库之九] 数据库索引之顺序索引
  • Cloudera CDP 7.1.3 主机异常关机导致元数据丢失,node不能与CM通信
  • 007 Linux 开发工具(上)—— vim、解放sudo、gc+
  • Java学习手册:ORM 框架性能优化