cve-2019-8956 分析
字数 1988 2025-08-26 22:11:45

CVE-2019-8956漏洞分析与教学文档

漏洞概述

CVE-2019-8956是Linux内核中SCTP(流控制传输协议)实现的一个漏洞,存在于sctp_sendmsg()函数中。该漏洞可能导致内核崩溃或潜在的安全问题,属于释放后使用(Use-After-Free)类型漏洞。

漏洞背景

SCTP协议简介

SCTP(Stream Control Transmission Protocol)是一种传输层协议,提供可靠的、面向消息的数据传输服务。其特点包括:

  • 多宿主支持
  • 多流特性
  • 消息边界保持
  • 选择性确认
  • 防止拒绝服务攻击

SCTP包结构

SCTP包由以下部分组成:

  1. 公共头(Common Header):包含源/目的端口、校验和、Verification Tag等
  2. 一个或多个chunk:每个chunk有自己的类型和标志

漏洞分析

漏洞位置

漏洞位于net/sctp/socket.c文件中的sctp_sendmsg()函数,具体是在处理SCTP_SENDALL标志时使用了不安全的链表遍历方式。

补丁分析

补丁链接:https://git.kernel.org/linus/ba59fb0273076637f0add4311faa990a5eec27c0

补丁将:

list_for_each_entry(asoc, &ep->asocs, asocs)

替换为:

list_for_each_entry_safe(asoc, tmp, &ep->asocs, asocs)

关键宏定义区别

  1. list_for_each_entry:
#define list_for_each_entry(pos, head, member) \
    for (pos = list_first_entry(head, typeof(*pos), member); \
         &pos->member != (head); \
         pos = list_next_entry(pos, member))
  1. list_for_each_entry_safe:
#define list_for_each_entry_safe(pos, n, head, member) \
    for (pos = list_first_entry(head, typeof(*pos), member), \
         n = list_next_entry(pos, member); \
         &pos->member != (head); \
         pos = n, n = list_next_entry(n, member))

关键区别:

  • list_for_each_entry_safe在遍历前预先获取下一个元素的指针
  • 当当前元素被删除时,仍能安全地继续遍历

SCTP关联(Association)结构

sctp_association结构体表示SCTP连接的重要信息,包含:

  • assoc_id: 关联ID(唯一标识)
  • c: 与关联状态相关的cookie
  • peer: 表示远程端点的结构体
  • transport_addr_list: 保存建立关联后的一个或多个地址
  • primary_path: 初始连接使用的地址
  • state: 关联状态
  • asocs: 链表节点,用于将关联连接到端点(ep)的关联列表中

漏洞触发机制

触发条件

  1. 发送SCTP消息时设置SCTP_ABORT|SCTP_SENDALL标志
  2. 使用UDP风格的SCTP套接字

漏洞触发流程

  1. SCTP_SENDALL标志设置时,代码会遍历端点(ep)的所有关联(asoc)
  2. 对每个关联调用sctp_sendmsg_check_sflags()进行检查
  3. 如果设置了SCTP_ABORT标志,可能会删除当前关联
  4. 使用list_for_each_entry时,删除当前关联会破坏链表遍历
  5. 下一次遍历时会使用已被释放的内存,导致崩溃

崩溃分析

崩溃时的关键点:

  1. 关联被删除时,其asocs链表节点会被设置为LIST_POISON1LIST_POISON2
    • LIST_POISON1 = 0xdead000000000100
    • LIST_POISON2 = 0xdead000000000200
  2. 下次遍历时,代码尝试访问asoc->asocs.next(已被设置为毒药指针)
  3. 导致非法内存访问,引发内核崩溃

POC构造

基本POC构造思路:

sctp_sendmsg(server_fd, &recvbuf, sizeof(recvbuf),
            (struct sockaddr *)&client_addr, sizeof(client_addr),
            sri.sinfo_ppid, SCTP_ABORT|SCTP_SENDALL,
            sri.sinfo_stream, 0, 0);

关键点:

  • 设置SCTP_ABORT|SCTP_SENDALL标志
  • 使用UDP风格的SCTP套接字

调试分析

调试时观察的关键点:

  1. 关联(asoc)结构的内存状态
  2. asocs链表节点的变化
  3. 内存访问断点在asoc->asocs上的触发

关键调试步骤:

  1. sctp_sendmsg函数设置断点
  2. 观察关联结构的链表操作
  3. 设置内存断点监视asoc->asocs的变化

修复方案

补丁通过使用list_for_each_entry_safe宏修复了此漏洞,该宏:

  1. 在遍历前预先获取下一个元素的指针
  2. 允许在遍历过程中安全地删除当前元素
  3. 避免了释放后使用的问题

教学总结

