Feb 8, 2026·6 min read·20 visits
Nebula versions 1.7.0 through 1.10.2 using non-default P256 settings allow blocklist evasion. Because ECDSA signatures are malleable, an attacker can modify a blocked certificate's signature to generate a different SHA-256 fingerprint while maintaining validity. This bypasses blocklists that rely on static file hashes.
In the world of cryptography, two things can be mathematically identical yet look completely different to a computer. CVE-2026-25793 is a fascinating logic flaw in Slack's Nebula overlay network that exploits the malleability of ECDSA signatures on the NIST P-256 curve. By flipping the 's' value of a cryptographic signature, an attacker can generate a new binary representation of a certificate that remains cryptographically valid but results in a completely different file hash. Since Nebula's blocklist mechanism relied solely on SHA-256 fingerprints of the raw certificate bytes, this allowed banned attackers to simply 'flip a switch' and re-enter the network.
Nebula is one of the darling children of the mesh VPN world. It’s fast, it’s secure, and it usually defaults to modern, safe crypto like Curve25519. But, enterprise environments often have rigid compliance requirements that force them to use NIST standards, specifically the P-256 curve. If you were one of the unlucky admins forced to configure Nebula to use P-256, you were sitting on a ticking time bomb of cryptographic trivia.
The core promise of a blocklist is simple: "I don't like this certificate. I will calculate its fingerprint (hash), put it in a blocklist.yml file, and never speak to it again." It's the digital equivalent of a bouncer with a photo of a troublemaker taped to the door.
But what happens if the troublemaker is a shapeshifter? CVE-2026-25793 isn't a buffer overflow or a command injection. It's a failure to understand that in the weird world of Elliptic Curve Digital Signature Algorithm (ECDSA), a single identity can wear two different faces. You banned the face, but the math let them swap it out.
To understand this exploit, we have to endure a tiny bit of math. An ECDSA signature is composed of two integers: $(r, s)$. When you verify a signature, the math checks that these numbers correspond to the signer's public key and the message hash. However, elliptic curves over finite fields have a property called malleability.
For any valid signature $(r, s)$, there exists a second valid signature: $(r, n - s)$, where $n$ is the order of the curve. Think of it like a clock. If the time is 10:00, that's effectively the same position as -2:00 relative to 12:00. Both $s$ and its negative (modulo the curve order) are valid proofs of identity.
The vulnerability lies in how Nebula implemented its blocklist. Nebula calculated the "fingerprint" of a certificate by taking the SHA-256 hash of the entire serialized DER-encoded certificate. Since the signature bytes are part of that file, if you change $s$ to $n - s$, the binary content of the file changes. Consequently, the SHA-256 hash changes completely.
So, an attacker with a blocked certificate evil.crt (Fingerprint A) can simply do some math to generate evil_v2.crt (Fingerprint B). Nebula sees Fingerprint B, checks the blocklist, sees only Fingerprint A is banned, and rolls out the red carpet. The cryptographic signature is still valid, so the connection is established.
The fix, applied in commit f573e8a26695278f9d71587390fbfe0d0933aa21, is a lesson in defensive coding. The developers couldn't just change how fingerprints are calculated globally without breaking backward compatibility for every existing deployment. Instead, they had to make the verifier smarter.
First, they implemented a function to calculate the "alternate" fingerprint. This function parses the certificate, extracts the signature, calculates the inverted $s$ value, and re-serializes the certificate to see what its evil twin would look like:
// cert/p256/p256.go snippet from the patch
func swap(r, s []byte) ([]byte, []byte, error) {
// ... setup big integers ...
sNormalized := nMod.Nat().Sub(bigS, nMod) // Calculate n - s
return r, sNormalized.Bytes(nMod), nil
}Then, in the verification logic (cert/ca_pool.go), they check both possibilities. If the incoming certificate isn't on the blocklist, they calculate its doppelgänger and check if that is on the blocklist:
// The patch logic in VerifyCertificate
if ncp.IsBlocklisted(fp) {
return nil, ErrBlockListed
}
// Check the alternate fingerprint for P256 malleability
fp2, err := CalculateAlternateFingerprint(c)
if fp2 != "" && ncp.IsBlocklisted(fp2) {
return nil, ErrBlockListed
}They essentially said: "We see you're wearing a blue shirt. Let's check if we banned you wearing a red shirt."
Let's walk through how a researcher (or attacker) would actually pull this off. Assume you have a valid certificate key pair that has been revoked by the administrator. You cannot get a new cert signed by the CA, so you must reuse your existing one.
Step 1: The Setup
You try to connect. The Nebula lighthouse rejects you: handshake failed: certificate is blocklisted.
Step 2: The Transformation
You don't need the CA's private key to do this; you just need your own certificate. You write a script (likely in Python using cryptography or Go) that:
Step 3: The Re-entry
You save this as bypass.crt. The SHA-256 hash is now completely different. You configure your Nebula node to use bypass.crt. When you initiate the handshake, the Nebula CA validates the signature (which is mathematically sound) and checks the blocklist. Since the admin only banned the hash of original.crt, bypass.crt slides right through.
> [!NOTE] > This only works because the underlying identity (the public key) is effectively immutable, but the container (the certificate bytes) is mutable via the signature.
The severity of this vulnerability (CVSS 7.6) is tempered only by the fact that P-256 is not the default configuration for Nebula. Most users are on Curve25519, which uses EdDSA. EdDSA signatures are deterministic and generally not malleable in this specific way (or at least, libraries are stricter about it).
However, for environments that do use P-256, the impact is high. A blocklist is the primary mechanism for revoking trust in a compromised node without rotating the entire CA. If a blocklist can be bypassed, revocation is broken. An attacker with a stolen laptop or a compromised server can persist in the network indefinitely, even after the security team thinks they have cut off access.
This is a classic "Zombie" vulnerability—you kill the threat, but it rises from the grave with a slightly different look.
To mitigate this, you must upgrade to Nebula v1.10.3. The patch does two things: it catches the malleable signatures during verification (as shown above), and it enforces "Low-S" normalization for all new signatures generated by Nebula.
"Low-S" normalization means the software enforces a rule: if $s > n/2$, replace it with $n - s$. This forces a canonical representation of the signature, effectively killing the doppelgänger before it's born. If you are stuck on an older version, your only real mitigation is to rotate your CA authorities or migrate away from P-256 to Curve25519 (Ed25519), which is the superior choice for modern infrastructure anyway.
If migration isn't an option, ensure your monitoring alerts on successful connections from nodes that were recently attempted to be blocked. If you see a node ID connecting with a new hash immediately after a ban, you're witnessing the math in action.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Nebula SlackHQ | >= 1.7.0, <= 1.10.2 | 1.10.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-347 |
| Attack Vector | Network |
| CVSS v4.0 | 7.6 (High) |
| Impact | Security Bypass / Revocation Failure |
| EPSS Score | 0.00017 (Low) |
| Configuration | Requires non-default P256 curve |
Improper Verification of Cryptographic Signature