Attacker Value
Very High
(1 user assessed)
Exploitability
Very High
(1 user assessed)
User Interaction
Unknown
Privileges Required
Unknown
Attack Vector
Unknown
4

CVE-2022-0543

Disclosure Date: February 18, 2022
Exploited in the Wild
Add MITRE ATT&CK tactics and techniques that apply to this CVE.

Description

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.

Add Assessment

2
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

Muhstik Gang has been seen exploiting this vulnerability to target Redis servers
Poc is publicly available https://github.com/aodsec/CVE-2022-0543

General Information

Vendors

  • Debian

Products

  • redis

Additional Info

Technical Analysis

On February 18, 2022, Ubuntu and Debian released security advisories for CVE-2022-0543. The vulnerability is the result of an oversight in the operating systems’ Redis package, and not actually a fault in Redis itself. Insufficient sanitization of the Lua environment used by Redis resulted in remote attackers being able to execute arbitrary Lua and escape the Redis sandbox. The vulnerability was assigned a critical CVSSv3 score of 10.0 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H).

On March 8, a public proof of concept was published by Reginaldo Silva, who is also credited with finding the vulnerability. Juniper Threat Labs followed up with a report of exploitation in the wild on March 24, and a Metasploit module was published on April 26. Given availability of both private and public weaponized exploits, attackers will continue to opportunistically exploit this vulnerability as long as there are internet facing targets to exploit.

Targets?

Initially, this vulnerability sounded like an edge case that wouldn’t affect many hosts. It only affected distros using Ubuntu or Debian Redis packages and requires the user to configure Redis in a non-default and known “dangerous” state; something that Redis does a commendable job of warning the user against. The user then needs to expose the poor configuration to the internet. For this to be widely exploited, and therefore a priority from a research perspective, a good number of people would need to take these steps to render themselves exposed and exploitable. We assumed exploitable internet-facing targets would be few.

However, reports of exploitation in the wild did catch our attention, and got us thinking,
“Given the requirements for exploitation, are there really enough vulnerable targets for attackers to pursue this?” This is actually a very important question. Like most in the infosec world, we have to prioritize incoming threats and we only have so much bandwidth to expend. We didn’t prioritize this vulnerability initially, so it’s fun to run through the exercise of determining if we were right or wrong (Spolier: We ended up wroting a Metasploit module and this AKB entry):

On April 27, 2022, we found that there are approximately 33,000 internet-facing Redis servers that allow unauthenticated access.

shodan

Of course, CVE-2022-0543 also affects hosts that require authentication, but we’re going to focus on the unauthenticated case.

Now that we have a baseline of unauthenticated internet-facing Redis, the question is, “How many are actually vulnerable to CVE-2022-0543?” Apparently, it’s “wrong” and “illegal” to exploit targets in the wild to satisfy curiosity so we relied on the output Shodan acquired. Shodan appears to issue the INFO command after connecting to a Redis server. This command returns a lot of useful information, like os, redis_version, build_id, etc.

We sorted the 33,000 hosts Shodan identified by the “OS” field to see if we could weed out some obviously unaffected targets. Here is the top ten list:

Count OS String
8055 os:Linux 4.14.109-99.92.amzn2.x86_64 x86_64
1293 os:Linux 3.10.0-1160.11.1.el7.x86_64 x86_64
1158 os:Linux 3.10.0-1160.45.1.el7.x86_64 x86_64
992 os:Linux 3.10.0-1127.19.1.el7.x86_64 x86_64
859 os:Windows
419 os:Linux 5.4.0-107-generic x86_64
380 os:Linux 3.10.0-957.el7.x86_64 x86_64
366 os:Linux 4.18.0-305.3.1.el8.x86_64 x86_64
359 os:Linux 3.10.0-1160.49.1.el7.x86_64 x86_64
349 os:Linux 5.4.0-77-generic x86_64

Only two rows in the list above could potentially be Ubuntu, Debian, or derivatives. amzn2 (Amazon Linux 2) and el7 (CentOS) use yum-based systems and therefore aren’t affected. Windows isn’t affected. Only OS with the generic string look like our test targets. Our test targets have OS strings like the following:

OS String Exploitable
Ubuntu 20.04 os:Linux 5.13.0-40-generic x86_64 Yes
Ubuntu 18.04 os:Linux 5.4.0-42-generic i686 No
Debian 11 os:Linux 5.10.0-13-amd64 x86_64 No
Ubuntu Docker os:Linux 5.13.0-40-generic x86_64 Yes

