CVE-2026-52773: Yeswiki/yeswiki Cross-Site Scripting Vulnerability

MODERATE
Advisory Identifier: GHSA-35f3-pg38-486f
CVSS 6.1
CWE-80
Published
Advisory Summary

CVE-2026-52773 is a MODERATE (CVSS 6.1) cross-site scripting vulnerability (CWE-80: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)) affecting Yeswiki/yeswiki libraries in the Packagist ecosystem. The issue involves yeswiki vulnerable to reflected xss via unescaped archived-revision `time` parameter in `handlers/page/show.php`. Remediation is available by upgrading affected packages to version 4.6.6.

CVE-2026-52773 at a Glance

CVE Identifier CVE-2026-52773
GHSA Identifier GHSA-35f3-pg38-486f
Severity MODERATE
CVSS v3.1 Base Score 6.1
CWE Classification CWE-80: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)
Affected Product Yeswiki/yeswiki
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 6.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2026-52773

To remediate this vulnerability, upgrade all affected Yeswiki/yeswiki dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

yeswiki/yeswiki fixed: 4.6.6
Upgrade yeswiki/yeswiki to 4.6.6
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for yeswiki/yeswiki

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-52773, inspect your project dependencies for Yeswiki/yeswiki:

inspect dependencies for yeswiki/yeswiki

Analyze the terminal output to locate any version of Yeswiki/yeswiki in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.6.6), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-80: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS), where the product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes special characters such as "<", ">", and "&" that could be interpreted as web-scripting elements when they are sent to a downstream component that processes web pages. In Yeswiki/yeswiki, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary YesWiki's archived-revision view reflects the `time` `GET` parameter into a hidden HTML input in `handlers/page/show.php` without escaping. Because MySQL coerces malformed `DATETIME` strings, an attacker can append HTML or JavaScript to a valid archived revision timestamp, still load that archived revision, and execute arbitrary JavaScript in the victim's browser. The vulnerable form is only rendered when the victim can both read and edit the target page. In restricted deployments this requires a victim with `read` and `write` access to that page. On a default `doryphore 4.6.5` install, public pages such as `PagePrincipale` were editable anonymously during validation, so the issue can also affect unauthenticated visitors in that configuration. ### Details The request routing path uses the user-controlled `time` parameter to load a specific page revision. In `includes/YesWiki.php` around `Run()` line `1223`, the request is routed through: ```php $this->SetPage($this->LoadPage($tag, isset($_REQUEST['time']) ? $_REQUEST['time'] : '')); ``` `LoadPage()` delegates to `PageManager::getOne()` in `includes/services/PageManager.php` around line `75`, which builds a SQL predicate directly from the supplied revision time: ```php $timeQuery = $time ? "time = '{$this->dbService->escape($time)}'" : "latest = 'Y'"; ``` If the loaded page is an archived revision (`latest == 'N'`) and the current user has `write` access, `handlers/page/show.php` around lines `43-49` renders an edit form for that archived revision and copies `$_GET['time']` into a hidden input without `htmlspecialchars()`: ```php $time = isset($_GET['time']) ? $_GET['time'] : ''; echo $this->FormOpen(testUrlInIframe() ? 'editiframe' : 'edit', '', 'get'); <input type="hidden" name="time" value="<?php echo $time; ?>" /> ``` That sink is reachable only when all of the following are true: 1. The target page has at least one archived revision. 2. The victim can `read` the target page. 3. The victim can `write` the target page, because the archived revision edit form is rendered only inside the `if ($this->HasAccess('write'))` branch. In practice, the payload must begin with a real archived revision timestamp. A completely invalid `time` value does not reach the archived branch because YesWiki only updates the current page object when the revision lookup returns a non-empty row. During local validation on the official `doryphore 4.6.5` package, the exploit worked because MySQL accepted a malformed timestamp string as matching an existing archived revision row. For example, the following expression was coerced to the stored revision time: ```sql CAST(CONCAT('2026-05-24 04:30:00', CHAR(34), CHAR(62), CHAR(60), 'script', CHAR(62), 'alert(1)', CHAR(60), '/script', CHAR(62)) AS DATETIME) ``` and the corresponding query predicate still matched the archived row: ```sql WHERE time = '2026-05-24 04:30:00"><script>alert(1)</script>' ``` This means a payload can begin with a valid archived revision timestamp, still resolve to the archived revision, and then be reflected unescaped into the hidden HTML field. For comparison, `tools/bazar/handlers/page/show__.php` around lines `13-14` escapes the same `time` value with `htmlspecialchars()`, which shows that the core handler's behavior is inconsistent and unsafe. This issue maps to **CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')**. ### PoC 1. Set up a vulnerable YesWiki instance. This was validated locally on the official `doryphore 4.6.5` release. 2. Use a target page that has at least one archived revision. Any page with revision history is sufficient. 3. Confirm the victim has the rights needed to reach the vulnerable sink: - The victim must have `read` access to the page. - The victim must have `write` access to the page. - On the default validation install, these rights were available anonymously on public pages such as `PagePrincipale`, so no login was required in that configuration. 4. Identify the timestamp of an archived revision. In the validated setup, an archived `PagePrincipale` revision existed at `2026-05-24 04:30:00`. 5. Send the victim a crafted URL that starts with that valid archived revision timestamp and then appends an attribute-breaking payload: ```text http://127.0.0.1:8085/PagePrincipale?time=2026-05-24%2004:30:00%22%3E%3Cscript%3Ealert(1)%3C/script%3E%3Cinput%20value=%22 ``` 6. Open the URL in a browser as a victim who has the required `read` and `write` rights. 7. Observe that YesWiki still loads the archived revision view and displays the archived-revision warning block, proving the malformed `time` value matched the stored archived revision. 8. Inspect the returned HTML. The response contains the injected payload inside the hidden form field: ```html <input type="hidden" name="time" value="2026-05-24 04:30:00"><script>alert(1)</script><input value="" /> ``` 9. The browser executes the injected JavaScript in the YesWiki origin. A simple payload such as `alert(1)` demonstrates code execution; a real payload could read browser-accessible data or perform actions in the victim's session. <img width="1600" height="829" alt="image" src="https://github.com/user-attachments/assets/d0b85a7c-2213-4459-908c-969934f06358" /> ### Impact This is a **reflected XSS** vulnerability in the archived-revision workflow. The practical access model is: - The attacker only needs to send a crafted link. - The victim must have `read` and `write` access to the target page. - The target page must have at least one archived revision. - On deployments where anonymous visitors can edit public pages, the issue can be exploited against unauthenticated visitors as well. An attacker may be able to: - Execute arbitrary JavaScript in the victim's browser. - Steal browser-accessible sensitive data. - Perform actions as the victim inside YesWiki. - Abuse the trusted YesWiki origin for phishing, UI redressing, or follow-on attacks.

