Redis hyperloglog远程代码执行漏洞 (CVE-2025-32023)复现分析
字数 2498 2025-09-01 11:26:03

Redis HyperLogLog 远程代码执行漏洞(CVE-2025-32023)分析与利用

漏洞概述

Redis HyperLogLog(HLL)数据结构在处理稀疏型与密集型HLL合并操作时存在整数溢出漏洞,攻击者可通过构造恶意HLL数据触发该漏洞,最终实现远程代码执行。该漏洞编号为CVE-2025-32023,影响Redis多个版本。

漏洞原理

HyperLogLog数据结构

HyperLogLog是Redis用于基数统计的数据结构,使用16384个6位寄存器存储基数估算中间状态。HLL有两种存储模式:

  1. 密集模式:直接存储所有寄存器值,内存消耗较大
  2. 稀疏模式:通过编码压缩存储连续相同值,编码格式如下:
类型 编码格式 说明
拓展零运行 xzero 01xxxxxx yyyyyyyy 表示连续较长的零值区域
零运行 zero 00xxxxxx 设置连续但不够长的零值区域
特殊值 1vvvvvxx 紧凑存储小值

漏洞根源

hllMergehllSparseToDense函数中存在整数溢出漏洞:

  1. 处理ZERO/XZERO编码时无边界检查,仅VAL类型存在检查if ((runlen + idx) > HLL_REGISTERS) break
  2. 检查中的idxint类型,可能为负值
  3. 溢出后HLL_DENSE_SET_REGISTER(hdr->registers,idx,regval)可越界修改堆内存

利用流程

1. 通过整数溢出导致受限地址写

利用步骤

  1. 构造恶意稀疏型HLL数据:

    • HYLL魔术头开头
    • 设置稀疏编码标识并填充至0x10字节完成头部构造
    • 通过xzero(0x4000)-0x3fffdxzero(0xc000-0x956c)组合使计数器溢出
    • 通过p8(0b1_00011_00)设置runlen=1regval=4(对应SDS_TYPE_64)
  2. 触发漏洞:

    • 将目标key(即pfmerge命令的首个参数)预设为稀疏型HLL
    • 确保复制后的新空间在转换为密集型时触发漏洞

效果:实现受限地址写能力(覆盖范围为int负数空间的3/4)

2. 堆风水构建实现大范围内存读写

堆布局构建

  1. 预分配三个sdshdr16实例(sdsa/b/c)
  2. 调整数据尺寸使其均分配0x3800大小的堆块
  3. 形成四个0x3800堆块连续排列的内存布局

内存修改

  1. 通过漏洞修改sdsbflags字段
  2. SDSHDR16(0x02)改为SDSHDR64(0x04)
  3. 使len/alloc字段解析范围从2字节扩展至8字节

效果:实现大范围内存读写能力

3. 堆喷射和内存块识别泄露基地址

3.1 泄露sdsb基址

步骤

  1. 通过embstr对象内存布局实现地址计算
  2. 采用特征化批量喷射策略(堆喷射)
  3. 生成8字节随机标记用于识别
  4. 计算喷射总量spray_cnt = 0x100000 // 0x40
  5. 每个对象命名格式为sds:_{idx},内容结构为标记(8字节)+序号(8字节)

地址解析

  1. 通过三重校验确保目标对象的合法性:
    • 对象头校验(dump[tofs] == 0x80表示embstr类型)
    • 引用计数校验(u32(dump[tofs+4:tofs+8]) == 0x1)
    • SDS头部校验(dump[tofs+0x10:tofs+0x13] == b'\x2b\x2b\x01')
  2. 逆向推导出sdsb字符串的头部地址

3.2 泄露程序基地址

jemalloc内存结构

  • 主分配区采用独立开辟的2MB内存块
  • 堆块头部包含base_block_t结构体、edata_t元数据及base_t管理结构
  • base_t中两个ehooks_t成员的ptr字段指向ehooks_default_extent_hooks

泄露方法

  1. 遍历堆内存转储数据,筛选满足0x200000(2MB)大小的内存块
  2. 验证0xc8-0xcf与0xd8-0xdf偏移处的两个地址的低12位是否与je_ehooks_default_extent_hooks符号的低12位一致
  3. 通过符号的编译偏移量反向计算进程基址

4. 构造伪模块完成栈迁移实现提权

4.1 构造模块对象

步骤

  1. 修改之前泄露的embstr对象的robj头部:
    • 类型标记设为0x05(对应Module类型)
    • 保持原有编码和LRU字段
    • 设置引用计数为1
    • 将对象指针重定向至可控内存区域
  2. 伪造moduleValue结构体
  3. 修改type指针指向地址,将free函数指向ROP gadget

4.2 栈迁移与ROP链执行

ROP链功能

  1. 通过三次调用dup2系统调用,将当前连接的文件描述符fd复制到标准输入(0)、标准输出(1)和标准错误(2)
  2. 执行execve("/bin/sh")获取shell

栈迁移过程

  1. 进入gadget#0,rdi被设置为badr + 0x2010
  2. 执行gadget#1,rdi被修改为badr + 0x2028
  3. 执行gadget#2,rbp的值为badr + 0x2028
  4. 执行leave指令,rsp的值变为badr + 0x2030,完成栈转移

防御建议

  1. 升级到修复该漏洞的Redis版本
  2. 对HLL数据结构操作添加严格的边界检查
  3. 启用Redis的安全配置,如禁用危险命令、限制网络访问等
  4. 使用内存保护机制如ASLR、DEP等

总结

CVE-2025-32023漏洞通过精心构造的HLL数据结构触发整数溢出,结合堆布局操控和ROP技术实现远程代码执行。该漏洞利用展示了从内存破坏到完整权限提升的完整攻击链,强调了数据结构边界检查的重要性。

