Nick from ENS has just announced that ENS received a report of a critical bug that "would have been catastrophic if deployed".

Hey, it's not that one of us didn't say in advance that we were about to take a look.


There's an often overlooked feature of ENS: the possibility to integrate DNS names. Here's a user-friendly guide explaining it 👇

Integrate a DNS name | ENS Support
Apart from using ENS names like snowowl.eth, ENS also allows you to integrate existing DNS domain names with ENS

Leveraging some features of DNSSEC records, the smart contracts of ENS are able take a set of signed records, verify them, do some magic, and allow you to claim a DNS name on ENS.

So cool! Bringing together the good-old DNS stuff into ENS. We were curious to understand how this integration was even possible in the first place. Who doesn't like figuring out the magician's tricks ?

The contracts inside the dnsregistrar folder of the ENS repository seemed a good place to start. Soon we spotted one of the possible entry-points for users to claim a DNS name on ENS:

https://github.com/ensdomains/ens-contracts/blob/v0.0.19/contracts/dnsregistrar/DNSRegistrar.sol#L85-L99

Whoever calls proveAndClaim, they must specify the name to claim and a piece signed data (the DNSSEC records).

Undoubtedly, the magic must happen before the owner is set on the ENS registry. So we continued at the internal call to _claim.

https://github.com/ensdomains/ens-contracts/blob/v0.0.19/contracts/dnsregistrar/DNSRegistrar.sol#L133-L137

The signed data is passed to an oracle, which should verify the input isn't forged. Assuming the oracle works ok, then an attacker shouldn't be able pass a fraudulent set of DNSSEC records. One less possible attack vector.

But it's not sufficient for the records to be legitimate. Their actual contents are also key to ensure the DNS-ENS integration is done right. According to the docs:

The DNS Registrar on ENS looks for a TXT record with a specific name and format in order to verify what Ethereum address should be given ownership of the domain. To claim ownership of mydomain.xyz, create a TXT record in your DNS zone, _ens.mydomain.xyz, with text data of the form a=0x1234..., where 0x1234... is the Ethereum address you want to give control of the ENS record to.

Somehow, somewhere, the _claim function must check this. Look, here it is!

https://github.com/ensdomains/ens-contracts/blob/v0.0.19/contracts/dnsregistrar/DNSRegistrar.sol#L157-L161

You know where this is going, right? Down into the getOwnerAddress function of the DNSClaimChecker library.

The first few lines start building a local buffer, containing the name that is to be found iterating through all given records. But oooh look, the buffer is left unused.

Meaning that there's nothing validating that the name being claimed actually corresponds to the given set of signed records. What's preventing anyone from claiming any name with any set of legitimate, but unrelated, DNSSEC records?

This is when we freaked out. This was ENS we were talking about. And this smell bad, really bad.

Honestly, we weren't that familiar with the rest of ENS yet. It wasn't straightforward to tell how this would affect the whole system. On top of it, we didn't know whether this code was released, or worse, deployed to mainnet.

If you've been in our shoes, no need to tell you about the mix excitement and fear that comes right afterwards of finding something (potentially) significant.

The next few hours were a rush of confirming these weren't the mainnet version of the contracts, building quick local tests (Foundry we love you), reviewing the rest of the related contracts in the repository to determine impact, and crafting the final test using ENS's test suite to fully confirm the problem. Which was... ?

Anyone with a valid proof for a legitimate DNS record could claim whatever DNS name registered on ENS. The claimed name should not necessarily be associated with the proof. And it'd be possible to do so for names already claimed by other users. So yes, anyone could have taken over a DNSSEC name owned by any other user on ENS.

Almost. Because luckily, the vulnerability was not in mainnet. And no users were affected by it.

Still we quickly reached out to the ENS team because the vulnerability was present in the latest tagged version of the repository. As well as published in the npm package of the contracts. The vulnerable code was part of ENS's release candidate, which was to be put forward for a vote shortly.

After a few days of anxious waiting, we heard back from ENS, confirming our report. And now we can tell you about it.

For more details:

  • See the full description of the issue we sent to ENS here.
  • Check out the announcement in ENS's forums and Twitter.
  • Read more about the work of The Red Guild here

If you ever find a potential security vulnerability in ENS, you must follow the responsible disclosure process outlined in their bug bounty program.