CVE-2018-3639/CVE-2019-7308—Spectre攻击linux内核ebpf的分析
字数 2972 2025-08-05 19:10:02

eBPF与Spectre漏洞分析:CVE-2018-3639和CVE-2019-7308深入解析

前言

本文深入分析两个与eBPF相关的Spectre漏洞:CVE-2018-3639(Spectre变种4)和CVE-2019-7308(Spectre变种1补丁不完善导致的问题)。这些漏洞利用了现代CPU的推测执行特性,通过eBPF子系统实现侧信道攻击。

1. Spectre变种4 (CVE-2018-3639) 与eBPF

1.1 Speculative Store Bypass原理

Spectre变种4又称Speculative Store Bypass (SSB),其核心原理如下:

考虑以下代码序列:

01: 88040F mov [rdi+rcx],al
02: 4C0FB6040E movzx r8,byte [rsi+rcx]
03: 49C1E00C shl r8,byte 0xc
04: 428B0402 mov eax,[rdx+r8]

攻击场景:

  1. 第1行的MOV指令可能因地址计算等待而延迟执行
  2. CPU可能推测MOVZX指令(第2行)不依赖MOV指令(第1行)
  3. 在MOV指令执行前就执行MOVZX指令
  4. 导致加载了RSI+RCX处的旧数据而非更新后的数据
  5. 最终第4行使用了错误的数据

1.2 eBPF利用实现

Project Zero提供的利用CVE-2018-3639攻击eBPF的EXP需要对Linux内核进行修改:

  1. 在eBPF系统调用中添加处理0x13370001的特殊命令
  2. 添加map_time_flush_loc函数,用于测量读取特定内存位置的时间
  3. 添加bpf_clflush_mfence_proto helper函数,用于清空缓存

eBPF指令分析

关键eBPF指令及其伪代码注释:

  1. 初始化设置:

    • 获取需要泄露的内存地址
    • 设置leak_map偏移2048处为基准点
  2. 关键操作序列:

    • r1 = r7并清空缓存
    • r3 = fp-216(设置探测点)
    • 利用*(u64 *)(r8) = r3执行慢的特性,提前执行r1 = *(u64 *)(r6)获取目标地址
    • r2 = *(u8 *)(r1)获取目标值
  3. 探测逻辑:

    • 对于sockfds[bit+8]*(u8 *)(fp-216) = 0x00
    • 对于sockfds[bit+0]*(u8 *)(fp-216) = 0xff
    • 根据读取leak_map不同偏移的时间差判断bit值:
      • 读取leak_map偏移2048+0x1000时间短 → bit=1
      • 读取leak_map偏移2048时间短 → bit=0

实际利用示例

泄露内核符号core_pattern的值:

$ sudo grep core_pattern /proc/kallsyms
ffffffff9b2954e0 D core_pattern
$ gcc -o bpf_store_skipper_assisted bpf_store_skipper_assisted.c
$ time ./bpf_store_skipper_assisted ffffffff9b2954e0 5
...
ffffffff9b2954e0: 0x63 ( 'c' )
ffffffff9b2954e1: 0x6f ( 'o' )
ffffffff9b2954e2: 0x72 ( 'r' )
ffffffff9b2954e3: 0x65 ( 'e' )
ffffffff9b2954e4: 0x00 ( '' )

2. Spectre变种1补丁不完善导致的CVE-2019-7308

2.1 Bounds Check Bypass原理

Spectre变种1又称Bounds Check Bypass (BCB),其核心原理:

考虑以下代码:

if (untrusted_offset_from_caller < arr1->length) {
    unsigned char value = arr1->data[untrusted_offset_from_caller];
    unsigned long index2 = ((value&1)*0x100)+0x200;
    if (index2 < arr2_length) {
        unsigned char value2 = arr2->data[index2];
    }
}

攻击场景:

  1. CPU可能不检查if条件就推测其为真
  2. 提前将arr1->data[untrusted_offset_from_caller]加载到缓存
  3. 即使发现推测错误回滚指令,缓存状态仍保留
  4. 通过侧信道攻击可获取越界访问的数据

2.2 eBPF补丁不完善问题

原始补丁措施:

  • 在每个数组元素访问操作中将索引与掩码相与,避免越界访问
  • 对于非CAP_SYS_ADMIN用户创建的map,在JIT代码中添加相同操作

补丁缺陷:

  • 未正确处理数组指针计算的情况
  • 例如ptr += valval += ptr等操作未充分保护