关键知识点

  1. Linux内核链表操作机制

    • 理解list_for_each_entrylist_for_each_entry_safe的区别
    • 链表毒药指针的作用
  2. SCTP协议实现

    • 关联(asoc)的生命周期管理
    • SCTP消息发送流程
  3. 内核漏洞分析技巧

    • 补丁分析
    • 崩溃日志解读
    • 内存断点设置

学习建议

  1. 深入理解Linux内核链表实现
  2. 研究SCTP协议规范及内核实现
  3. 练习内核调试技巧
  4. 分析更多类似的链表操作漏洞案例

扩展阅读

  1. Linux内核链表实现源码:include/linux/list.h
  2. SCTP协议RFC文档:RFC4960
  3. 内核内存调试工具:KASAN, KFENCE
  4. 相关漏洞案例:CVE-2017-2636, CVE-2018-5391
CVE-2019-8956漏洞分析与教学文档 漏洞概述 CVE-2019-8956是Linux内核中SCTP(流控制传输协议)实现的一个漏洞,存在于 sctp_sendmsg() 函数中。该漏洞可能导致内核崩溃或潜在的安全问题,属于释放后使用(Use-After-Free)类型漏洞。 漏洞背景 SCTP协议简介 SCTP(Stream Control Transmission Protocol)是一种传输层协议,提供可靠的、面向消息的数据传输服务。其特点包括: 多宿主支持 多流特性 消息边界保持 选择性确认 防止拒绝服务攻击 SCTP包结构 SCTP包由以下部分组成: 公共头(Common Header):包含源/目的端口、校验和、Verification Tag等 一个或多个chunk:每个chunk有自己的类型和标志 漏洞分析 漏洞位置 漏洞位于 net/sctp/socket.c 文件中的 sctp_sendmsg() 函数,具体是在处理 SCTP_SENDALL 标志时使用了不安全的链表遍历方式。 补丁分析 补丁链接:https://git.kernel.org/linus/ba59fb0273076637f0add4311faa990a5eec27c0 补丁将: 替换为: 关键宏定义区别 list_for_each_entry : list_for_each_entry_safe : 关键区别: list_for_each_entry_safe 在遍历前预先获取下一个元素的指针 当当前元素被删除时,仍能安全地继续遍历 SCTP关联(Association)结构 sctp_association 结构体表示SCTP连接的重要信息,包含: assoc_id : 关联ID(唯一标识) c : 与关联状态相关的cookie peer : 表示远程端点的结构体 transport_addr_list : 保存建立关联后的一个或多个地址 primary_path : 初始连接使用的地址 state : 关联状态 asocs : 链表节点,用于将关联连接到端点(ep)的关联列表中 漏洞触发机制 触发条件 发送SCTP消息时设置 SCTP_ABORT|SCTP_SENDALL 标志 使用UDP风格的SCTP套接字 漏洞触发流程 当 SCTP_SENDALL 标志设置时,代码会遍历端点(ep)的所有关联(asoc) 对每个关联调用 sctp_sendmsg_check_sflags() 进行检查 如果设置了 SCTP_ABORT 标志,可能会删除当前关联 使用 list_for_each_entry 时,删除当前关联会破坏链表遍历 下一次遍历时会使用已被释放的内存,导致崩溃 崩溃分析 崩溃时的关键点: 关联被删除时,其 asocs 链表节点会被设置为 LIST_POISON1 和 LIST_POISON2 LIST_POISON1 = 0xdead000000000100 LIST_POISON2 = 0xdead000000000200 下次遍历时,代码尝试访问 asoc->asocs.next (已被设置为毒药指针) 导致非法内存访问,引发内核崩溃 POC构造 基本POC构造思路: 关键点: 设置 SCTP_ABORT|SCTP_SENDALL 标志 使用UDP风格的SCTP套接字 调试分析 调试时观察的关键点: 关联(asoc)结构的内存状态 asocs 链表节点的变化 内存访问断点在 asoc->asocs 上的触发 关键调试步骤: 在 sctp_sendmsg 函数设置断点 观察关联结构的链表操作 设置内存断点监视 asoc->asocs 的变化 修复方案 补丁通过使用 list_for_each_entry_safe 宏修复了此漏洞,该宏: 在遍历前预先获取下一个元素的指针 允许在遍历过程中安全地删除当前元素 避免了释放后使用的问题 教学总结 关键知识点 Linux内核链表操作机制 理解 list_for_each_entry 和 list_for_each_entry_safe 的区别 链表毒药指针的作用 SCTP协议实现 关联(asoc)的生命周期管理 SCTP消息发送流程 内核漏洞分析技巧 补丁分析 崩溃日志解读 内存断点设置 学习建议 深入理解Linux内核链表实现 研究SCTP协议规范及内核实现 练习内核调试技巧 分析更多类似的链表操作漏洞案例 扩展阅读 Linux内核链表实现源码: include/linux/list.h SCTP协议RFC文档:RFC4960 内核内存调试工具:KASAN, KFENCE 相关漏洞案例:CVE-2017-2636, CVE-2018-5391