CodeQL Java 工作坊:Apache Struts 中的不安全反序列化
内容提要
本教程介绍如何使用CodeQL查找Apache Struts中的不安全XML反序列化漏洞(CVE-2017-9805)。通过编写查询,识别XStream的fromXML调用及ContentTypeHandler接口的toObject方法,并利用数据流分析追踪不受信任数据流向,最终定位漏洞。教程包含详细步骤、代码示例和路径问题查询转换,帮助用户掌握CodeQL在Java安全分析中的应用。
延伸解读
漏洞根源:ContentTypeHandler 接口设计缺陷
Apache Struts 通过 ContentTypeHandler 接口支持多种内容类型,其 toObject 方法接收请求体(Reader)并填充目标对象。然而,该方法的 in 参数通常直接来自请求体,未经过净化或安全检查,被视为不受信任的用户数据。这种设计为反序列化漏洞提供了入口,CVE-2017-9805 正是由于 XStream 的 fromXML 默认不验证输入,导致远程代码执行。
CodeQL 数据流分析的核心思路
本工作坊通过 CodeQL 将漏洞识别转化为数据流问题:先定位接收不受信任数据的源(toObject 的第一个参数),再定位可能执行不安全反序列化的汇(fromXML 的参数),最后用 DataFlow 配置追踪两者之间的路径。这种分析方法不仅适用于此漏洞,也可推广到其他类似的反序列化场景,帮助安全人员系统性地发现潜在风险。
从问题查询到路径查询的实践价值
教程展示了如何将基础查询升级为路径问题查询,通过 @kind path-problem 和 hasFlowPath 输出完整的攻击路径。这不仅能确认漏洞存在,还能直观展示数据如何从源头流向危险操作,便于开发者理解漏洞触发条件并修复。对于复杂的数据流问题,路径可视化是提升分析效率的关键。
Q&A
CVE-2017-9805是什么漏洞?
CVE-2017-9805是Apache Struts中的一个XML反序列化漏洞,允许远程代码执行。该漏洞源于Struts框架通过ContentTypeHandler接口处理多种内容类型时,未对请求体中的数据进行充分验证,导致不安全的XML反序列化。
如何用CodeQL查找Apache Struts中的不安全XML反序列化漏洞?
通过编写CodeQL查询,识别XStream的fromXML调用(作为sink)和ContentTypeHandler接口的toObject方法(作为source),然后使用数据流分析追踪不受信任数据是否流向fromXML调用。具体步骤包括:1) 查找所有fromXML调用及其参数;2) 定义ContentTypeHandler接口及其toObject方法;3) 配置数据流源和汇,运行查询找到漏洞路径。
在CodeQL中如何定义ContentTypeHandler接口?
在CodeQL中,可以创建一个继承自RefType的类,并在其特征谓词中使用hasQualifiedName方法指定包名和类名,例如:this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")。
如何识别ContentTypeHandler的toObject方法?
创建一个继承自Method的类,在其特征谓词中检查方法名是否为"toObject",并且其声明类型的直接超类型包含ContentTypeHandler。具体条件为:this.getDeclaringType().getASupertype() instanceof ContentTypeHandler and this.hasName("toObject")。
CodeQL数据流分析中如何设置source和sink?
在数据流配置类中,重写isSource谓词,将source设置为ContentTypeHandlerToObject方法的第一个参数(即toObject方法的第一个参数);重写isSink谓词,将sink设置为isXMLDeserialized谓词识别的表达式(即fromXML调用的第一个参数)。然后使用hasFlow或hasFlowPath检查数据流是否存在。
如何将CodeQL查询转换为路径问题查询?
将查询的@kind从problem改为path-problem,导入DataFlow::PathGraph,将source和sink变量类型改为DataFlow::PathNode,使用hasFlowPath代替hasFlow,并在select中同时报告source和sink。
CodeQL查询结果中如何验证漏洞?
运行查询后,如果找到数据流路径,则说明不受信任的数据流向了不安全的XML反序列化调用。对于CVE-2017-9805,查询结果应恰好有一个,且源和汇在同一方法中,便于验证。