Feb 25, 2026·6 min read·5 visits
The ENS DNSSEC oracle failed to verify RSA PKCS#1 v1.5 padding, checking only the hash suffix. This allowed attackers to use basic math (cube roots) to forge signatures for TLDs using $e=3$, enabling the theft of domains like `target.cc` on the Ethereum network.
A critical cryptographic oversight in the Ethereum Name Service (ENS) DNSSEC oracle allowed attackers to bypass RSA signature verification. By exploiting a lazy validation implementation in `RSASHA256Algorithm` and `RSASHA1Algorithm` contracts, adversaries could forge valid-looking signatures for specific Top-Level Domains (TLDs) like `.cc` and `.name` that utilize a low public exponent ($e=3$). This flaw essentially permitted the hijacking of ENS domains rooted in these TLDs without possessing the actual DNS private keys.
The Ethereum Name Service (ENS) is the phonebook of the decentralized web, turning arcane hexadecimal addresses into human-readable names like vitalik.eth. But ENS isn't just an island; it builds bridges to the legacy DNS world. If you own example.com, you can claim example.com on ENS. To do this securely, ENS relies on a DNSSEC Oracle—a set of smart contracts that verify the chain of trust from the root DNS keys down to your domain.
This mechanism sounds robust on paper. It uses RSA cryptography, the bedrock of internet security. However, cryptography is brittle. It's not enough to use the right algorithms; you have to implement them with paranoid precision. In CVE-2026-22866, the developers made a classic mistake: they assumed that if the math works out at the end of the equation, the input must be valid.
This vulnerability is a resurrection of a ghost from 2006—Daniel Bleichenbacher's RSA signature forgery. It’s a stark reminder that in the unforgiving world of smart contracts, skipping a few bytes of validation to save gas can result in a total collapse of trust for affected domains.
To understand the exploit, we have to look at how RSA PKCS#1 v1.5 signatures work. When a signer creates a signature, they don't just encrypt the hash of the message. They format a specific block of data:
0x00 0x01 [Padding of 0xFFs] 0x00 [ASN.1 OID] [Hash]
This structure prevents mathematical malleability. A verifier computes $M = S^e \pmod N$ (where $S$ is the signature, $e$ is the public exponent, and $N$ is the modulus). The verifier MUST check that the resulting $M$ matches the strict format above, byte for byte.
Here is where the ENS contracts failed. The implementation of RSASHA256Algorithm and RSASHA1Algorithm performed the modular exponentiation, but then it got lazy. It looked at the result and effectively asked, "Does the last 20 bytes match the hash of the DNS record?" If yes, it returned true.
This is the cryptographic equivalent of authenticating a painting by only looking at the signature in the bottom corner. An attacker doesn't need to paint a masterpiece; they can draw a stick figure (garbage data), as long as they glue a convincing signature (the correct hash) at the very end. When the public exponent is small—like $e=3$, which is used by .cc and .name TLDs—finding a number that cubes to a value ending in a specific suffix is trivial.
Let's look at the vulnerable logic. In the unpatched versions of RSASHA1Algorithm.sol, the verification logic was dangerously concise. It delegated the heavy lifting of RSA recovery but fumbled the check.
function verify(bytes calldata key, bytes calldata data, bytes calldata sig) external view returns (bool) {
// ... key parsing ...
(bool ok, bytes memory result) = RSAVerify.rsarecover(modulus, exponent, sig);
// CRITICAL FLAW: Ignores everything before the last 20 bytes
return ok && SHA1.sha1(data) == result.readBytes20(result.length - 20);
}As you can see, readBytes20(result.length - 20) extracts only the tail of the decrypted message. The padding, the structure, the ASN.1 Object Identifier—all ignored.
The patch, introduced in commit c76c5ad0dc9de1c966443bd946fafc6351f87587, rips out this naive check and replaces it with a rigorous validation library, RSAPKCS1Verify. The new logic explicitly walks through the decrypted byte array:
0x00 0x01.0xFF.0x00 byte after the padding.This forces the attacker to generate a signature that, when cubed, matches the entire block, which requires breaking RSA itself.
So, how does a hacker exploit this? We need to forge a signature $S$ for a malicious DNS record (claiming we own google.cc, for example). The target TLD .cc uses a public key with exponent $e=3$.
Since the contract only checks the suffix, we need to find an integer $S$ such that: $S^3 \equiv \text{Garbage} \ || \ \text{TargetHash} \pmod N$
If we can make $S^3$ end with the TargetHash, the contract accepts it. Because we don't care about the "Garbage" part (the higher-order bytes), we aren't constrained by the modulus $N$ in the same way a legitimate signer is. We can treat this as finding a cube root in modular arithmetic over a power of 2, specifically $2^{hash_bits}$.
Using Hensel lifting, we can iteratively construct the root bit by bit (or byte by byte). Here is the conceptual attack flow:
H = SHA256(MaliciousDNSData).The oracle computes $x^3$, sees the result ends in $H$, ignores the fact that the rest of the result is mathematical noise, and hands over the domain.
While the CVSS score sits at a seemingly low 2.7 (likely due to the high complexity of setting up the DNSSEC environment and the limitation to specific TLDs), the functional impact is severe for the affected zones.
An attacker successfully exploiting this could claim ownership of any domain under TLDs that use $e=3$ for their Key Signing Keys (KSK) or Zone Signing Keys (ZSK). Known vulnerable TLDs include .cc and .name.
Imagine an attacker claiming exchange.cc on ENS and setting the resolution address to a wallet they control. Users trusting ENS to resolve that domain would be sending funds directly to the attacker. It undermines the core promise of ENS: that it is a secure, trustless naming system. The vulnerability allows for a complete bypass of the DNS ownership proof for these specific zones.
For developers and integrators running their own ENS infrastructure or forks, the path is clear: Upgrade immediately.
The fix is available in ens-contracts versions following 1.6.2. If you are utilizing the RSASHA256Algorithm or RSASHA1Algorithm contracts directly:
RSAPKCS1Verify.sol.DNSSECImpl configuration to point to these new algorithm verifiers via setAlgorithm.This vulnerability highlights a broader lesson for Solidity developers: Never roll your own crypto verification logic without strictly adhering to the RFCs. The "happy path" is not enough. You must validate the entire structure of cryptographic primitives, or the math will find a way to bite you.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U| Product | Affected Versions | Fixed Version |
|---|---|---|
ens-contracts ENS Domains | <= 1.6.2 | 1.6.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-347 |
| CVSS Score | 2.7 (Low) |
| Attack Vector | Network |
| Key Factor | RSA Public Exponent e=3 |
| Exploit Status | PoC Available |
| Impact | Domain Hijacking / Signature Forgery |
The product verifies a cryptographic signature but does not verify or incorrectly verifies that the signature is valid.