2.3 利用方式分析

利用程序包含两个关键结构体:

  1. mem_leaker_prog:主攻击程序
  2. array_timed_reader_prog:用于时间测量

攻击步骤:

  1. 设置mem_leaker_prog.data_map偏移0x1200、0x2000和0x3000处为1
  2. 通过leak_byte函数调用leak_bit逐bit泄露内存

eBPF指令分析

关键指令序列:

  1. 获取12-bit_indexbyte_offset参数
  2. 读取data_map偏移0x1200处的值(已知为1)
  3. 通过BPF_ANDBPF_OR延长执行时间,促使BPF_JGT推测执行失败
  4. r4 = *(u8 *)(r4)读取目标内存
  5. 根据bit值:
    • bit=1:访问data_map偏移0x3000
    • bit=0:访问data_map偏移0x2000
  6. 通过测量两个偏移的访问时间差确定bit值

2.4 完整补丁方案

完整补丁需要考虑:

  1. 值可能存在于源寄存器(ptr+=val)或目的寄存器(val+=ptr)
  2. limit取决于ALU操作类型(加/减)和偏移量正负

retrieve_ptr_limit函数逻辑:

  • 加法操作但偏移为负,或减法操作但偏移为正 → 视为减法,mask_to_left=1
  • 否则视为加法,mask_to_left=0
  • 对于map指针:
    • 减法:limit = ptr_reg->umax_value + ptr_reg->off
    • 加法:limit = ptr_reg->map_ptr->value_size - (ptr_reg->smin_value + ptr_reg->off)
  • 对于stack指针:
    • 减法:limit = MAX_BPF_STACK + off
    • 加法:limit = -off (其中off = ptr_reg->off + ptr_reg->var_off.value)

fixup_bpf_calls函数:

  • 根据计算的limit对寄存器值进行掩码运算
  • 彻底防止越界访问

3. 总结与防护建议

关键发现

  1. eBPF子系统成为Spectre漏洞的重要攻击向量
  2. 原始补丁未全面考虑所有指针运算场景
  3. 通过精心构造的eBPF程序可绕过边界检查
  4. 侧信道计时攻击是核心利用手段

防护措施

  1. 及时更新内核,应用完整补丁
  2. 限制非特权用户的eBPF使用
  3. 启用所有Spectre变种的缓解措施
  4. 监控异常的eBPF程序行为

进一步研究资源

CPU漏洞相关研究资料:
https://github.com/houjingyi233/CPU-vulnerability-collections

参考资料

  1. Reading privileged memory with a side-channel
  2. 深入分析Ubuntu本地提权漏洞—【CVE-2017-16995】
  3. http://man7.org/linux/man-pages/man2/bpf.2.html
  4. Linux CVE-2017-16995整数扩展问题导致提权漏洞分析
  5. Issue 1711: Linux: eBPF Spectre v1 mitigation is insufficient
  6. https://www.kernel.org/doc/Documentation/networking/filter.txt
  7. Analysis and mitigation of speculative store bypass (CVE-2018-3639)
  8. Issue 1528: speculative execution, variant 4: speculative store bypass
