在本工作坊中,我们将利用语法和语义推理,在 XWiki 平台的评分组件中发现一个 SQL 注入漏洞,该漏洞记录在 CVE-2021-21380 中。
请尽量在工作坊前完成本节内容。
你不需要安装 CodeQL CLI:扩展会自动处理。
克隆此仓库:
git clone https://github.com/advanced-security/codeql-workshop-cve-2021-21380.git
xwiki-platform-CVE-2021-21380.zip 文件,选择 CodeQL: Set Current Database 命令。Cmd/Ctrl+Shift+P)中,搜索并运行 CodeQL: Install Pack Dependencies 命令。github 以过滤列表。cve-2021-21380-exercises、cve-2021-21380-exercises-tests、cve-2021-21380-solutions 和 cve-2021-21380-solutions-tests 旁边的复选框。工作坊分为多个练习,介绍 Java 的 QL 语言支持,最后以一个最终查询来发现已知的 SQL 注入。 在这些练习中,你将学会:
在本工作坊中,我们将寻找 XWiki 平台 评分 API 组件中已知的 SQL 注入漏洞。当外部用户控制的信息进入应用程序代码,这些代码不安全地构造 SQL 查询并执行时,就可能出现此类漏洞。
本工作坊讨论的已知 SQL 注入在 GHSA-79rg-7mv3-jrr5 和 GitHub Advisory Database 中有所记录。为了找到 SQL 注入及其可能的变体,我们将处理以下子问题:
在前几个练习中,我们将利用抽象语法树(AST)对语法信息进行推理,以识别:
通过完成查询 exercise1.ql,找到程序中所有名为 getAverageRating 的方法及其声明类型。
java 模块提供了一个 Method 类,用于推理程序中的方法。Method 类提供了成员谓词 getName 和 hasName,用于推理方法的名称。Method 类提供了成员 getDeclaringType,用于推理声明该方法的类型。解决方案可在查询 exercise1.ql 中找到。
练习 1 的解决方案返回了一个方法列表。其中一些定义在名为 RatingsManager 的接口中,另一些定义在 AbstractRatingsManager 和 RatingsScriptService 类中。
从查询返回的信息以及 XWiki 组件文档 可以推断出:
Role 注解的接口和一个用 Component 注解的实现。ScriptService 的组件可以通过脚本在 Wiki 页面中访问。通过完成查询 exercise2.ql,找到所有用 Component 注解标注的类。
注意,该注解类型的全限定名为 org.xwiki.component.annotation.Component。
this 访问。Class 类提供了一个方法 getAnAnnotation 来获取关联的注解。Annotation 类提供了成员谓词 getType 来推理其类型。Type 类提供了成员谓词 getName 和 hasName 来推理类型的名称。RefType 类(表示类和接口)提供了成员谓词 getQualifiedName 和 hasQualifiedName,用于推理 reftype 的全限定名。解决方案可在查询 exercise2.ql 中找到。
通过完成查询 exercise3.ql,找到所有实现了 ScriptService 接口的组件。
Class 类型提供了成员谓词 getASuperType,用于推理类的超类型,即它 extends 或 implements 的类型。解决方案可在查询 exercise3.ql 中找到。
至此,我们已经从语法上识别出了用户可调用的方法,这些方法的参数将在后续工作坊中被视为不可信数据的来源。
在下个练习中,我们将调查并识别可能的接收点。从练习 1 的结果可以推断出,其中一个实现调用了 getAverageRatingFromQuery 方法。使用类似的查询,我们可以找到 getAverageRatingFromQuery 的声明类型,从而确定 AbstractRatingsManager 类中的实现构造了一个 SQL 语句并将其传递给 search 方法。
search 方法实现在一个依赖中,因此我们无法获得其实现。在下几个练习中,我们将利用可用的类型信息来识别这个 search 方法调用及其声明类型。
通过完成查询 exercise4.ql,找到所有对 search 方法的调用,并识别其声明类型。
MethodCall 类型为我们提供了推理方法调用的手段。MethodAccess 类型提供了成员谓词 getMethod,用于推理方法调用的目标。解决方案可在查询 exercise4.ql 中找到。
通过完成查询 exercise5.ql,找到所有由接口 XWikiStorageInterface(全限定名 com.xpn.xwiki.store.XWikiStoreInterface)声明的方法的调用。
instanceof 关键字可用于声明某个值属于某个类型所表示的值集合。
例如,识别所有对接口方法的调用:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
解决方案可在查询 exercise5.ql 中找到。
至此,我们已经使用 QL 从语法上描述了可能的来源和接收点。为了确定信息是否在程序中的这些点之间流动,我们将使用污点跟踪进行语义分析。 我们支持的语言的标准库提供了两种数据流机制:
DataFlow 模块支持保持值的流。TaintTracking 模块支持即使值被修改也能流动。后者正是我们感兴趣的,因为在 SQL 注入等注入漏洞中,不可信数据通常会变成更大的语句的一部分,然后被执行。
在本工作坊中,我们将复用现有的 SQL 注入污点跟踪配置,并用我们建模的来源和接收点来扩展它,以找到 SQL 注入。为了理解如何扩展配置,我们先看看下面配置的定义。
class QueryInjectionFlowConfig extends TaintTracking::Configuration {
QueryInjectionFlowConfig() { this = "SqlInjectionLib::QueryInjectionFlowConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or
node.getType() instanceof BoxedType or
node.getType() instanceof NumberType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
any(AdditionalQueryInjectionTaintStep s).step(node1, node2)
}
}
配置中使用的 isSource 和 isSink 谓词都使用了我们之前见过的 instanceof 关键字。RemoteFlowSource 和 QueryInjectionSink 类都是 abstract 类。
这是标准库中常见的模式,允许我们扩展 RemoteFlowSource 和 QueryInjectionSink 类所表示的值集合。
一个扩展 abstract class 的 QL class 并不会细化超类所表示的值集合,而是将子类表示的值添加到超类中。
在接下来的练习中,我们将实现这些子类并构建最终的查询。
通过完成查询 exercise6.ql,将练习 3 中识别的组件类的公共方法的参数添加到 RemoteFlowSource 的值集合中。
instanceof 关键字可用于声明某个值属于某个类型所表示的值集合。
例如,识别所有对接口方法的调用:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
解决方案可在查询 exercise6.ql 中找到。
通过完成查询 exercise7.ql,将练习 5 中识别的存储接口方法调用的参数添加到 QueryInjectionSink 的值集合中。
exists 公式允许引入临时变量,并在 exists 作用域内进行推理。
以下示例使用 exists 表达式来将方法集合限定为那些被调用的方法。
from Method m
where m.hasName("foo") and exists(MethodCall ma | ma.getMethod() = m)
select m
MethodCall 类提供了成员谓词 getQualifier,用于推理方法访问的限定符。
解决方案可在查询 exercise7.ql 中找到。
通过完成查询 exercise8.ql,将前面练习的解决方案组合成最终解决方案。
解决方案可在查询 exercise8.ql 中找到。
XWikiStoreInterface。扩展接收点以包括接口实现的直接使用。Class 类提供了成员谓词 extendsOrImplements,Method 类提供了成员谓词 overridesOrInstantiates,可能会有所帮助。