文章目录
- 前言
- 一、CodeQL 简介
- 二、编写污点跟踪查询
- 扩展传播功能
- 总结
前言
最近,CodeQL使用非常广泛(GitHub 开发的一种强大的静态分析工具)来将代码扫描作为 CI/CD 管道的一部分。其核心是 QL 语言,它用于编写对代码进行推理的查询。与竞争对手相比,CodeQL 擅长污点跟踪和数据流分析,它非常擅长并强调潜在的恶意或不安全数据如何流经您的代码并最终进入引入安全性的危险位置和代码的脆弱性。
本质上,QL 是一种声明性逻辑编程语言,因此它建立在集合论和谓词逻辑的基础上。
提示:以下是本篇文章正文内容,下面案例可供参考
一、CodeQL 简介
QL 是一种声明性语言。这意味着查询是以描述所需结果的方式编写的,而不是实现它的步骤。这与 Python 或 Java 等命令式语言形成鲜明对比,在这些语言中,编写的代码描述了实现所需结果的步骤。
典型的 CodeQL 分析工作流程如下所示:
- 源代码被编译到数据库中。该数据库包含代码库的关系表示,其中包括有关代码结构、控制流和数据流的信息。
- CodeQL 引擎对数据库运行 QL 查询,类似于对关系数据库3、运行 SQL 查询,以查找代码中与查询匹配的模式。
- 结果导出为 SARIF 格式,可供 CI 工具或自定义集成使用。
从这个意义上说,CodeQL 很像 SQL。甚至可能会认识到跟sql语法的相似之处:
import javascript
from Function f
where not exists(CallExpr c | c.getCallee() = f)
select f, "This function is never called."
例如,此查询查找 JavaScript 代码库中从未调用的所有函数。注意存在量词“exists”,它检查是否至少有一个 CallExpr 调用函数 f。
QL 是面向对象的。 CallExpr::getCallee() 返回一个 Expr,它可以是一个 FunctionExpr。但是,传统意义上的类对象(分配内存来保存类实例的状态)在 QL 中不存在,类更像是描述现有值集的抽象数据类型。
二、编写污点跟踪查询
来看一下 CodeQL 查询,该查询在 JavaScript 代码库中查找基于 DOM 的基本 XSS 漏洞。首先,我们需要定义污点跟踪的配置:
class UnsafeDOMManipulationConfiguration extends TaintTracking::Configuration {
UnsafeDOMManipulationConfiguration()
{
this = "UnsafeDOMManipulationConfiguration"
}
}
现在,我们需要定义来源。这些是不受信任的数据首次进入程序的地方。在本例中,我们正在寻找 RemoteFlowSource(例如来自请求参数的数据)、ClientRequest::Range(来自 HTTP 响应的数据)和SomeOtherSource (我们在其他地方定义的自定义来源)。
override predicate isSource(DataFlow::Node source) {
source instanceof RemoteFlowSource or
source instanceof ClientRequest::Range or
source instanceof SomeOtherSource
}
接下来,我们需要定义接收器。这些是受污染数据可能造成危害的地方。最终,我们希望找到从任何源到任何接收器的所有路径,这些将是我们查询的结果。
override predicate isSink(DataFlow::Node sink) {
// Direct assignment to innerHTML or outerHTML
exists(DataFlow::PropWrite pw |
pw.getPropertyName() in ["innerHTML", "outerHTML"] and
sink = pw.getRhs()
)
or
// Element.insertAdjacentHTML()
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "insertAdjacentHTML" and
sink = call.getArgument(1)
)
or
// Direct assignment to iframe.srcdoc
exists(DataFlow::PropWrite pw |
pw.getPropertyName() = "srcdoc" and
sink = pw.getRhs()
)
or
// document.write() and document.writeln()
exists(DataFlow::MethodCallNode call |
call.getMethodName() in ["write", "writeln"] and
call.getReceiver() = DOM::documentRef() and
sink = call.getArgument(0)
)
or
// DOMParser.parseFromString()
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "parseFromString" and
call.getReceiver().getALocalSource() instanceof DataFlow::NewNode and
call.getReceiver().getALocalSource().(DataFlow::NewNode).getCalleeName() = "DOMParser" and
sink = call.getArgument(0)
)
or
sink instanceof DomBasedXss::TooltipSink
}
这定义了数据流图中的哪些节点被视为接收器。在本例中,我们正在识别可用于将不受信任的数据注入 DOM 的方法,例如innerHTML、、、、、、
和。当不受信任的数据到达这些接收器时,outerHTM
L它可用于在页面上下文中执行任意 JavaScript 代码。insertAdjacentHTMLsrcdocdocument.writedocument.writelnDOMParser.parseFromString
计算出最小不动点后(并且我们拥有程序中所有受污染元素的完整集合),我们便可以根据这组受污染元素评估接收器,以确定是否有任何受污染数据可以到达接收器。如果是这样,我们就有一个潜在的漏洞。
from DataFlow::PathNode source, DataFlow::PathNode sink,UnsafeDOMManipulationConfiguration config
where config.hasFlowPath(source, sink)
select sink.getNode(), "Potentially unsafe DOM manipulation with $@.",source.getNode(), "untrusted data"
扩展传播功能
有时,默认的污染步骤不足以捕获所需的传播。可以通过覆盖来扩展它isAdditionalTaintStep:
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::ArrayCreationNode array |
pred = array.getAnElement() and
succ = array
)
or
exists(DataFlow::MethodCallNode find |
find.getMethodName().regexpMatch("find|filter|some|every|map") and
pred = find.getReceiver() and
succ = find.getCallback(0).getParameter(0)
)
}
这会通过数组元素和数组方法传播污点,例如:ind, filter, some, every, 和 map.
还可以通过覆盖来实现isSanitizer:
override predicate isSanitizer(DataFlow::Node node) {
node = DataFlow::moduleImport("dompurify").getAMemberCall("sanitize")
}
总结
它是一款非常有趣且功能强大的工具,虽然缺乏其他一些静态分析工具的用户友好性,但它的灵活性和表现力弥补了这一缺陷。
文章原创,欢迎转载,请注明文章出处: CodeQL和数据流分析的简介.。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。