CVE-2026-52763 is a medium-severity (CVSS 6.5) SQL Injection vulnerability in yeswiki/yeswiki. A fix is available for yeswiki/yeswiki — see the affected versions and patch details below.
YesWiki: SQL injection via the `recentchanges` action `period` argument leading to arbitrary DB read
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
CVE-2026-52763 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 395,249 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
yeswiki/yeswikiReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
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.if (isset($_GET['period']) && in_array($_GET['period'], ['day', 'week', 'month'])) { switch ($_GET['period']) { case 'day': $d = strtotime('-1 day'); $dateMin = date('Y-m-d H:i:s', $d); break; case 'week': $d = strtotime('-1 week'); $dateMin = date('Y-m-d H:i:s', $d); break; case 'month': $d = strtotime('-1 month'); $dateMin = date('Y-m-d H:i:s', $d); break; } } else { $dateMin = $this->GetParameter('period'); }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.
public function getRecentlyChanged($limit = 50, $minDate = ''): ?array
{
if (!empty($minDate)) {
if ($pages = $this->dbService->loadAll(
'select id, tag, time, user, owner from' . $this->dbService->prefixTable('pages')
. "where latest = 'Y' and comment_on = '' and time >= '$minDate' order by time desc"
)) {
return $pages;
}
}
}
$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
{{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
GET /?<TriggerPage> HTTP/1.1
Host: target.example
The 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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | yeswiki/yeswiki | all versions | 4.6.6composer require yeswiki/yeswiki:^4.6.6 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for yeswiki/yeswiki, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update yeswiki/yeswiki to 4.6.6 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-52763 is resolved across your whole dependency graph.
Workarounds
Until you can upgrade, make sure every query built from user input uses parameterised statements or a prepared-statement API rather than string concatenation, and reduce the database account's privileges so an injected query cannot read or alter data beyond what the feature needs.
Frequently Asked Questions
Is CVE-2026-52763 in your dependencies?
Find it across Packagist, including transitive dependencies.