首页 » 漏洞 » Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析

Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析

 

Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析,这个漏洞在去年11月份官方发布通告的时候我当时关注过,当时自己一直在找 com.sun.jndi.ldap.LdapAttribute 这个类相关的反序列化,当时意识到这个类里面的 _getAttributeSyntaxDefinition()_ 方法和 _getAttributeDefinition()_ 可能会存在反序列化的问题,但是当时找了好多类,发现在发序列化的时候都无法触发这两个方法,原本以为是jdk里面自己的问题,最后就没继续跟下去了,中途有老外放出了一个ppt里面演示了这个漏洞,大概看了下发现是利用json来bypass Jenkins的白名单,当时一直在忙数据分析的事情,事情就搁浅了,前不久刚好MSF上有Payload了,再加上年底了没那么多事了,所以就研究了下,这个漏洞还是挺有意思的,涉及的知识面还是稍微广了一点,这里不得不佩服那些漏洞发现者。

每当一个漏洞漏洞出现的时候,我就在想为什么自己不能发现,当每次漏洞分析完的时候才发现各方面的差距真的是不小。

技术在于分享,这样才能进步。漏洞简介

2016年11月16号Jenkins官方发布了一个安全通告,命名为 CVE-2016-9299 ,从通告上来看,该漏洞依然是个反序列的漏洞,不过这个漏洞的反序列化和LDAP有关,而且在反序列化后需要连接到一个恶意的LDAP服务器,Jenkins对于之前反序列化的修复方法就是对一些恶意的类加上黑名单,所以这里首先得Bypass官方的黑名单,对于该漏洞只有这么多信息,而且在官方给的POC里面也仅仅是提到了 com.sun.jndi.ldap.LdapAttribute 这个类,这个漏洞的利用首先是不需要认证的,而且能任意代码执行,危害可见一斑。

漏洞分析

从官方的描述以及后面的Payload来看,问题和net.sf.json以及com.sun.jndi.ldap.LdapAttribute有关,通过分析对LdapAttribute这个类的分析,我们可以确定以下两个方法是触发反序列化漏洞的根本(关于下文中LDAP的反序列相关的知识请移步16年blackhat老外的Paper “us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE”)

getAttributeSyntaxDefinition getAttributeDefinition

这两个方法中都调用了该 _DirContext schema = getBaseCtx().getSchema(rdn);_ 代码片段其中getBaseCtx()方法定义如下:

Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析

该段代码使用jndi的方式去访问LDAP服务,这里我们可以控制Context.PROVIDER_URL的参数,从而控制jndi访问的LDAP服务器的地址。

getSchema(rdn)方法最终会调用 com.sun.jndi.ldap.LdapBindingEnumeration.createItem(String, Attributes, Vector) 方法(调用关系太多,自己去调试),该方法的定义如下图

Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析

在该方法中最终会调用 Obj.decodeObject(attrs) 方法,从而实现对象的反序列化。这里稍微提下,com.sun.jndi.ldap.Obj对象中定义了几种对象序列化与反序列化的方法,有直接反序列化的,也有直接通过远程加载的,这里的的反序列化稍微与其它地方的反序列化不同的点在于我们不能远程加载对象,因为com.sun.jndi.ldap.VersionHelper12.trustURLCodebase的默认值为false,所以直接决定了类加载器只能加载当前classpath下面的类,关于如何去构造对象使得LDAP在反序列化能执行任意代码,请看下文。

到这里我们知道了com.sun.jndi.ldap.LdapAttribute中相关的方法能触发反序列化的漏洞,那么现在我们要做的就是去找到一个类在反序列化的时候能调用我们相应触发漏洞的函数,也就是在反序列化时能调用getAttributeSyntaxDefinition方法或者getAttributeDefinition方法的类,通过老外的PPT以及公开的gadgets,我们稍微分析下就会发现在net.sf.json这个类库中存在可以调用类任意getXXX函数的地方,那么com.sun.jndi.ldap.LdapAttribute这个类中的getXXX方法是不是也可以通过这种方式来调用,首先我们先确定究竟是那个类中的那个方法能调用getXXX函数,通过gadgets中的json Payload我们发现最终能调用对象的getXXX函数如下图(net.sf.json.JSONObject.defaultBeanProcessing(Object, JsonConfig))所示

上图中圈起来的两个地方就是能调用getXXX函数的地方,这里会先遍历javabean的所有属性,最后在挨个的调用。

弄明白了能函数调用的根源,下一步就是去找这个函数究竟会怎样触发。通过eclipse我们可以很容易发现如下调用方式。

Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析

如上图所示,我们可以看见defaultBeanProcessing方法最终会被ConcurrentSkipListSet类中的equals方法调用,到这里很多人可能会问了,那么多调用关系,你为什么就找这个类的equals方法,这里可能会有一些经验在里面,因为对于和equals方法相关的东西太多了,对于java中的某些数据结构,例如Set,每次添加元素的时候都会判断当前key是否存在,还有就是比较两个对象是否相等的时候会去调用hashcode和equals方法,这里如果了解过其它反序列化的同学对此可能会稍有感触,例如jdk的那个反序列化的触发过程。如果这种经验没有的话,那么你只能一个一个的去找了。

{C}

最终我们找到了一个类可以的某个方法可以调用我们的函数了,但是你可能会发现在eclipse中这样的函数调用关系大多是多态情况下的方法调用,所以我们还需要对equals方法中的方法调用进行分析,这里我们需要注意的是defaultBeanProcessing这个函数的直接调用对象是net.sf.json.JSONArray.fromObject(Object, JsonConfig)方法,我们来看下equals方法

Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析

在这个方法里面有两处调用了containsAll方法,我们要看看究竟是那个可能会调用fromObject,我们再来看下fromObject的调用关系,如下图

Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析

你会发现JSONArray调用了containsAll方法,

containsAll(c) && c.containsAll(this);

这里的第一个containsAll方法是触发不了的那个函数的,所以我们只要满足对象o是JSONArray就行了,但是事实上是不行了,因为这个对象o不是Set的子类,所以这条路到这基本上就走不通了,所以我们还得继续找。

继续回到c.containsAll这个地方我们再找那些函数最终调用了containsAll,这里我们发现org.apache.commons.collections.collection.AbstractCollectionDecorator.containsAll(Collection)这个抽象类调用了,来看改函数的定义

protected Collection collection; .... public boolean containsAll(Collection coll) { return collection.containsAll(coll); }

这里最终会调用collection.containsAll方法,如果这里我们将collection赋值为JSONArray对象的话不照样触发漏洞么,由于AbstractCollectionDecorator这个类是抽象的,无法实例化,所以我们得找一个它的子类,注意这里我们必须得满足子类是实现了Set接口并且是可以序列化的,所以找到最后我们找到了org.apache.commons.collections.set.ListOrderedSet这个类。这里只需要满足父类的collection是JSONArray就行了。

原文链接:Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析,转载请注明来源!

0