前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JNDI与RMI、LDAP

JNDI与RMI、LDAP

作者头像
HhhM
发布2022-08-10 16:23:23
1.2K0
发布2022-08-10 16:23:23
举报
文章被收录于专栏:H&M的专栏H&M的专栏

JNDI与RMI、LDAP

2022-02-18 04:02:00
java - jndi

Concepts of JNDI

JNDI 全名 Java Naming and Directory Interface,实际上简单来说就是一个接口,应用通过该接口来访问对应的目录服务。好吧,先了解一下目录服务是啥。

JNDI分为了Naming和Directory,对应的命名服务和目录服务。

所谓Naming-命名服务,常见的有如DNS,一句话概括来说其实就是我们可以根据某一个具体的名称来获取到其对应的对象。

所谓Directory-目录服务,如反序列化中常常见到的ldap就是目录服务中的一种,实际上目录服务可以理解为名称服务的一个扩展。

回顾我写过的RMI攻击方式[1]

在编写一个Server和Registry时我选择将他们放置在一起,而实际上在代码中起到server作用的是:

毫无疑问,作为一个命名服务首先需要将对象和某个名称绑定在一起,也就是所谓的Bindings,而之所以说目录服务是命名服务的扩展是因为目录服务还可以通过属性来搜索对象。

JNDI到底是什么,实际上是java的一个api,通过JNDI可以对不同的目录系统做操作,将不同的目录系统(如RMI和LDAP)放入统一的一个接口中方便使用,其整体架构可看oracle官方文档[2]中给的图:

JNDI Architecture
JNDI Architecture

在目录系统之上还有一层SPI是什么?与API有和关系?

SPI(Service Provider Interface),即服务供应接口 API是你可以调用或者使用类/接口/方法等去完成某个目标的意思。 SPI是你需要继承或实现某些类/接口/方法等去完成某个目标的意思。 换句话说,API制定的类/方法可以做什么,而SPI告诉你你必须符合什么规范。 有时候SPI和API互相重叠。例如JDBC驱动类是SPI的一部分:如果仅仅是想使用JDBC驱动,你不需要实现这个类,但如果你想要实现JdBC驱动那就必须实现这个类。

其中JDK默认内置了如下SPI:

  • Lightweight Directory Access Protocol (LDAP)
  • Common Object Request Broker Architecture (CORBA) Common Object Services (COS) name service
  • Java Remote Method Invocation (RMI) Registry
  • Domain Name Service (DNS)

同时JNDI分为了5个包:

See RMI and know others

SPI层下可供我们利用的有LDAP,RMI,CORBA,相对来说我对于RMI相关的知识了解偏多,既然同属于SPI下的东西,那么大体上应该大同小异,因此我从RMI切入,窥RMI而知其他。

RMI的介绍看[1],本文建立在对RMI有一定了解的前提。

直接看代码胜过一大堆解释,首先是Server和Register:

Register没有变化,server用Reference来替代我们继承自UnicastRemoteObject的实现类,同时不需要进行实例化。

Client:

客户端其实基本上一致,都是通过几种方法去操作对象。

在此种情况下我们需要关注的就是我们的可控点,大部分情况下需要可控到PROVIDER_URL,或者说是lookup内可控,在服务器上放置EvilObj.class后将所谓的PROVIDER_URL指向服务器即可达成利用。

例如fastjson中在1.2.22-1.2.24版本中的JdbcRowSetImpl链就是通过控制lookup的内容来达成利用,如下图:

其payload为:

具体链路就不作分析了,主要是fastjson的autotype的缘故,且设置autoCommit为true时会走到connect调用到lookup。

fastjson还有另一个payload是利用ldap:

会发现其实单论利用而言,区别只在于协议以及指定开启的恶意端,而我们的payload需要修改的地方仅仅只有协议头,同理对于CORBA有如:

不过CORBA的利用需要SecurityManager启用,且需要配置规则,因此后面主要谈谈rmi和ldap。

与常规的rmi实现不同的是此处我们操作的是Reference对象而非直接对远程类对象做操作,这样就是JNDI对于RMI或者说是SPI层下的实现,通过返回Reference的方式,由JNDI统一去加载指定的地址上的obj,而加载时会去先从本地CLASSPATH查找EvilObj,找不到时会到指定的地址也就是http://localhost:18888/EvilObj.class中去加载,有前提条件:com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase为true。