Redis HyperLogLog 远程代码执行漏洞(CVE-2025-32023)分析与利用 漏洞概述 Redis HyperLogLog(HLL)数据结构在处理稀疏型与密集型HLL合并操作时存在整数溢出漏洞,攻击者可通过构造恶意HLL数据触发该漏洞,最终实现远程代码执行。该漏洞编号为CVE-2025-32023,影响Redis多个版本。 漏洞原理 HyperLogLog数据结构 HyperLogLog是Redis用于基数统计的数据结构,使用16384个6位寄存器存储基数估算中间状态。HLL有两种存储模式: 密集模式 :直接存储所有寄存器值,内存消耗较大 稀疏模式 :通过编码压缩存储连续相同值,编码格式如下: | 类型 | 编码格式 | 说明 | |------|----------|------| | 拓展零运行 | xzero 01xxxxxx yyyyyyyy | 表示连续较长的零值区域 | | 零运行 | zero 00xxxxxx | 设置连续但不够长的零值区域 | | 特殊值 | 1vvvvvxx | 紧凑存储小值 | 漏洞根源 在 hllMerge 和 hllSparseToDense 函数中存在整数溢出漏洞: 处理 ZERO / XZERO 编码时无边界检查,仅 VAL 类型存在检查 if ((runlen + idx) > HLL_REGISTERS) break 检查中的 idx 为 int 类型,可能为负值 溢出后 HLL_DENSE_SET_REGISTER(hdr->registers,idx,regval) 可越界修改堆内存 利用流程 1. 通过整数溢出导致受限地址写 利用步骤 : 构造恶意稀疏型HLL数据: 以 HYLL 魔术头开头 设置稀疏编码标识并填充至0x10字节完成头部构造 通过 xzero(0x4000)-0x3fffd 与 xzero(0xc000-0x956c) 组合使计数器溢出 通过 p8(0b1_00011_00) 设置 runlen=1 、 regval=4 (对应 SDS_TYPE_64 ) 触发漏洞: 将目标key(即 pfmerge 命令的首个参数)预设为稀疏型HLL 确保复制后的新空间在转换为密集型时触发漏洞 效果 :实现受限地址写能力(覆盖范围为int负数空间的3/4) 2. 堆风水构建实现大范围内存读写 堆布局构建 : 预分配三个 sdshdr16 实例( sdsa/b/c ) 调整数据尺寸使其均分配0x3800大小的堆块 形成四个0x3800堆块连续排列的内存布局 内存修改 : 通过漏洞修改 sdsb 的 flags 字段 将 SDSHDR16 (0x02)改为 SDSHDR64 (0x04) 使 len/alloc 字段解析范围从2字节扩展至8字节 效果 :实现大范围内存读写能力 3. 堆喷射和内存块识别泄露基地址 3.1 泄露 sdsb 基址 步骤 : 通过 embstr 对象内存布局实现地址计算 采用特征化批量喷射策略(堆喷射) 生成8字节随机标记用于识别 计算喷射总量 spray_cnt = 0x100000 // 0x40 每个对象命名格式为 sds:_{idx} ,内容结构为标记(8字节)+序号(8字节) 地址解析 : 通过三重校验确保目标对象的合法性: 对象头校验( dump[tofs] == 0x80 表示 embstr 类型) 引用计数校验( u32(dump[tofs+4:tofs+8]) == 0x1 ) SDS头部校验( dump[tofs+0x10:tofs+0x13] == b'\x2b\x2b\x01' ) 逆向推导出 sdsb 字符串的头部地址 3.2 泄露程序基地址 jemalloc内存结构 : 主分配区采用独立开辟的2MB内存块 堆块头部包含 base_block_t 结构体、 edata_t 元数据及 base_t 管理结构 base_t 中两个 ehooks_t 成员的 ptr 字段指向 ehooks_default_extent_hooks 泄露方法 : 遍历堆内存转储数据,筛选满足0x200000(2MB)大小的内存块 验证0xc8-0xcf与0xd8-0xdf偏移处的两个地址的低12位是否与 je_ehooks_default_extent_hooks 符号的低12位一致 通过符号的编译偏移量反向计算进程基址 4. 构造伪模块完成栈迁移实现提权 4.1 构造模块对象 步骤 : 修改之前泄露的 embstr 对象的 robj 头部: 类型标记设为0x05(对应 Module 类型) 保持原有编码和LRU字段 设置引用计数为1 将对象指针重定向至可控内存区域 伪造 moduleValue 结构体 修改 type 指针指向地址,将 free 函数指向ROP gadget 4.2 栈迁移与ROP链执行 ROP链功能 : 通过三次调用 dup2 系统调用,将当前连接的文件描述符 fd 复制到标准输入(0)、标准输出(1)和标准错误(2) 执行 execve("/bin/sh") 获取shell 栈迁移过程 : 进入gadget#0, rdi 被设置为 badr + 0x2010 执行gadget#1, rdi 被修改为 badr + 0x2028 执行gadget#2, rbp 的值为 badr + 0x2028 执行 leave 指令, rsp 的值变为 badr + 0x2030 ,完成栈转移 防御建议 升级到修复该漏洞的Redis版本 对HLL数据结构操作添加严格的边界检查 启用Redis的安全配置,如禁用危险命令、限制网络访问等 使用内存保护机制如ASLR、DEP等 总结 CVE-2025-32023漏洞通过精心构造的HLL数据结构触发整数溢出,结合堆布局操控和ROP技术实现远程代码执行。该漏洞利用展示了从内存破坏到完整权限提升的完整攻击链,强调了数据结构边界检查的重要性。