初探CodeQL之Python篇-使用AST(一)
字数 2114 2025-08-22 12:22:24

CodeQL Python 静态分析基础教程:AST 应用与实践

1. 静态分析基础概念

1.1 AST(抽象语法树)

AST 是将源代码以树形式进行表示的数据结构,在 CodeQL 中可以将其视为比正则表达式更精确的代码匹配工具。

特点:

  • 能够精确匹配特定代码结构(表达式、语句块、函数定义等)
  • 比正则表达式定位更准确和细腻
  • 主要用于关键代码、函数或表达式的查找和定位
  • 不具备上下文分析能力(如参数来源追踪)

应用场景:

  • 查找特定函数(如 eval()
  • 匹配特定代码模式
  • 替代简单的 grep 搜索

1.2 控制流(Control Flow)

控制流表示程序的执行逻辑,用于分析代码的执行路径和分支。

特点:

  • 表示程序的执行顺序和执行逻辑
  • 用于分析 ifwhiletry...catch 等控制结构
  • 帮助理解敏感函数的调用过程

应用场景:

  • 函数回溯分析
  • 执行路径分析
  • 条件分支分析

1.3 数据流(Data Flow)

数据流分析用于追踪数据在程序中的流动路径。

关键概念:

  • Source(源点):程序的可控输入点(如 $_GET$_POSTrequest.GET
  • Sink(汇点):漏洞触发点(如 system()eval()os.system()

应用场景:

  • 判断数据是否能从 source 成功到达 sink
  • 分析参数传递路径

1.4 污点分析(Taint Tracking)

污点分析是数据流分析的扩展,用于处理数据在传递过程中的变化。

特点:

  • 标记特定输入为"污点"(不安全、用户可控)
  • 跟踪污点数据的传播过程
  • 解决数据流分析中数据变化导致跟踪中断的问题

示例场景:

username = request.GET.get("username")  # source
sql = f"SELECT * FROM users WHERE username={username}"  # 污点传播
cursor.execute(sql)  # sink

2. CodeQL Python 基础应用

2.1 基本查询结构

import python

from Function f
select f, "Found function:" + f.toString()

2.2 函数装饰器查询

查找所有带有装饰器的函数:

from Function f
where f.getADecorator().toString() != "" and
      f.getLocation().getFile().getRelativePath().regexpMatch(".*")
select f, "Path is:" + f.getLocation().getFile().getRelativePath() + 
          " Decorator string:" + f.getADecorator().toString()

2.3 特定装饰器查询

查找使用 FastAPI APIRouter 装饰的函数:

import python
from Function f, Call c, Name router

where f.getADecorator().toString() != "" and
      f.getADecorator() instanceof Call and
      f.getADecorator().getAChildNode() instanceof Attribute and
      router = f.getADecorator().getAChildNode().getAChildNode() and
      router.getId() = "router" and
      f.getLocation().getFile().getRelativePath().regexpMatch(".*")
select f, "Path is:" + f.getLocation().getFile().getRelativePath() + 
          " Decorator string:" + f.getADecorator().toString()

2.4 参数检查查询

查找不包含 session_user 参数的 FastAPI 路由函数:

import python
from Function f, Name router, Call decorator

where decorator = f.getADecorator() and
      decorator.getAChildNode() instanceof Attribute and
      router = decorator.getAChildNode().getAChildNode() and
      router.getId() = "router" and
      not exists(
          Parameter session_user | 
          session_user = f.getAnArg() and 
          session_user.getName() = "session_user"
      ) and
      f.getLocation().getFile().getRelativePath().regexpMatch(".*")
select f, "Path is:" + f.getLocation().getFile().getRelativePath()

2.5 改进版参数检查

查找不包含 session_useruser 参数的 FastAPI 路由函数:

import python
from Function f, Name router, Call decorator

where decorator = f.getADecorator() and
      decorator.getAChildNode() instanceof Attribute and
      router = decorator.getAChildNode().getAChildNode() and
      router.getId() = "router" and
      not exists(
          Parameter session_user | 
          session_user = f.getAnArg() and 
          (session_user.getName() = "session_user" or 
           session_user.getName() = "user")
      ) and
      f.getLocation().getFile().getRelativePath().regexpMatch(".*")
select f, "Path is:" + f.getLocation().getFile().getRelativePath()

3. 调试技巧与最佳实践

3.1 调试技巧

  1. 使用 toString() 输出:在不确定查询结果时,使用 toString() 输出中间结果
  2. AST Viewer 工具:利用 VSCode CodeQL 扩展的 AST Viewer 可视化代码结构
  3. 类型判断:使用 instanceof 判断节点类型
  4. 类信息查询:使用 getAQlClass() 获取节点的 CodeQL 类型

3.2 查询优化技巧

  1. 路径限制:使用 getLocation().getFile().getRelativePath().regexpMatch(".*") 限制查询范围
  2. 子节点遍历:使用 getAChildNode() 遍历 AST 节点
  3. 存在性判断:使用 not exists() 进行否定条件判断
  4. 参数获取:使用 getAnArg() 获取函数参数

3.3 实用谓词

  • Function.getADecorator():获取函数装饰器
  • Call.getAChildNode():获取调用表达式的子节点
  • Name.getId():获取名称标识符
  • Parameter.getName():获取参数名称
  • Function.getAnArg():获取函数参数

4. 实际案例分析

4.1 案例背景

分析目标:open-webui 项目中可能存在未授权访问漏洞的路由函数

4.2 分析步骤

  1. 识别 FastAPI 路由函数特征

    • 使用 APIRouter 装饰
    • 装饰器形式如 @router.get("/path")
  2. 构建查询

    • 查找所有使用 router 装饰的函数
    • 排除包含 session_useruser 参数的函数
  3. 验证结果

    • 检查查询结果的函数是否确实不需要认证
    • 人工验证潜在的安全问题

4.3 完整查询

import python
from Function f, Name router, Call decorator

where decorator = f.getADecorator() and
      decorator.getAChildNode() instanceof Attribute and
      router = decorator.getAChildNode().getAChildNode() and
      router.getId() = "router" and
      not exists(
          Parameter session_user | 
          session_user = f.getAnArg() and 
          (session_user.getName() = "session_user" or 
           session_user.getName() = "user")
      ) and
      f.getLocation().getFile().getRelativePath().regexpMatch(".*")
select f, "Path is:" + f.getLocation().getFile().getRelativePath()

5. 总结与进阶

5.1 学习要点

  1. AST 是基础:掌握 AST 结构是编写有效查询的前提
  2. 逐步构建查询:从简单查询开始,逐步添加条件
  3. 善用调试工具:AST Viewer 和 toString() 是重要调试手段
  4. 理解静态分析概念:数据流、控制流和污点分析的关系

5.2 进阶方向

  1. 结合数据流分析:从 AST 查询扩展到数据流分析
  2. 自定义污点跟踪:定义特定的 source 和 sink
  3. 复杂模式识别:识别更复杂的漏洞模式
  4. 性能优化:优化大型项目的查询性能

5.3 参考资源

  1. CodeQL zero to hero part 1: The fundamentals of static analysis for vulnerability research
  2. CodeQL library for Python
  3. CodeQL for Python 官方文档
CodeQL Python 静态分析基础教程:AST 应用与实践 1. 静态分析基础概念 1.1 AST(抽象语法树) AST 是将源代码以树形式进行表示的数据结构,在 CodeQL 中可以将其视为比正则表达式更精确的代码匹配工具。 特点: 能够精确匹配特定代码结构(表达式、语句块、函数定义等) 比正则表达式定位更准确和细腻 主要用于关键代码、函数或表达式的查找和定位 不具备上下文分析能力(如参数来源追踪) 应用场景: 查找特定函数(如 eval() ) 匹配特定代码模式 替代简单的 grep 搜索 1.2 控制流(Control Flow) 控制流表示程序的执行逻辑,用于分析代码的执行路径和分支。 特点: 表示程序的执行顺序和执行逻辑 用于分析 if 、 while 、 try...catch 等控制结构 帮助理解敏感函数的调用过程 应用场景: 函数回溯分析 执行路径分析 条件分支分析 1.3 数据流(Data Flow) 数据流分析用于追踪数据在程序中的流动路径。 关键概念: Source(源点) :程序的可控输入点(如 $_GET 、 $_POST 、 request.GET ) Sink(汇点) :漏洞触发点(如 system() 、 eval() 、 os.system() ) 应用场景: 判断数据是否能从 source 成功到达 sink 分析参数传递路径 1.4 污点分析(Taint Tracking) 污点分析是数据流分析的扩展,用于处理数据在传递过程中的变化。 特点: 标记特定输入为"污点"(不安全、用户可控) 跟踪污点数据的传播过程 解决数据流分析中数据变化导致跟踪中断的问题 示例场景: 2. CodeQL Python 基础应用 2.1 基本查询结构 2.2 函数装饰器查询 查找所有带有装饰器的函数: 2.3 特定装饰器查询 查找使用 FastAPI APIRouter 装饰的函数: 2.4 参数检查查询 查找不包含 session_user 参数的 FastAPI 路由函数: 2.5 改进版参数检查 查找不包含 session_user 或 user 参数的 FastAPI 路由函数: 3. 调试技巧与最佳实践 3.1 调试技巧 使用 toString() 输出 :在不确定查询结果时,使用 toString() 输出中间结果 AST Viewer 工具 :利用 VSCode CodeQL 扩展的 AST Viewer 可视化代码结构 类型判断 :使用 instanceof 判断节点类型 类信息查询 :使用 getAQlClass() 获取节点的 CodeQL 类型 3.2 查询优化技巧 路径限制 :使用 getLocation().getFile().getRelativePath().regexpMatch(".*") 限制查询范围 子节点遍历 :使用 getAChildNode() 遍历 AST 节点 存在性判断 :使用 not exists() 进行否定条件判断 参数获取 :使用 getAnArg() 获取函数参数 3.3 实用谓词 Function.getADecorator() :获取函数装饰器 Call.getAChildNode() :获取调用表达式的子节点 Name.getId() :获取名称标识符 Parameter.getName() :获取参数名称 Function.getAnArg() :获取函数参数 4. 实际案例分析 4.1 案例背景 分析目标: open-webui 项目中可能存在未授权访问漏洞的路由函数 4.2 分析步骤 识别 FastAPI 路由函数特征 : 使用 APIRouter 装饰 装饰器形式如 @router.get("/path") 构建查询 : 查找所有使用 router 装饰的函数 排除包含 session_user 或 user 参数的函数 验证结果 : 检查查询结果的函数是否确实不需要认证 人工验证潜在的安全问题 4.3 完整查询 5. 总结与进阶 5.1 学习要点 AST 是基础 :掌握 AST 结构是编写有效查询的前提 逐步构建查询 :从简单查询开始,逐步添加条件 善用调试工具 :AST Viewer 和 toString() 是重要调试手段 理解静态分析概念 :数据流、控制流和污点分析的关系 5.2 进阶方向 结合数据流分析 :从 AST 查询扩展到数据流分析 自定义污点跟踪 :定义特定的 source 和 sink 复杂模式识别 :识别更复杂的漏洞模式 性能优化 :优化大型项目的查询性能 5.3 参考资源 CodeQL zero to hero part 1: The fundamentals of static analysis for vulnerability research CodeQL library for Python CodeQL for Python 官方文档