eef-cve-2026-88255
CVSS 6.3 osv_hex## Summary Improper Validation of Unsafe Equivalence in Input in ZenHive mpp allows an unauthenticated remote client to pass the Tempo duplicate-submission gate twice with one signed transaction. `MPP.Methods.Tempo` reserves the pre-broadcast dedup slot on the caller-supplied hex in `reserve_hash_atomic/2`, keyed through `store_key/1` on `tx.raw` rather than on a canonical form of the transaction. The deserializer stores the caller's hex verbatim and accepts both recovery-id encodings, so one signed transaction submitted once with `v=27` and once with `v=0` yields two distinct reserve keys, and both pass the reserve and reach the broadcast path. The plug-level credential replay store is deliberately carved out for `tempo` in `lib/mpp/replay.ex`, leaving this reserve as the only gate, and the post-broadcast mark writes the canonical hash key that the raw-keyed reserve never reads. What the duplicate submission yields depends on the node: a nonce-reuse rejection fails closed, while a node that answers with the canonical hash for an already-known transaction returns a second valid `Payment-Receipt` for a single on-chain payment. This issue affects mpp: from 0.2.0 before 0.16.2. ## Impact A client re-encodes the recovery id of a transaction it has already submitted and presents it again. The duplicate passes the only duplicate-submission gate the Tempo method has, and against a node that answers with the canonical hash for an already-known transaction the client is issued a second `Payment-Receipt` for one on-chain payment. ## Configurations Reachable when `MPP.Methods.Tempo` broadcasts a `type="transaction"` credential with a dedup store in place. From mpp 0.7.0 the dedup store is on by default (the app-started `MPP.Tempo.ConCacheStore`; opt out with `store: false`), so every default Tempo deployment from 0.7.0 before 0.16.2 is affected. Before 0.7.0 the default store was `nil`, which runs the library statelessly and provides no application-level replay protection at all, a documented property rather than part of this issue; those versions are affected only where a `"store"` was configured explicitly in `method_config`.
- Published
- unknown
- Last Modified
- unknown
CVSS details not available.
No product information available.
- https://github.com/ZenHive/mpp/security/advisories/GHSA-8x7x-5j8g-8hcx
- https://cna.erlef.org/cves/CVE-2026-88255.html
- https://github.com/ZenHive/mpp/commit/f8904666061fbab695874856d8fcd02c471dfe1b
- https://github.com/ZenHive/mpp/commit/e12bd4a1cea2e97c2a01fc059c48e5594a7b4a43
- https://hex.pm/packages/mpp
No linked vulnerabilities found.
{
"affected": [
{
"database_specific": {
"source": "https://cna.erlef.org/osv/EEF-CVE-2026-88255.json"
},
"package": {
"ecosystem": "Hex",
"name": "mpp",
"purl": "pkg:hex/mpp"
},
"ranges": [
{
"events": [
{
"introduced": "0.2.0"
},
{
"fixed": "0.16.2"
}
],
"type": "SEMVER"
}
],
"versions": [
"0.10.0",
"0.11.0",
"0.12.0",
"0.13.0",
"0.14.0",
"0.15.0",
"0.16.0",
"0.16.1",
"0.2.0",
"0.3.0",
"0.3.1",
"0.3.2",
"0.4.0",
"0.6.0",
"0.6.1",
"0.6.2",
"0.6.3",
"0.6.4",
"0.7.0",
"0.8.0",
"0.9.0"
]
},
{
"database_specific": {
"source": "https://cna.erlef.org/osv/EEF-CVE-2026-88255.json"
},
"ranges": [
{
"events": [
{
"introduced": "f8904666061fbab695874856d8fcd02c471dfe1b"
},
{
"fixed": "e12bd4a1cea2e97c2a01fc059c48e5594a7b4a43"
}
],
"repo": "https://github.com/ZenHive/mpp",
"type": "GIT"
}
],
"versions": [
"v0.16.1",
"v0.16.0",
"v0.15.0",
"v0.14.0",
"v0.13.0",
"v0.12.0",
"v0.11.0",
"v0.10.0",
"v0.9.0",
"v0.8.0",
"v0.7.0",
"v0.6.4",
"v0.6.3",
"v0.6.1",
"v0.6.0",
"v0.5.1",
"v0.4.0",
"v0.3.0",
"v0.2.0"
]
}
],
"aliases": [
"CVE-2026-88255",
"GHSA-8x7x-5j8g-8hcx"
],
"credits": [
{
"name": "E.FU",
"type": "FINDER"
},
{
"name": "E.FU",
"type": "REMEDIATION_DEVELOPER"
},
{
"name": "Jonatan Männchen / EEF",
"type": "COORDINATOR"
}
],
"database_specific": {
"capec_ids": [
"CAPEC-267"
],
"cpe_ids": [
"cpe:2.3:a:ZenHive:mpp:*:*:*:*:*:*:*:*"
],
"cwe_ids": [
"CWE-1289"
]
},
"details": "## Summary\n\nImproper Validation of Unsafe Equivalence in Input in ZenHive mpp allows an unauthenticated remote client to pass the Tempo duplicate-submission gate twice with one signed transaction.\n\n`MPP.Methods.Tempo` reserves the pre-broadcast dedup slot on the caller-supplied hex in `reserve_hash_atomic/2`, keyed through `store_key/1` on `tx.raw` rather than on a canonical form of the transaction. The deserializer stores the caller's hex verbatim and accepts both recovery-id encodings, so one signed transaction submitted once with `v=27` and once with `v=0` yields two distinct reserve keys, and both pass the reserve and reach the broadcast path. The plug-level credential replay store is deliberately carved out for `tempo` in `lib/mpp/replay.ex`, leaving this reserve as the only gate, and the post-broadcast mark writes the canonical hash key that the raw-keyed reserve never reads.\n\nWhat the duplicate submission yields depends on the node: a nonce-reuse rejection fails closed, while a node that answers with the canonical hash for an already-known transaction returns a second valid `Payment-Receipt` for a single on-chain payment.\n\nThis issue affects mpp: from 0.2.0 before 0.16.2.\n\n## Impact\n\nA client re-encodes the recovery id of a transaction it has already submitted and presents it again. The duplicate passes the only duplicate-submission gate the Tempo method has, and against a node that answers with the canonical hash for an already-known transaction the client is issued a second `Payment-Receipt` for one on-chain payment.\n\n## Configurations\n\nReachable when `MPP.Methods.Tempo` broadcasts a `type=\"transaction\"` credential with a dedup store in place. From mpp 0.7.0 the dedup store is on by default (the app-started `MPP.Tempo.ConCacheStore`; opt out with `store: false`), so every default Tempo deployment from 0.7.0 before 0.16.2 is affected. Before 0.7.0 the default store was `nil`, which runs the library statelessly and provides no application-level replay protection at all, a documented property rather than part of this issue; those versions are affected only where a `\"store\"` was configured explicitly in `method_config`.",
"id": "EEF-CVE-2026-88255",
"modified": "2026-09-16T08:40:58.847428916Z",
"published": "2026-09-16T08:24:40.446Z",
"references": [
{
"type": "ADVISORY",
"url": "https://github.com/ZenHive/mpp/security/advisories/GHSA-8x7x-5j8g-8hcx"
},
{
"type": "WEB",
"url": "https://cna.erlef.org/cves/CVE-2026-88255.html"
},
{
"type": "WEB",
"url": "https://github.com/ZenHive/mpp/commit/f8904666061fbab695874856d8fcd02c471dfe1b"
},
{
"type": "FIX",
"url": "https://github.com/ZenHive/mpp/commit/e12bd4a1cea2e97c2a01fc059c48e5594a7b4a43"
},
{
"type": "PACKAGE",
"url": "https://hex.pm/packages/mpp"
}
],
"schema_version": "1.9.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "mpp Tempo keys its pre-broadcast dedup reserve on the caller-supplied transaction encoding, so a re-encoded signed transaction reserves a second slot"
}