【缺陷周话】第28期:被污染的内存分配
字数 1525 2025-08-18 11:38:22

被污染的内存分配 - 代码安全审计详解

1. 概念与背景

被污染的内存分配(Uncontrolled Memory Allocation)是指当内存分配函数的长度参数来自于不可信源且未经验证时,可能导致系统分配过大内存的安全缺陷。

CWE编号: CWE-789
常见内存分配函数:

  • malloc()
  • kmalloc()
  • smalloc()
  • xmalloc()
  • realloc()
  • calloc()
  • GlobalAlloc()
  • HeapAlloc()

2. 漏洞原理

2.1 基本机制

当以下条件同时满足时,会产生被污染的内存分配漏洞:

  1. 内存分配函数的长度参数来自外部不可信源
  2. 未对输入的长度参数进行有效验证和限制
  3. 直接使用该参数进行内存分配

2.2 污染源类型

  • 命令行参数
  • 配置文件
  • 网络通信数据
  • 数据库记录
  • 环境变量
  • 注册表值
  • 其他外部输入数据

3. 危害与影响

3.1 直接危害

  • 分配超大内存块导致系统内存耗尽
  • 引发拒绝服务(DoS)攻击
  • 程序崩溃或异常终止

3.2 实际漏洞案例

CVE编号 受影响组件 漏洞位置 攻击方式
CVE-2018-6869 ZZIPlib 0.13.68 zzip/zip.c中的__zzip_parse_root_directory函数 特制zip文件
CVE-2018-5783 PoDoFo 0.9.5 base/PdfVecObjects.h中的PoDoFo::PdfVecObjects::Reserve函数 特制pdf文件
CVE-2018-5296 PoDoFo 0.9.5 base/PdfParser.cpp中的PdfParser::ReadXRefSubsection函数 特制pdf文件

4. 代码示例分析

4.1 缺陷代码示例

#include <stdio.h>
#include <stdlib.h>

int GetUntrustedInt() {
    int size;
    scanf("%d", &size); // 从用户输入获取size值
    return size;
}

int main() {
    int size = GetUntrustedInt();
    size_t totBytes = size * sizeof(char);
    char* buf = (char*)malloc(totBytes); // 危险:未验证size大小
    if(buf != NULL) {
        free(buf);
    }
    return 0;
}

问题分析:

  1. size来自用户输入(不可信源)
  2. 直接使用size计算totBytes
  3. 未对totBytes进行任何限制检查
  4. 可能导致分配超大内存块

4.2 修复代码示例

#include <stdio.h>
#include <stdlib.h>

#define MAX_ALLOWED_BYTES 1024

int GetUntrustedInt() {
    int size;
    scanf("%d", &size);
    return size;
}

int main() {
    int size = GetUntrustedInt();
    size_t totBytes = size * sizeof(char);
    
    // 添加长度验证
    if(totBytes > MAX_ALLOWED_BYTES) {
        fprintf(stderr, "Error: Memory allocation size too large\n");
        return -1;
    }
    
    char* buf = (char*)malloc(totBytes);
    if(buf != NULL) {
        free(buf);
    }
    return 0;
}

修复要点:

  1. 定义最大允许分配大小MAX_ALLOWED_BYTES
  2. 在分配前验证totBytes是否超过限制
  3. 超过限制时返回错误而非继续分配

5. 检测与防御

5.1 检测方法

  1. 代码审计:

    • 跟踪所有内存分配函数的参数来源
    • 检查是否来自不可信源
    • 检查是否有适当的验证逻辑
  2. 静态分析工具:

    • 使用360代码卫士等专业工具
    • 可自动识别未经验证的内存分配

5.2 防御措施

  1. 输入验证:

    • 对所有来自外部的长度参数进行验证
    • 设置合理的上下限
  2. 安全编码实践:

    • 避免直接使用外部输入作为分配大小
    • 使用安全的包装函数
  3. 资源限制:

    • 设置进程内存限制
    • 使用内存池技术

6. 最佳实践建议

  1. 对于所有内存分配函数,都应验证其大小参数
  2. 建立组织内部的安全编码规范,明确内存分配的安全要求
  3. 在代码审查中重点关注内存分配相关的代码
  4. 对开发人员进行安全编码培训,提高安全意识
  5. 在持续集成流程中加入静态分析工具检查