回到漏洞层面,在client端发起lookup后,通过层层调用,在RMI Registry获取到绑定的obj,然后在javax.naming.spi.NamingManager#getObjectFactoryFromReference函数最后return了如下:

此处的cls也就是我们的factory—EvilObj,此时会调用到EvilObj的构造函数达成一整个的利用,

LDAP

在JDK8u113以及JDK6u132, JDK7u122中对于两个trustURLCodebase的值都默认设置为false,对此有两种不同思路的绕过方式:

  • 利用本地Factory类绕过,有Tomcat和SpringBoot以及其他链,参考[6]。
  • 利用ldap协议绕过。

关于第一点具体可参考[4],我主要谈谈ldap。

Ldap是一种目录服务,轻量级目录访问协议(The Lightweight Directory Access Protocol),不仅仅在java中,在其他地方也有其存在,ldap有着一些独特的机制,例如索引,属性等,同时java对象在ldap中也有多种存储形式,其中比较值得关注的是SerializedData以及JNDI Reference,而存储的java对象可以放置的属性有:

  • ObjectClass
  • javaCodebase
  • JavaFactory
  • javaClassName

为方便,直接利用https://github.com/welk1n/JNDI-Injection-Exploit/做调试,在客户端下断点,跟入到:com.sun.jndi.ldap.Obj#decodeObject时能够发现上面的四个属性:

这四个属性就是从服务器获取的Entry中得到的,后续将提取javaClassNamejavaFactory这两个属性并生成一个Reference,最后交由javax.naming.spi.NamingManager#getObjectFactoryFromReference来处理,并且将classFactoryLocation赋值给了codebase,最后从codebase中加载factory类并执行初始化造成漏洞的利用:

在高版本 JDK,如 11.0.18u1917u2016u211 版本时加入了对于ldap的codebase的限制,因此在除了使用ref的利用方式之外,还可以利用SerializedData,同样位于com.sun.jndi.ldap.Obj#decodeObject,还有另一个分支:

因此只需要在获取到的Entry中添加javaSerializedData字段即可进入该分支,那么具体该怎么实现?

[7]中提到一个朴实无华的技巧:

用LDAP Server做周知端口时,rebind()的内部实现就是将Object序列化后置于”javaSerializedData”属性中,lookup()则对”javaSerializedData”属性的值进行反序列化,就这么设计的。

因此在实现ldap恶意端时只需要先启动一个正常的ldap服务:

执行rebind服务:

执行client达成利用:

这一利用过程相对于前面的codebase来说缺点是需要本地有gadget,但在JDK 11.0.1、8u191、7u201、6u211之后由于com.sun.jndi.ldap.object.trustURLCodebase也默认置为false,此时只能选择使用SerializedData的方式。

总结

在JNDI注入中

就RMI而言:

  • 在JDK8u113以及JDK6u132, JDK7u122版本以下,可以使用JNDI + RMI lookup Reference的利用方式。
  • 在JDK8u113以及JDK6u132, JDK7u122之后的版本,可以利用存在gadget的本地Factory类,具体可看[6]。

就LDAP而言:

  • 11.0.18u1917u2016u211 版本以下,可以使用JNDI + LDAP lookup Reference的利用方式。
  • 11.0.18u1917u2016u211 之后的版本,可以使用javaSerializedData的利用方式。

Ref

[1]https://www.anquanke.com/post/id/263726

[2]https://docs.oracle.com/javase/tutorial/jndi/overview/

[3]https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE-wp.pdf

[4]https://paper.seebug.org/942/#classreference-factory

[5]https://www.jianshu.com/p/776c56fc3a80

[6]https://tttang.com/archive/1405/

[7]http://blog.nsfocus.net/ldap-0521/

本文原创于HhhM的博客,转载请标明出处。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JNDI与RMI、LDAP
    • Concepts of JNDI
      • See RMI and know others
        • LDAP
          • 总结
            • Ref
            相关产品与服务
            对象存储
            对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档