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包由以下部分组成:
- 公共头(Common Header):包含源/目的端口、校验和、Verification Tag等
- 一个或多个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)
关键宏定义区别
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))
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: 与关联状态相关的cookiepeer: 表示远程端点的结构体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_POISON2LIST_POISON1=0xdead000000000100LIST_POISON2=0xdead000000000200
- 下次遍历时,代码尝试访问
asoc->asocs.next(已被设置为毒药指针) - 导致非法内存访问,引发内核崩溃
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套接字
调试分析
调试时观察的关键点:
- 关联(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