使用CodeQL捕捉OpenSSL误用漏洞 - 关键加密API安全实践

本文详细介绍了如何利用CodeQL编写5个定制化查询,检测OpenSSL加密库中常见的密钥处理、熵初始化和大数清除等安全隐患,帮助开发者避免内存泄漏、认证绕过等严重漏洞。

使用CodeQL捕捉OpenSSL误用漏洞

作者:Damien Santiago
日期:2023年12月22日

我开发了五个CodeQL查询,用于捕捉OpenSSL libcrypto API中潜在的高危漏洞。这个被广泛采用但容错性低的API,一旦误用可能导致内存泄漏、认证绕过等加密实现问题。这些查询(在导师Fredrik Dahlgren和Filipe Casal指导下完成)通过确保正确的密钥处理、熵初始化和大数清除检查来预防误用。

快速开始

要运行这些查询,首先执行以下命令下载查询包:

1
codeql pack download trailofbits/cpp-queries

对预生成的C/C++数据库进行分析:

1
2
3
codeql database analyze database.db \
    --format=sarif-latest \
    --output=./tob-cpp.sarif -- trailofbits/cpp-queries

密钥长度检测

初始化加密算法时使用过短的密钥会导致严重问题:OpenSSL仍会接受该密钥,但在初始化时会越界读取,可能导致弱密钥问题。我们通过检查密钥尺寸与算法匹配性来实现检测。

CodeQL实现关键点

  1. 定义Key类型:通过数据流分析识别传入EVP_EncryptInit_ex第4参数的变量
1
2
3
4
5
6
class Key extends Variable {
  Key() { exists(FunctionCall init | 
    init.getTarget().hasName("EVP_EncryptInit_ex") and
    this.getANode() = init.getArgument(getKey())
  )
}
  1. 密码识别:通过API函数名模式匹配(如EVP_aes_256_cbc
1
2
3
4
5
6
7
class EVP_CIPHER extends FunctionCall {
  int keySize;
  EVP_CIPHER() { 
    this.getTarget().getName().matches("EVP_aes_%_cbc") and
    keySize = this.getTarget().getName().charAt(8).toInt() / 8
  }
}

引擎初始化验证

OpenSSL 1.1.1支持动态加载加密引擎,但必须按特定顺序初始化:

  1. 通过ENGINE_by_id等函数加载引擎
  2. 调用ENGINE_init进行初始化
  3. 使用ENGINE_set_default设置为默认实现

检测逻辑

1
2
3
4
5
from CreateEngine create, FunctionCall init, FunctionCall set
where 
  not exists(init.getTarget().hasName("ENGINE_init")) or
  not exists(set.getTarget().hasName("ENGINE_set_default"))
select create, "Engine not properly initialized"

扩展应用

OpenSSL libcrypto API充满潜在陷阱,微小的错误可能导致严重漏洞。CodeQL等工具能帮助开发者和代码审查人员构建定制化查询来加固代码。我们已在GitHub仓库中提供了更多针对Go和C++的查询示例。

项目地址:https://github.com/trailofbits/cpp-queries

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