7. 扩展思考

  1. 相关漏洞类型:

    • 整数溢出导致的内存分配问题
    • 内存泄漏问题
    • 双重释放问题
  2. 防御深度:

    • 除了代码层面的防御,还应考虑系统层面的防护
    • 如使用地址空间布局随机化(ASLR)
    • 内存保护机制等
  3. 语言选择考量:

    • 考虑使用更安全的语言(如Rust)替代C/C++
    • 或使用智能指针等现代C++特性

通过全面理解被污染的内存分配问题,开发者可以编写出更安全的代码,有效预防此类安全漏洞的发生。

被污染的内存分配 - 代码安全审计详解 1. 概念与背景 被污染的内存分配(Uncontrolled Memory Allocation)是指当内存分配函数的长度参数来自于不可信源且未经验证时,可能导致系统分配过大内存的安全缺陷。 CWE编号 : CWE-789 常见内存分配函数 : malloc() kmalloc() smalloc() xmalloc() realloc() calloc() GlobalAlloc() HeapAlloc() 2. 漏洞原理 2.1 基本机制 当以下条件同时满足时,会产生被污染的内存分配漏洞: 内存分配函数的长度参数来自外部不可信源 未对输入的长度参数进行有效验证和限制 直接使用该参数进行内存分配 2.2 污染源类型 命令行参数 配置文件 网络通信数据 数据库记录 环境变量 注册表值 其他外部输入数据 3. 危害与影响 3.1 直接危害 分配超大内存块导致系统内存耗尽 引发拒绝服务(DoS)攻击 程序崩溃或异常终止 3.2 实际漏洞案例 | CVE编号 | 受影响组件 | 漏洞位置 | 攻击方式 | |---------|------------|----------|----------| | CVE-2018-6869 | ZZIPlib 0.13.68 | zzip/zip.c中的 __zzip_parse_root_directory 函数 | 特制zip文件 | | CVE-2018-5783 | PoDoFo 0.9.5 | base/PdfVecObjects.h中的 PoDoFo::PdfVecObjects::Reserve 函数 | 特制pdf文件 | | CVE-2018-5296 | PoDoFo 0.9.5 | base/PdfParser.cpp中的 PdfParser::ReadXRefSubsection 函数 | 特制pdf文件 | 4. 代码示例分析 4.1 缺陷代码示例 问题分析 : size 来自用户输入(不可信源) 直接使用 size 计算 totBytes 未对 totBytes 进行任何限制检查 可能导致分配超大内存块 4.2 修复代码示例 修复要点 : 定义最大允许分配大小 MAX_ALLOWED_BYTES 在分配前验证 totBytes 是否超过限制 超过限制时返回错误而非继续分配 5. 检测与防御 5.1 检测方法 代码审计 : 跟踪所有内存分配函数的参数来源 检查是否来自不可信源 检查是否有适当的验证逻辑 静态分析工具 : 使用360代码卫士等专业工具 可自动识别未经验证的内存分配 5.2 防御措施 输入验证 : 对所有来自外部的长度参数进行验证 设置合理的上下限 安全编码实践 : 避免直接使用外部输入作为分配大小 使用安全的包装函数 资源限制 : 设置进程内存限制 使用内存池技术 6. 最佳实践建议 对于所有内存分配函数,都应验证其大小参数 建立组织内部的安全编码规范,明确内存分配的安全要求 在代码审查中重点关注内存分配相关的代码 对开发人员进行安全编码培训,提高安全意识 在持续集成流程中加入静态分析工具检查 7. 扩展思考 相关漏洞类型 : 整数溢出导致的内存分配问题 内存泄漏问题 双重释放问题 防御深度 : 除了代码层面的防御,还应考虑系统层面的防护 如使用地址空间布局随机化(ASLR) 内存保护机制等 语言选择考量 : 考虑使用更安全的语言(如Rust)替代C/C++ 或使用智能指针等现代C++特性 通过全面理解被污染的内存分配问题,开发者可以编写出更安全的代码,有效预防此类安全漏洞的发生。