eBPF与Spectre漏洞分析:CVE-2018-3639和CVE-2019-7308深入解析 前言 本文深入分析两个与eBPF相关的Spectre漏洞:CVE-2018-3639(Spectre变种4)和CVE-2019-7308(Spectre变种1补丁不完善导致的问题)。这些漏洞利用了现代CPU的推测执行特性,通过eBPF子系统实现侧信道攻击。 1. Spectre变种4 (CVE-2018-3639) 与eBPF 1.1 Speculative Store Bypass原理 Spectre变种4又称Speculative Store Bypass (SSB),其核心原理如下: 考虑以下代码序列: 攻击场景: 第1行的MOV指令可能因地址计算等待而延迟执行 CPU可能推测MOVZX指令(第2行)不依赖MOV指令(第1行) 在MOV指令执行前就执行MOVZX指令 导致加载了RSI+RCX处的旧数据而非更新后的数据 最终第4行使用了错误的数据 1.2 eBPF利用实现 Project Zero提供的利用CVE-2018-3639攻击eBPF的EXP需要对Linux内核进行修改: 在eBPF系统调用中添加处理0x13370001的特殊命令 添加 map_time_flush_loc 函数,用于测量读取特定内存位置的时间 添加 bpf_clflush_mfence_proto helper函数,用于清空缓存 eBPF指令分析 关键eBPF指令及其伪代码注释: 初始化设置: 获取需要泄露的内存地址 设置 leak_map 偏移2048处为基准点 关键操作序列: r1 = r7 并清空缓存 r3 = fp-216 (设置探测点) 利用 *(u64 *)(r8) = r3 执行慢的特性,提前执行 r1 = *(u64 *)(r6) 获取目标地址 r2 = *(u8 *)(r1) 获取目标值 探测逻辑: 对于 sockfds[bit+8] , *(u8 *)(fp-216) = 0x00 对于 sockfds[bit+0] , *(u8 *)(fp-216) = 0xff 根据读取 leak_map 不同偏移的时间差判断bit值: 读取 leak_map偏移2048+0x1000 时间短 → bit=1 读取 leak_map偏移2048 时间短 → bit=0 实际利用示例 泄露内核符号 core_pattern 的值: 2. Spectre变种1补丁不完善导致的CVE-2019-7308 2.1 Bounds Check Bypass原理 Spectre变种1又称Bounds Check Bypass (BCB),其核心原理: 考虑以下代码: 攻击场景: CPU可能不检查if条件就推测其为真 提前将 arr1->data[untrusted_offset_from_caller] 加载到缓存 即使发现推测错误回滚指令,缓存状态仍保留 通过侧信道攻击可获取越界访问的数据 2.2 eBPF补丁不完善问题 原始补丁措施: 在每个数组元素访问操作中将索引与掩码相与,避免越界访问 对于非CAP_ SYS_ ADMIN用户创建的map,在JIT代码中添加相同操作 补丁缺陷: 未正确处理数组指针计算的情况 例如 ptr += val 或 val += ptr 等操作未充分保护 2.3 利用方式分析 利用程序包含两个关键结构体: mem_leaker_prog :主攻击程序 array_timed_reader_prog :用于时间测量 攻击步骤: 设置 mem_leaker_prog.data_map 偏移0x1200、0x2000和0x3000处为1 通过 leak_byte 函数调用 leak_bit 逐bit泄露内存 eBPF指令分析 关键指令序列: 获取 12-bit_index 和 byte_offset 参数 读取 data_map 偏移0x1200处的值(已知为1) 通过 BPF_AND 和 BPF_OR 延长执行时间,促使 BPF_JGT 推测执行失败 r4 = *(u8 *)(r4) 读取目标内存 根据bit值: bit=1:访问 data_map 偏移0x3000 bit=0:访问 data_map 偏移0x2000 通过测量两个偏移的访问时间差确定bit值 2.4 完整补丁方案 完整补丁需要考虑: 值可能存在于源寄存器( ptr+=val )或目的寄存器( val+=ptr ) limit取决于ALU操作类型(加/减)和偏移量正负 retrieve_ptr_limit 函数逻辑: 加法操作但偏移为负,或减法操作但偏移为正 → 视为减法, mask_to_left=1 否则视为加法, mask_to_left=0 对于map指针: 减法: limit = ptr_reg->umax_value + ptr_reg->off 加法: limit = ptr_reg->map_ptr->value_size - (ptr_reg->smin_value + ptr_reg->off) 对于stack指针: 减法: limit = MAX_BPF_STACK + off 加法: limit = -off (其中 off = ptr_reg->off + ptr_reg->var_off.value ) fixup_bpf_calls 函数: 根据计算的limit对寄存器值进行掩码运算 彻底防止越界访问 3. 总结与防护建议 关键发现 eBPF子系统成为Spectre漏洞的重要攻击向量 原始补丁未全面考虑所有指针运算场景 通过精心构造的eBPF程序可绕过边界检查 侧信道计时攻击是核心利用手段 防护措施 及时更新内核,应用完整补丁 限制非特权用户的eBPF使用 启用所有Spectre变种的缓解措施 监控异常的eBPF程序行为 进一步研究资源 CPU漏洞相关研究资料: https://github.com/houjingyi233/CPU-vulnerability-collections 参考资料 Reading privileged memory with a side-channel 深入分析Ubuntu本地提权漏洞—【CVE-2017-16995】 http://man7.org/linux/man-pages/man2/bpf.2.html Linux CVE-2017-16995整数扩展问题导致提权漏洞分析 Issue 1711: Linux: eBPF Spectre v1 mitigation is insufficient https://www.kernel.org/doc/Documentation/networking/filter.txt Analysis and mitigation of speculative store bypass (CVE-2018-3639) Issue 1528: speculative execution, variant 4: speculative store bypass