本来想要写一下jndi注入在jdk高版本下的利用这个专题的,后来发现之前分析过底层jndi注入的原理,但是好像一直没有记述,就先来补一下;趁着现在机会写一写,顺便复习一下;改天再来补jndi的高版本jdk的利用;
低版本的jndi注入可以很方便的进行攻击,利用reference远程绑定class和factory以及factorylocation可以在本地解析的时候使用造成factory去对class进行处理,因为本地无相应的factory最终会去factorylocation上进行远程加载,调用class.forname;但是高版本因为有JEP290的限制原因,导致无法进行相应的攻击,
jdk8u113开始进行相关的安全设置,这里先使用8u112进行测试下;
远程打开恶意server;本地进行相关的lookup操作;可以看到本地在RegistryContext class中拿到了远程返回的wrapper;顾名思义是一个封装;相关的具体请求解析点是在RegistryImpl_Stub中,具体可以去看一看相关的jndi注入的原理就可搞明白了,也会理解为什么会请求1099端口;这里不多解释;
因为这里远程是通过reference进行class和factory的绑定,所以会进入以下处理流程:
ref先进行相关的赋值,然后判断是否有绑定的factory;如果有责去进行加载;看下图:
先从本地进行加载相关的factory;本地自然为null;所以就会在reference中去寻找相关的facotrylocation;这里server端进行了相关的自动化赋值;所以本地自然会拿到,然后进行相关的赋值;
codebase也为null;这一步就是进行codebase的赋值;赋完值之后就会去远程codebase加载;
相关效果如上图所示;通过实例化urlclassloader进行远程加载;如下图:
最后实际触发是调用Class.forName进行远程加载;
有个细节点,forName的第二个参数为true;
这里熟悉class加载到jvm流程的肯定会熟悉这个函数,第二个true意味着在进行class加载到jvm之后会触发其初始化操作,会执行class中的静态代码块;所以这里也就是为什么会触发漏洞点,我们远程下载相关的恶意class看看其内部结构就懂了;看下图:
所以为什么可命令执行就已经很显然了;这里其实还没有到后续,基本的命令执行就在此可以发生,但是继续往后续追一下看看;
这里已经loadclass成功,最后会将其实例化,那么自然会触发其构造器中的代码,所以这里也可将恶意的代码放在构造器中;同样可触发;可以看到这里是强制转换成ObjectFactory对象;当然恶意class并没有继承相关的接口,所以最后会导致抛出 ExecTemplateJDK8 cannot be cast to javax.naming.spi.ObjectFactory 无法转换为ObjectFactory这样的错误导致流程结束,但是已经触发了恶意的命令;
这里简单的来说就是jndi的rmi的利用方式,除了rmi的利用点,还有ldap的利用等等,从危害面来分析,ldap的攻击方式更加的广泛,因为ldap有两处可以触发反序列化的地方;简单的追溯一下源码就可明白;一般情况下使用的还是开启恶意的server的攻击方式,但是ldap可以在不开启恶意的class-server的时候就对其进行攻击;
来追溯一下源码:
入口点调用了lookup方法;调用其父类的方法;追过去看一下,拿到ldap的上下文环境,然后调用lookup继续加载;具体的可以看下图所示:
继续调用c_lookup
在c_lookup中触发相关的远程加载;
调用doSearchOnce方法去远程获得远程的Result;
细致的跟进去看一下,最终在
地方发起了远程的请求;拿到返回的对象,以及其相关的属性;这里有个细节,需要相应的classname不为空才会进行下一步的流程;
追到decodeObject函数下,分别对返回的属性进行相关的处理,可以看到拿到了codebases,这个点我们在恶意的server上已经为相关的Entry属性赋过值了;所以这里就发生了注入;看一下下面的图,最主要的有一个点,当javaSerializedData不为空的时候,会采用相应的classloader去对相关的数据进行deserialize;这里就很显然了,也是一个注入点;
所以ldap的攻击使用范围挺广泛,除了可以返回相应的对象导致解析属性去加载原程的恶意factory class之外还可以直接利用javaSerializedData返回一段序列化数据在不去加载外部的class的时候就可以实现反序列化命令执行;
我们来看一下相关的恶意工具中的class的设计;
可以看到相关的工具中也是对一些敏感的属性进行属性值的注入,从而导致本地解析流程中的一些逻辑处理;从而可以对远程的恶意factoryclass进行加载;有点类似tomcat的AJP任意文件读取;都是对属性进行注入,从而影响相关的解析流程,从而达到文件包含或者文件读取的效果;不过文件读取因为normail函数的限制只可以读取特定目录下的文件,因为相关函数会对目录穿越进行检测;这里不过多赘述另外的漏洞了;
接着刚才的流程:
看到相关的程序进入到return的阶段;这里的一些判断句我们都可控制;首先来看var1;var1这里可以eval一下
很清晰的可以看到确实是有返回值;所以为null的条件并不满足,后面的几个条件我们都可通过相关的属性值来进行控制;目的是为了让其进入decodeReference函数而不是返回null;
来看一下相关的敏感函数:
可以清晰的看到这里对javaCLassName进行相关的判断和处理,显而易见这个方法就是处理class的地方;跟着程序看,var2拿到的是javaClassName,然后后续利用get方法赋值给了var3;然后下面又进行了Reference的封装;来细致的看一下;
可以看到的是利用get方法最后赋值的是相关的factoryclass;然后进入var5的时候进行Reference的封装;
最后return;
然后在LdapCtx类下进行继续解析;具体看下图;
到这里就和rmi后面的相关流程一样了;贴一下图吧;
就会造成远程加载factoryclass进行恶意命令执行;至于ldap的其他的几个用法,因为在之前追溯的时候也有过一些其他的if;比如上面的一张图:
可以看到回去进行decodeRmiObject,这也是一种利用点,可以考虑深度挖掘一下,这里不讲那么多hhh;可以自行深度挖掘,因为用的也不是很多,但是并不意味着不可以进行相关注入;
相信看到这里应该对jndi注入有了更清晰的认知,知道为什么叫做jndi“注入”;对其相关的原理进行理解,就会问题的产生还是因为信任用户的输入,引导程序去加载恶意的server从而导致属性可控,返回对象类型可控。从而导致本地的很多流程都可控,最终导致远程加载恶意class或者其他的一些命令执行等等;