Feb 25, 2026·6 min read·31 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.
A state persistence vulnerability exists in Tornado's CurlAsyncHTTPClient component where pooled pycurl.Curl handles are reused across asynchronous requests without a complete state reset. Consequently, sensitive per-request configurations, such as client TLS certificates or proxy basic authentication credentials, persist on the shared handle. This behavior leads to subsequent requests leaking these credentials to unauthorized remote servers.
CVE-2026-48748 is a denial-of-service vulnerability in Netty's HTTP/3 codec (netty-codec-http3) occurring when QPACK dynamic tables are enabled but the blocked streams limit is not explicitly configured. A bug in limit checking and a memory leak in stream tracking allow unauthenticated remote attackers to exhaust the JVM heap memory and crash the server.
CVE-2026-50009 is a cryptographic design vulnerability in the Netty network application framework. Prior to version 4.2.15.Final, the framework's QUIC protocol implementation fails to cryptographically segregate the generated Connection IDs and the associated Stateless Reset Tokens. An on-path network attacker who sniffs traffic during a Connection ID rotation can extract secret token material from cleartext headers, enabling them to inject spoofed reset packets and terminate active connections.
A critical hostname verification bypass vulnerability exists in the Netty network application framework when configured as a TLS client. When a developer registers a custom plain X509TrustManager, Netty wraps it inside an X509TrustManagerWrapper to adapt it to the X509ExtendedTrustManager API. However, this wrapper discards the SSLEngine context, bypassing critical hostname checks. Because the wrapper is identified as an X509ExtendedTrustManager, standard cryptographic engines and Netty's OpenSSL wrappers do not re-wrap it, failing to execute any hostname validation. Consequently, clients silently accept certificates for any host, enabling unauthenticated Man-in-the-Middle (MitM) attacks.
An uncontrolled resource pre-allocation flaw in the Netty Redis codec module allows remote unauthenticated attackers to cause a denial of service (OutOfMemoryError) by sending a crafted Redis Serialization Protocol (RESP) array header.
CVE-2026-50020 is a medium-severity HTTP Request Smuggling/Response Smuggling vulnerability (CWE-444) within the Netty asynchronous network application framework. The flaw resides in Netty's HTTP codec implementation, specifically the HttpObjectDecoder class, which silently consumes arbitrary ISO control bytes preceding the first request line.