sortf)此仓库包含一个本地 Docker 实验室,用于重现和验证 CVE-2026-42647,这是一个影响 WordPress 插件 JoomSport - for Sports: Team & League, Football, Hockey & more 的未经验证 SQL 注入漏洞。
漏洞行为出现在球员列表排序功能中。公开访客可以控制 sortf 查询参数,该参数用于构建 SQL ORDER BY 子句。在受影响版本中,该值被作为文本进行清理并用反引号包裹,但在追加到 SQL 查询之前并未经过严格的允许列表验证。
本实验室比较了两个 JoomSport 版本:
| 服务 | JoomSport 版本 | 用途 | URL |
|---|---|---|---|
vuln | 5.7.6 | 易受攻击的比较目标 | http://localhost:8081 |
patched | 5.7.8 | 已修补的比较目标 | http://localhost:8082 |
公共公告将 5.7.8 之前的版本标识为受影响版本,5.7.8 为修复版本。本实验室使用 5.7.6 作为易受攻击的目标,因为在准备此实验室时,WordPress.org 插件 SVN 标签列表中未提供 5.7.7 的源代码标签。
演示的漏洞链是:```text Unauthenticated visitor → JoomSport season player list route → attacker-controlled sortf parameter → unsafe dynamic ORDER BY construction → SQL expression execution → measurable database delay in vulnerable version → patched version rejects the injected sort field and falls back to a safe allowlisted field
本实验将该漏洞验证为基于时间的盲SQL注入。它不执行数据库导出、凭据提取、数据修改或破坏性SQL操作。
本实验仅用于受控的本地研究、源码级理解及作品集演示。
## Verified Facts
| Claim | Evidence | How to verify in this lab |
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| JoomSport before 5.7.8 is reported vulnerable to unauthenticated SQL injection. | Public advisories identify JoomSport `< 5.7.8` / `<= 5.7.7` as affected. | Review the References section and compare the vulnerable/patched services. |
| JoomSport 5.7.8 is the fixed version. | Public advisories and source comparison show that 5.7.8 validates the `sortf` value before building the ordering expression. | Inspect `class-jsport-playerlist.php` in both versions. |
| The affected parameter is `sortf`. | The vulnerable player list code reads `classJsportRequest::get('sortf')`. | Run the PoC and observe the injected `sortf` request. |
| The vulnerable code builds a dynamic SQL ordering value from user input. | In the vulnerable version, `sortf` is used to build `$options['ordering']`. | Inspect `sportleague/classes/objects/class-jsport-playerlist.php`. |
| The SQL sink is an `ORDER BY` clause. | The generated `$ordering` value is later appended into an SQL query with `ORDER BY`. | Inspect `sportleague/base/wordpress/classes/class-jsport-getplayers.php`. |
| The patch uses an allowlist-style fix. | The patched version introduces allowed static columns and expected dynamic field patterns before using the sort field. | Compare JoomSport 5.7.6 and 5.7.8 source. |
| The lab demonstrates time-based blind SQL injection. | The vulnerable target delays when an injected `SLEEP()` expression is used; the patched target does not. | Run `python3 poc/poc.py http://localhost:8081 http://localhost:8082`. |
## Assumptions and Unknowns
本实验使用 JoomSport 5.7.6 作为存在漏洞的对比目标,因为公开的修复版本是 5.7.8,而在准备实验时,WordPress.org 插件 SVN 标签列表中并不存在 5.7.7 的源码标签。
本实验并不声称 5.7.6 是唯一存在漏洞的版本。它被用作一个可复现的漏洞基线,用于对比存在漏洞的行为与已修复的 5.7.8 行为。
本实验聚焦于球员列表排序流程中的 `sortf` 参数。
演示的影响为基于时间的盲SQL注入。本实验不演示:
* 直接数据库导出,
* 凭据提取,
* 认证绕过,
* 权限提升,
* 任意数据修改,
* 远程代码执行,
* 持久化,
* 外部回调,
* 或针对非实验系统的攻击。
基于错误或基于布尔的行为可能取决于数据库行为、应用程序配置和响应差异,但本实验不依赖这些技术。主要证据基于时间。
## Root Cause Summary
根本原因是从 `sortf` 请求参数不安全地构建动态 SQL `ORDER BY` 子句。
存在漏洞的代码路径始于:```text
sportleague/classes/objects/class-jsport-playerlist.php
在球员列表加载逻辑内部,JoomSport 读取请求参数:```text sortf
并使用它来构建:```text
$options['ordering']
相关的易受攻击的源模式是:```php
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;
}
问题主要不在于 `sortd` 参数。`sortd` 的值被限制为:```text
ASC
DESC
问题在于 sortf 参数,因为它控制用于排序的 SQL 标识符/表达式位置。
危险的表达式是:```php
"".classJsportRequest::get('sortf').""
该代码将攻击者控制的输入放入MySQL标识符上下文中,然后将其作为SQL排序片段传递。该代码应用:```php
sanitize_text_field()
sanitize_text_field() 并非用于 SQL 标识符验证。它专为清理文本而设计,而非安全构建 SQL 语法。
这段易受攻击的代码还将用户控制的排序字段包裹在反引号中。然而,当攻击者能够影响标识符内容时,反引号并非安全边界。若攻击者能在值中注入反引号,便可脱离预期的标识符上下文。
生成的排序值随后被传入玩家检索查询,并附加到 SQL ORDER BY 子句中:```text
sportleague/base/wordpress/classes/class-jsport-getplayers.php
接收器模式是:```php
$query .= ' ORDER BY '.($ordering);
这创建了易受攻击的数据流:```text sortf request parameter → classJsportRequest::get('sortf') → $options['ordering'] → $ordering → ORDER BY
安全问题是,应用程序将用户可控的请求参数作为 SQL 标识符/表达式处理,而未先通过严格的允许列表对其进行验证。
## 源码补丁摘要
相关补丁位于:```text
sportleague/classes/objects/class-jsport-playerlist.php
在存在漏洞的版本中,玩家列表代码直接从请求值构建 $options['ordering']:```php
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;
}
漏洞部分在于,`classJsportRequest::get('sortf')` 被用于 SQL 排序表达式中。
JoomSport 5.7.8 通过引入一个经过验证的排序字段变量,在构建 `$options['ordering']` 之前改变了这一行为。
补丁版本初始化了一个安全的默认值:```php
$sortFieldEsc = 'post_title';
然后它定义了允许的静态排序列:```php $sortCols = array("played", "career_minutes", "post_title");
当 `sortf` 存在时,修补后的代码仅在其与预期的静态值之一匹配时才接受它:```php
if (in_array(classJsportRequest::get('sortf'), $sortCols)) {
$sortFieldEsc = classJsportRequest::get('sortf');
}
该补丁还允许预期的动态事件/统计字段格式:```php if (preg_match('/^eventid_\d+$/', classJsportRequest::get('sortf'))) { $sortFieldEsc = classJsportRequest::get('sortf'); }
if (preg_match('/^ef_\d+$/', classJsportRequest::get('sortf'))) { $sortFieldEsc = classJsportRequest::get('sortf'); }
最后一项与安全相关的更改是,`$options['ordering']` 由 `$sortFieldEsc` 构建,而不是来自原始的 `sortf` 请求值:```diff
- $options['ordering'] = str_replace(" ","",sanitize_text_field("`".classJsportRequest::get('sortf')."`")).' '.$typeAD;
+ $options['ordering'] = str_replace(" ","",sanitize_text_field("`".$sortFieldEsc."`")).' '.$typeAD;
这并不移除动态排序。它改变了信任边界。
补丁之前:```text request sortf value directly controlled the ORDER BY identifier
补丁之后:```text
request sortf value can only influence ORDER BY if it matches an allowed column name or an expected dynamic field pattern
如果攻击者发送了意外的值,例如:```text post_title`DESC,(SLEEP(2))#
修补后的代码不会将该值赋给 `$sortFieldEsc`。相反,排序字段会回退到:```text
post_title
这就是为什么有漏洞的服务会延迟,而修复后的服务保持在基准时间附近。
这个补丁的安全教训是:```text Dynamic SQL identifiers such as ORDER BY columns must be validated with strict allowlists. Text sanitization and backtick wrapping are not sufficient for SQL identifier safety.
## 实验室架构
实验室通过Docker Compose运行两个隔离的WordPress安装。```text
.
├── docker-compose.yml
├── vuln/
│ └── Dockerfile
├── patched/
│ └── Dockerfile
├── scripts/
│ └── init-wordpress.sh
├── poc/
│ └── poc.py
├── README.md
└── .gitignore
两个 WordPress 服务分别运行独立的数据库和独立的插件版本: