使用CodeQL高效挖掘未处理错误的技术实践

本文详细介绍了如何利用CodeQL进行变体分析,通过构建自定义查询包和污点跟踪技术,系统性地检测C++代码库中未处理的错误返回值,显著提升漏洞挖掘效率。

使用CodeQL查找未处理错误 - Trail of Bits博客

问题背景

开发人员在代码库中发现了一个未处理的错误代码漏洞,并怀疑存在更多类似问题。手动逐个检查代码效率低下,如同"打地鼠"游戏。未处理的错误可能被利用并导致严重问题,例如Kubernetes的CVE-2018-1002105漏洞就源于错误处理不当。

CodeQL解决方案

我们使用CodeQL进行变体分析,通过现有漏洞模式寻找类似问题。具体步骤包括:

1. 构建CodeQL数据库

1
codeql database create -l cpp -c 'MSBuild.exe <solution>.sln' <solution>.codeql

2. 设置自定义查询包

创建qlpack.yml文件:

1
2
3
name: <some snazzy QL pack name>
version: 0.0.1
libraryPathDependencies: [codeql-cpp]

3. 定义自定义错误类型

1
2
3
4
5
class CustomError extends FunctionCall {
    CustomError() {
        this.getUnderlyingType().getName() = "CustomErrorType"
    }
}

4. 检测未处理错误

通过污点跟踪技术检查返回值是否影响控制流:

1
2
3
4
5
6
7
8
9
predicate isChecked() {
    exists (IfStmt is |
        TaintTracking::localTaint(
            DataFlow::exprNode(this),
            DataFlow::exprNode(is.getCondition().getAChild*())
        )
    ) or
    // 类似处理while和switch语句
}

5. 优化查询精度

排除以下假阳性情况:

  • 返回值被直接返回
  • 返回值作为函数参数传递
  • 返回值赋值给类成员变量

6. 全局污点跟踪

使用全局分析获得更准确结果:

1
2
3
4
5
6
7
8
class GuardConfiguration extends TaintTracking::Configuration {
    GuardConfiguration() { this = "GuardConfiguration" }
    
    override predicate isSource(DataFlow::Node source) {
        source.asExpr().(FunctionCall).getUnderlyingType().getName() = 
            "CustomErrorType"
    }
}

实践效果

最终查询识别出100多个需要手动审查的代码位置,其中大部分确实是需要修复的真实问题。全局分析运行时间与本地分析相当(约20秒编译,10秒运行)。

技术价值

CodeQL允许我们将漏洞知识编码为可重复使用的查询,确保相同的错误不会再次出现。这种静态分析方法显著提高了代码审计的效率和准确性。

本文展示了如何系统化地使用CodeQL进行代码安全审计,为开发团队提供了实用的技术方案。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计