codeql分析grafana任意文件读取
字数 759 2025-08-24 23:51:21
CodeQL分析Grafana任意文件读取漏洞
前言
本文记录使用CodeQL分析Grafana任意文件读取漏洞的过程,适合有一定基础的读者。通过这个案例,我们将学习如何构建CodeQL数据库、定义数据流、设置污点跟踪以及解决分析过程中遇到的问题。
环境准备
-
获取Grafana源代码:
- 使用漏洞修复前的版本进行分析(示例中使用的是grafana-8.2.6)
-
创建CodeQL数据库:
codeql database create /path/to/database/gf --language="go" --source-root=/path/to/grafana-8.2.6 --overwrite
漏洞分析流程
1. 确定Sink点
首先分析漏洞的Sink点(危险操作点),在本例中是文件操作函数:
predicate isSink(DataFlow::Node sink) {
exists(Method m, string name |
m.hasName(name) and
(
name = "Open" or
name = "ReadFile"
) and
sink.asExpr() = m.getACall()
)
}
2. 确定Source点
Grafana使用多种方法注册路由,这些路由处理函数可以作为Source点:
predicate isSource(DataFlow::Node source) {
exists(Method m, string name |
m.hasName(name) and
(
name = "Get" or
name = "Post" or
name = "Delete" or
name = "Put" or
name = "Patch" or
name = "Any"
) and
source.asExpr() = m.getACall()
)
}
3. 污点跟踪配置
初始尝试直接连接Source和Sink可能不会得到结果,需要定义额外的污点传播步骤:
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(AssignStmt assign |
node1.asExpr() = assign.getRhs() and
node2.asExpr() = assign.getLhs()
)
}
4. 特殊路由处理
Grafana中有些路由使用*通配符,然后在方法内部通过Params获取参数,需要特别处理:
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma, Method m |
m.hasName("Param") and
ma.getMethod() = m and
node1.asExpr() = ma.getQualifier() and
node2.asExpr() = ma
)
or
exists(AssignStmt assign |
node1.asExpr() = assign.getRhs() and
node2.asExpr() = assign.getLhs()
)
}
5. 完整查询示例
import go
import semmle.go.dataflow.DataFlow
class GrafanaFileReadConfig extends DataFlow::Configuration {
GrafanaFileReadConfig() { this = "GrafanaFileReadConfig" }
override predicate isSource(DataFlow::Node source) {
exists(Method m, string name |
m.hasName(name) and
(
name = "Get" or
name = "Post" or
name = "Delete" or
name = "Put" or
name = "Patch" or
name = "Any"
) and
source.asExpr() = m.getACall()
)
}
override predicate isSink(DataFlow::Node sink) {
exists(Method m, string name |
m.hasName(name) and
(
name = "Open" or
name = "ReadFile"
) and
sink.asExpr() = m.getACall()
)
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma, Method m |
m.hasName("Param") and
ma.getMethod() = m and
node1.asExpr() = ma.getQualifier() and
node2.asExpr() = ma
)
or
exists(AssignStmt assign |
node1.asExpr() = assign.getRhs() and
node2.asExpr() = assign.getLhs()
)
}
}
from GrafanaFileReadConfig config, DataFlow::Node source, DataFlow::Node sink
where config.hasFlow(source, sink)
select source, sink
关键点总结
-
数据库构建:必须使用漏洞修复前的版本构建CodeQL数据库才能发现漏洞。
-
路由处理:Grafana使用多种HTTP方法注册路由,需要全部考虑作为Source点。
-
参数获取:特别注意使用
*通配符的路由和通过Params获取参数的情况。 -
污点传播:需要明确定义赋值语句和参数获取作为额外的污点传播步骤。
-
调试技巧:当查询没有结果时,可以逐步简化查询,先确保Source和Sink定义正确,再添加污点传播规则。
参考资料
通过这个案例,我们学习了如何使用CodeQL分析Go语言项目中的安全漏洞,特别是如何处理Web框架中的路由和参数传递问题。这些技术可以应用于其他类似项目的安全分析中。