CVE-2026-52773 Affected Yeswiki/yeswiki Versions

1 component(s)
Fixed Versions:
4.6.6
Product / Package Ecosystem Affected Versions Fixed Version
yeswiki/yeswiki
Packagist
≥ 4.1.0
< 4.6.6
4.6.6

References

Frequently Asked Questions

What is CVE-2026-52773?
CVE-2026-52773 is a security vulnerability in Yeswiki/yeswiki involving YesWiki Vulnerable to Reflected XSS via Unescaped Archived-Revision `time` Parameter in `handlers/page/show.php` under CWE-80: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS).
Which Yeswiki/yeswiki versions are affected?
Affected versions include releases prior to 4.6.6 across affected packages: yeswiki/yeswiki.
What version fixes CVE-2026-52773?
Upgrading to 4.6.6 (or the latest patched release for your respective branch) resolves the vulnerability.
Is yeswiki/yeswiki affected?
Yes, yeswiki/yeswiki is affected in releases prior to 4.6.6. Upgrade your dependency to 4.6.6 or later.
What is the CVSS score of CVE-2026-52773?
CVE-2026-52773 has a CVSS v3.1 base score of 6.1 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N.
What CWE is associated with CVE-2026-52773?
CVE-2026-52773 is classified under CWE-80: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS) (The product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes special characters such as "<", ">", and "&" that could be interpreted as web-scripting elements when they are sent to a downstream component that processes web pages.).
How can I check Packagist dependencies for CVE-2026-52773?
Run the dependency tree command: 'inspect dependencies for yeswiki/yeswiki'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-35f3-pg38-486f",
  "modified" : "2026-07-09T21:00:55Z",
  "published" : "2026-07-09T21:00:55Z",
  "aliases" : [ "CVE-2026-52773" ],
  "summary" : "YesWiki Vulnerable to Reflected XSS via Unescaped Archived-Revision `time` Parameter in `handlers/page/show.php`",
  "details" : "### Summary\nYesWiki's archived-revision view reflects the `time` `GET` parameter into a hidden HTML input in `handlers/page/show.php` without escaping. Because MySQL coerces malformed `DATETIME` strings, an attacker can append HTML or JavaScript to a valid archived revision timestamp, still load that archived revision, and execute arbitrary JavaScript in the victim's browser.\n\nThe vulnerable form is only rendered when the victim can both read and edit the target page. In restricted deployments this requires a victim with `read` and `write` access to that page. On a default `doryphore 4.6.5` install, public pages such as `PagePrincipale` were editable anonymously during validation, so the issue can also affect unauthenticated visitors in that configuration.\n\n### Details\nThe request routing path uses the user-controlled `time` parameter to load a specific page revision. In `includes/YesWiki.php` around `Run()` line `1223`, the request is routed through:\n\n```php\n$this->SetPage($this->LoadPage($tag, isset($_REQUEST['time']) ? $_REQUEST['time'] : ''));\n```\n\n`LoadPage()` delegates to `PageManager::getOne()` in `includes/services/PageManager.php` around line `75`, which builds a SQL predicate directly from the supplied revision time:\n\n```php\n$timeQuery = $time ? \"time = '{$this->dbService->escape($time)}'\" : \"latest = 'Y'\";\n```\n\nIf the loaded page is an archived revision (`latest == 'N'`) and the current user has `write` access, `handlers/page/show.php` around lines `43-49` renders an edit form for that archived revision and copies `$_GET['time']` into a hidden input without `htmlspecialchars()`:\n\n```php\n$time = isset($_GET['time']) ? $_GET['time'] : '';\necho $this->FormOpen(testUrlInIframe() ? 'editiframe' : 'edit', '', 'get');\n<input type=\"hidden\" name=\"time\" value=\"<?php echo $time; ?>\" />\n```\n\nThat sink is reachable only when all of the following are true:\n\n1. The target page has at least one archived revision.\n2. The victim can `read` the target page.\n3. The victim can `write` the target page, because the archived revision edit form is rendered only inside the `if ($this->HasAccess('write'))` branch.\n\nIn practice, the payload must begin with a real archived revision timestamp. A completely invalid `time` value does not reach the archived branch because YesWiki only updates the current page object when the revision lookup returns a non-empty row.\n\nDuring local validation on the official `doryphore 4.6.5` package, the exploit worked because MySQL accepted a malformed timestamp string as matching an existing archived revision row. For example, the following expression was coerced to the stored revision time:\n\n```sql\nCAST(CONCAT('2026-05-24 04:30:00', CHAR(34), CHAR(62), CHAR(60), 'script', CHAR(62), 'alert(1)', CHAR(60), '/script', CHAR(62)) AS DATETIME)\n```\n\nand the corresponding query predicate still matched the archived row:\n\n```sql\nWHERE time = '2026-05-24 04:30:00\"><script>alert(1)</script>'\n```\n\nThis means a payload can begin with a valid archived revision timestamp, still resolve to the archived revision, and then be reflected unescaped into the hidden HTML field.\n\nFor comparison, `tools/bazar/handlers/page/show__.php` around lines `13-14` escapes the same `time` value with `htmlspecialchars()`, which shows that the core handler's behavior is inconsistent and unsafe.\n\nThis issue maps to **CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')**.\n\n### PoC\n1. Set up a vulnerable YesWiki instance. This was validated locally on the official `doryphore 4.6.5` release.\n2. Use a target page that has at least one archived revision. Any page with revision history is sufficient.\n3. Confirm the victim has the rights needed to reach the vulnerable sink:\n   - The victim must have `read` access to the page.\n   - The victim must have `write` access to the page.\n   - On the default validation install, these rights were available anonymously on public pages such as `PagePrincipale`, so no login was required in that configuration.\n4. Identify the timestamp of an archived revision. In the validated setup, an archived `PagePrincipale` revision existed at `2026-05-24 04:30:00`.\n5. Send the victim a crafted URL that starts with that valid archived revision timestamp and then appends an attribute-breaking payload:\n\n```text\nhttp://127.0.0.1:8085/PagePrincipale?time=2026-05-24%2004:30:00%22%3E%3Cscript%3Ealert(1)%3C/script%3E%3Cinput%20value=%22\n```\n\n6. Open the URL in a browser as a victim who has the required `read` and `write` rights.\n7. Observe that YesWiki still loads the archived revision view and displays the archived-revision warning block, proving the malformed `time` value matched the stored archived revision.\n8. Inspect the returned HTML. The response contains the injected payload inside the hidden form field:\n\n```html\n<input type=\"hidden\" name=\"time\" value=\"2026-05-24 04:30:00\"><script>alert(1)</script><input value=\"\" />\n```\n\n9. The browser executes the injected JavaScript in the YesWiki origin. A simple payload such as `alert(1)` demonstrates code execution; a real payload could read browser-accessible data or perform actions in the victim's session.\n\n<img width=\"1600\" height=\"829\" alt=\"image\" src=\"https://github.com/user-attachments/assets/d0b85a7c-2213-4459-908c-969934f06358\" />\n\n### Impact\nThis is a **reflected XSS** vulnerability in the archived-revision workflow.\n\nThe practical access model is:\n\n- The attacker only needs to send a crafted link.\n- The victim must have `read` and `write` access to the target page.\n- The target page must have at least one archived revision.\n- On deployments where anonymous visitors can edit public pages, the issue can be exploited against unauthenticated visitors as well.\n\nAn attacker may be able to:\n\n- Execute arbitrary JavaScript in the victim's browser.\n- Steal browser-accessible sensitive data.\n- Perform actions as the victim inside YesWiki.\n- Abuse the trusted YesWiki origin for phishing, UI redressing, or follow-on attacks.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "yeswiki/yeswiki"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "4.1.0"
      }, {
        "fixed" : "4.6.6"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/YesWiki/yeswiki/security/advisories/GHSA-35f3-pg38-486f"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/YesWiki/yeswiki/commit/35ad9c2bb6cd338198b37c1f745e24bc302a3560"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/YesWiki/yeswiki"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-80" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-09T21:00:55Z",
    "nvd_published_at" : null
  }
}