
JoomSport WordPress 플러그인의 sortf 매개변수를 통한 인증되지 않은 시간 기반 블라인드 SQL 인젝션인 CVE-2026-42647을 재현하기 위한 Docker 기반 랩. 비교를 위해 취약한 대상과 패치된 대상을 포함합니다.
sortf를 통한 인증되지 않은 시간 기반 블라인드 SQL 인젝션이 저장소는 WordPress 플러그인 JoomSport(스포츠용: 팀 & 리그, 축구, 하키 등)에 영향을 미치는 인증되지 않은 SQL 인젝션 취약점인 CVE-2026-42647을 재현하고 검증하기 위한 로컬 Docker 랩을 포함합니다.
취약한 동작은 선수 목록 정렬 기능에서 발생합니다. 일반 방문자는 SQL ORDER BY 절을 구성하는 데 사용되는 sortf 쿼리 매개변수를 제어할 수 있습니다. 취약한 버전에서는 값이 텍스트로 정리되고 백틱으로 감싸지지만, SQL 쿼리에 추가되기 전에 엄격한 허용 목록에 대해 검증되지 않습니다.
이 랩은 두 가지 JoomSport 버전을 비교합니다:
| Service | JoomSport version | Purpose | 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 작업을 수행하지 않습니다.
이 랩은 통제된 로컬 연구, 소스 수준 이해 및 포트폴리오 시연 목적으로만 설계되었습니다.
## 검증된 사실
| 주장 | 증거 | 이 랩에서 검증하는 방법 |
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| JoomSport 5.7.8 이전 버전은 인증되지 않은 SQL 인젝션에 취약한 것으로 보고되었습니다. | 공개 권고는 JoomSport `< 5.7.8` / `<= 5.7.7`이 영향을 받는 것으로 식별합니다. | 참고 자료 섹션을 검토하고 취약/패치된 서비스를 비교합니다. |
| JoomSport 5.7.8은 패치된 버전입니다. | 공개 권고 및 소스 비교에 따르면 5.7.8은 정렬 표현식을 구성하기 전에 `sortf` 값을 검증합니다. | 두 버전 모두에서 `class-jsport-playerlist.php`를 검사합니다. |
| 영향을 받는 매개변수는 `sortf`입니다. | 취약한 선수 목록 코드는 `classJsportRequest::get('sortf')`를 읽습니다. | PoC를 실행하고 주입된 `sortf` 요청을 관찰합니다. |
| 취약한 코드는 사용자 입력으로 동적 SQL 정렬 값을 구성합니다. | 취약한 버전에서 `sortf`는 `$options['ordering']`을 구성하는 데 사용됩니다. | `sportleague/classes/objects/class-jsport-playerlist.php`를 검사합니다. |
| SQL 싱크는 `ORDER BY` 절입니다. | 생성된 `$ordering` 값은 나중에 `ORDER BY`와 함께 SQL 쿼리에 추가됩니다. | `sportleague/base/wordpress/classes/class-jsport-getplayers.php`를 검사합니다. |
| 패치는 허용 목록 방식의 수정을 사용합니다. | 패치된 버전은 정렬 필드를 사용하기 전에 허용된 정적 열과 예상 동적 필드 패턴을 도입합니다. | JoomSport 5.7.6과 5.7.8 소스를 비교합니다. |
| 이 랩은 시간 기반 블라인드 SQL 인젝션을 시연합니다. | 취약한 대상은 주입된 `SLEEP()` 표현식이 사용될 때 지연되고, 패치된 대상은 지연되지 않습니다. | `python3 poc/poc.py http://localhost:8081 http://localhost:8082`를 실행합니다. |
## 가정 및 미확인 사항
이 랩은 JoomSport 5.7.6을 취약한 비교 대상으로 사용합니다. 공개된 패치 버전이 5.7.8이고, 랩을 준비할 당시 WordPress.org 플러그인 SVN 태그 목록에 5.7.7 소스 태그가 없었기 때문입니다.
이 랩은 5.7.6이 유일한 취약 버전이라고 주장하지 않습니다. 5.7.6은 취약한 동작을 패치된 5.7.8 동작과 비교하기 위한 재현 가능한 취약 기준선으로 사용됩니다.
이 랩은 선수 목록 정렬 흐름의 `sortf` 매개변수에 초점을 맞춥니다.
시연된 영향은 시간 기반 블라인드 SQL 인젝션입니다. 이 랩은 다음을 시연하지 않습니다:
* 직접적인 데이터베이스 덤프,
* 자격 증명 추출,
* 인증 우회,
* 권한 상승,
* 임의 데이터 수정,
* 원격 코드 실행,
* 지속성,
* 외부 콜백,
* 또는 랩 외부 시스템에 대한 공격.
데이터베이스 동작, 애플리케이션 구성 및 응답 차이에 따라 오류 기반 또는 부울 기반 동작이 가능할 수도 있지만, 이 랩은 이러한 기법에 의존하지 않습니다. 주요 증명은 시간 기반입니다.
## 근본 원인 요약
근본 원인은 `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 정렬(order) 조각으로 전달합니다.
코드는 다음을 적용합니다:```php
sanitize_text_field()
하지만 sanitize_text_field()는 SQL 식별자 검증이 아닙니다. 텍스트 정리를 위해 설계된 것이지, 안전하게 SQL 구문을 구성하기 위한 것이 아닙니다.
취약한 코드는 또한 사용자가 제어하는 정렬 필드를 백틱으로 감쌉니다. 그러나 공격자가 식별자 내용에 영향을 미칠 수 있는 경우 백틱은 보안 경계가 아닙니다. 공격자가 값에 백틱을 주입할 수 있다면, 의도된 식별자 컨텍스트를 벗어날 수 있습니다.
생성된 정렬 값은 나중에 플레이어 검색 쿼리로 전달되어 SQL ORDER BY 절에 추가됩니다:```text
sportleague/base/wordpress/classes/class-jsport-getplayers.php
sink 패턴은 다음과 같습니다:```php
$query .= ' ORDER BY '.($ordering);
이것은 취약한 데이터 흐름을 생성합니다:```text sortf request parameter → classJsportRequest::get('sortf') → $options['ordering'] → $ordering → ORDER BY
보안 문제는 애플리케이션이 사용자 제어 요청 매개변수를 엄격한 허용 목록(allowlist)에 대해 먼저 검증하지 않고 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;
}
취약한 부분은 SQL 정렬 표현식 내에서 `classJsportRequest::get('sortf')`가 사용된다는 점입니다.
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']`가 원시 `sortf` 요청 값 대신 `$sortFieldEsc`에서 빌드된다는 것입니다:```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.
## 랩 아키텍처