If we filter the original 33,000 Redis hosts to only include the “-generic” or “-amd64” OS strings then we quickly narrow the search down to ~8100 hosts. The top 10 now looks like this:

Count OS String
419 os:Linux 5.4.0-107-generic x86_64
349 os:Linux 5.4.0-77-generic x86_64
319 os:Linux 5.4.0-100-generic x86_64
256 os:Linux 5.4.0-96-generic x86_64
250 os:Linux 5.4.0-97-generic x86_64
224 os:Linux 5.4.0-104-generic x86_64
221 os:Linux 5.4.0-105-generic x86_64
213 os:Linux 5.4.0-90-generic x86_64
187 os:Linux 5.4.0-88-generic x86_64
181 os:Linux 4.15.0-142-generic x86_64
—- —————-

Now that we’ve narrowed our list to systems that are potentially Ubuntu, Debian, or variants, we can start looking a little closer at the redis_version field. We know that the affected Debian packages installed Redis versions that very loosely fell between versions 5.0 and 6.1. Of the 8100 hosts we’ve narrowed our search down to, those versions are not highly represented in the data. Again a top 10 list:

Count Redis Version
3664 redis_version:6.2.6
581 redis_version:6.2.5
529 redis_version:5.0.7
378 redis_version:4.0.9
282 redis_version:6.2.4
247 redis_version:3.0.6
238 redis_version:3.2.12
235 redis_version:6.2.1
226 redis_version:6.0.9
211 redis_version:5.0.14

The Ubuntu and Debian advisories don’t list all of the affected packages, they just state the fixed version. Based on past experience, the patched package doesn’t update the “redis_version” so when Ubuntu says a fix was introduced in “5.0.7-2ubuntu0.1”, then we know redis_version 5.0.7 was an affected version. In the list above, 529 hosts advertise redis_version 5.0.7. Those hosts may or may not be Ubuntu/Debian variants and, if they are, the Redis package may or may not have been patched. But it does give some insight into the scope of affected targets.

Taking things a little further, we also know that redis_versions not explicitly mentioned in the advisory were affected. For example, the official Ubuntu Redis docker image we tested our Metasploit module with used redis_version 6.0.11. Instead of tracking down all potential versions, if we just generically accept redis_version 5.0 and above may be affected, and redis_version before 6.1 may be affected then we narrow the original ~8100 down to ~2000. Here is that complete dataset:

Count Redis Version
529 redis_version:5.0.7
226 redis_version:6.0.9
211 redis_version:5.0.14
151 redis_version:6.0.10
134 redis_version:6.0.6
118 redis_version:6.0.16
99 redis_version:6.0.8
90 redis_version:6.0.5
70 redis_version:5.0.3
55 redis_version:5.0.4
50 redis_version:5.0.5
38 redis_version:6.0.15
28 redis_version:5.0.8
24 redis_version:6.0.3
19 redis_version:6.0.7
19 redis_version:6.0.1
19 redis_version:5.0.12
18 redis_version:6.0.4
18 redis_version:5.0.9
18 redis_version:5.0.13
12 redis_version:5.0.6
10 redis_version:6.0.12
10 redis_version:5.0.2
9 redis_version:6.0.11
9 redis_version:5.0.10
8 redis_version:6.0.14
8 redis_version:5.0.1
5 redis_version:6.0.2
5 redis_version:6.0.13
4 redis_version:5.9.104
2 redis_version:5.9.103
2 redis_version:5.9.101
2 redis_version:5.3.3
2 redis_version:5.0.0
1 redis_version:6.0.18
1 redis_version:6.0.0
1 redis_version:5.9.102

2,000 hosts is the absolute ceiling of potentially vulnerable internet-facing Redis servers that can be exploited without authentication. We actually aren’t certain how many of these hosts installed Redis using an affected package or if they’ve been patched (we are looking at this approximately two months after initial disclosure). Heck, they could all be honeypots for all we know, but it’s a reasonable number to work with.

Is 2,000 hosts enough to grab our attention? While it’s difficult to call that “widespread”, it appears hosts vulnerable to CVE-2022-0543 are probably more prevalent than we anticipated. Given an exploit has been thrown around in the wild, it’s probably reasonable to bump the priority of fixing this vulnerability within your own organization.

Root Cause

The root cause is a simple oversight. Normally, Redis statically links Lua. The Ubuntu and Debian package dynamically links Lua. The vulnerable package disabled the use of the Lua require and module interfaces to prevent sandbox escapes, but failed to disable the Lua package interface. To fix this, Ubuntu and Debian simply set package to nil just like require and module. Here is the relevant line in Ubuntu’s rules file:

