codeql分析grafana任意文件读取
字数 759 2025-08-24 23:51:21

CodeQL分析Grafana任意文件读取漏洞

前言

本文记录使用CodeQL分析Grafana任意文件读取漏洞的过程,适合有一定基础的读者。通过这个案例,我们将学习如何构建CodeQL数据库、定义数据流、设置污点跟踪以及解决分析过程中遇到的问题。

环境准备

  1. 获取Grafana源代码

    • 使用漏洞修复前的版本进行分析(示例中使用的是grafana-8.2.6)
  2. 创建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

关键点总结

  1. 数据库构建:必须使用漏洞修复前的版本构建CodeQL数据库才能发现漏洞。

  2. 路由处理:Grafana使用多种HTTP方法注册路由,需要全部考虑作为Source点。

  3. 参数获取:特别注意使用*通配符的路由和通过Params获取参数的情况。

  4. 污点传播:需要明确定义赋值语句和参数获取作为额外的污点传播步骤。

  5. 调试技巧:当查询没有结果时,可以逐步简化查询,先确保Source和Sink定义正确,再添加污点传播规则。

参考资料

  1. CodeQL官方文档
  2. 示例代码仓库
  3. CodeQL学习笔记

通过这个案例,我们学习了如何使用CodeQL分析Go语言项目中的安全漏洞,特别是如何处理Web框架中的路由和参数传递问题。这些技术可以应用于其他类似项目的安全分析中。

CodeQL分析Grafana任意文件读取漏洞 前言 本文记录使用CodeQL分析Grafana任意文件读取漏洞的过程,适合有一定基础的读者。通过这个案例,我们将学习如何构建CodeQL数据库、定义数据流、设置污点跟踪以及解决分析过程中遇到的问题。 环境准备 获取Grafana源代码 : 使用漏洞修复前的版本进行分析(示例中使用的是grafana-8.2.6) 创建CodeQL数据库 : 漏洞分析流程 1. 确定Sink点 首先分析漏洞的Sink点(危险操作点),在本例中是文件操作函数: 2. 确定Source点 Grafana使用多种方法注册路由,这些路由处理函数可以作为Source点: 3. 污点跟踪配置 初始尝试直接连接Source和Sink可能不会得到结果,需要定义额外的污点传播步骤: 4. 特殊路由处理 Grafana中有些路由使用 * 通配符,然后在方法内部通过 Params 获取参数,需要特别处理: 5. 完整查询示例 关键点总结 数据库构建 :必须使用漏洞修复前的版本构建CodeQL数据库才能发现漏洞。 路由处理 :Grafana使用多种HTTP方法注册路由,需要全部考虑作为Source点。 参数获取 :特别注意使用 * 通配符的路由和通过 Params 获取参数的情况。 污点传播 :需要明确定义赋值语句和参数获取作为额外的污点传播步骤。 调试技巧 :当查询没有结果时,可以逐步简化查询,先确保Source和Sink定义正确,再添加污点传播规则。 参考资料 CodeQL官方文档 示例代码仓库 CodeQL学习笔记 通过这个案例,我们学习了如何使用CodeQL分析Go语言项目中的安全漏洞,特别是如何处理Web框架中的路由和参数传递问题。这些技术可以应用于其他类似项目的安全分析中。