echo 'luaL_dostring(lua, "module = nil; require = nil; package = nil");' >>$@

Proof of Concept

The Lua package interface can be used to load arbitrary Lua shared libraries. For example, the original proof of concept by Reginaldo Silva loads “liblua” in order to execute the shell command touch /tmp/redis_poc via os.execute:

eval 'local os_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so", "luaopen_os"); local os = os_l(); os.execute("touch /tmp/redis_poc"); return 0' 

An unattributed proof of concept, possibly developed by phithon appeared on vulhub, and used io.popen so that the server’s response would be written back to the attacker. Below the attacker executes the command id.

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

If you are reading the write-ups associated with these proof of concepts, and testing them yourself then you may find them a little misleading. The first point of confusion, for us, involved the touch /tmp/redis_poc in the original proof of concept. A reasonable person is going to look for the created file in /tmp/. However, the vulnerable packages install Redis under systemd with PrivateTmp=yes. Meaning, the file won’t be written to /tmp/. It will actually be written in some systemd-private subdirectory within /tmp. This could cause someone to think they aren’t vulnerable when, in fact, they are:

albinolobster@ubuntu:~/$ ls -l /tmp/redis_poc
ls: cannot access '/tmp/redis_poc': No such file or directory
albinolobster@ubuntu:~/$ sudo ls -l /tmp/systemd-private-214bed33ff5542c5bb9c754ce7984d61-redis-server.service-xOUzAh/tmp/redis_poc
-rw-rw---- 1 redis redis 0 Apr 27 10:19 /tmp/systemd-private-214bed33ff5542c5bb9c754ce7984d61-redis-server.service-xOUzAh/tmp/redis_poc

Additionally, the vulhub repository shows the output of the id command displaying execution as root. The Ubuntu and Debian packages install Redis to run using the redis user. It’s true the official Ubuntu Redis Docker image runs Redis as root but it also requires authentication. Execution as root is a pretty contrived example that is unlikely to exist in the wild. It also overstates the severity of an already bad vulnerability.

While both the original PoC and the vulhub repository are incredibly helpful, at face value, they are a little misleading and could lead to confusion around this vulnerability.

Lessons Learned from the Metasploit Module

As mentioned, Redis is run under systemd when installed by the vulnerable packages. This actually ended up having a significant impact on the development of the Metasploit module. There were initially crashes using both the meterpreter stager and the gjs binary. For example, I was running a payload like this:

eval 'local os_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_os"); local os = os_l(); local f = os.execute("gjs -v"); ' 0

And gjs was crashing.

[ 1400.928540] traps: gjs[13581] trap int3 ip:7f07ff71f295 sp:7ffd10d679b0 error:0 in libglib-2.0.so.0.6400.6[7f07ff6e3000+84000]

Running strace on gjs -v yielded the following failure.

mprotect

Note the failed mprotect call. This turns out to be a direct result of how the redis.service is configured. Amongst a few of the protections it enables, you can find MemoryDenyWriteExecute=true. MemoryDenyWriteExecute “rejects … mprotect(2) or pkey_mprotect(2) system calls with PROT_EXEC set.” This feature essentially prevents usage of JIT execution engines or software like the Meterpreter stages that load programs into memory and execute them. This is probably a well-known feature to some, but the first time I came across it while writing an exploit.

Indicators of Compromise

Unfortunately, there is no Redis specific IOC. By default, Redis logs to /var/log/redis/redis-server.log but no information regarding the attack appears there. At higher verbosity, the log will include IP addresses but if your Redis is internet-facing that’s already going to be quite a large number of addresses.

Perhaps the best indicator is observing sketchy programs running under the redis user. Here is an example of Meterpreter running as redis:

albinolobster@ubuntu:~$ ps faux | grep redis
albinol+   14150  0.0  0.0   9040   720 pts/0    S+   12:26   0:00          \_ grep --color=auto redis
redis      13229  0.1  0.1  52796  5292 ?        Ssl  10:17   0:14 /usr/bin/redis-server *:6379
redis      14147  0.0  0.0   1176    56 ?        Sl   12:26   0:00 /tmp/UtCkXuyg

Other than that, defenders will need to rely on catching typical malicious post-exploitation behavior.

For a PCAP demonstrating this attack please see the Metasploit pull request.

Guidance

We highly encourage users to patch this vulnerability. If your Redis server is exposed to the internet, we also encourage you to consider if that’s strictly necessary. If it is necessary, please consider enabling some type of authentication as, at minimum, that will prevent people from the internet issuing arbitrary Redis commands on your server.