Platform: Code4rena
Start Date: 14/04/2023
Pot Size: $90,500 USDC
Total HM: 7
Participants: 59
Period: 14 days
Judge: LSDan
Total Solo HM: 3
Id: 232
League: ETH
Rank: 10/59
Findings: 1
Award: $1,667.31
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: SaharAP
Also found by: Lilyjjo, chaduke, nobody2018
1667.3135 USDC - $1,667.31
Currently users can create malformed DNSSEC _ens
records which are erroneously accepted as valid in the ENS system. This is bad because users could experience loss of funds if they fail to notice that their ENS was not set to their intended address.
DNSClaimChecker.sol’s function getOwnerAddress()
is used to determine which address a DNSSEC's _ens
record points to. Currently this function fails to properly validate that an address is of the correct length and will accept “address” strings which are longer than 20 bytes. Problematically, the function will take the last 20 bytes of the input as the address.
For example, let a user fat-finger their address when creating an _ens
record and add two extra characters at the end of the address string:
// note, the hex string is 22 bytes long and isn't a valid address a=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb9226666
The address parsed during DNSClaimChecker.sol’s function getOwnerAddress()
for this record will be 0x9FD6E51aaD88F6F4Ce6aB8827279CffFb9226666
, which is the last 20 bytes of the _ens
record, which is definitely not what the user intended.
The issue is that DNSClaimChecker.sol’s parseString()
function fails to ensure that the string is logically restricted to the proper length (and is linked to in the provided code link).
diff --git a/test/dnsregistrar/TestDNSRegistrar.js b/test/dnsregistrar/TestDNSRegistrar_modified.js index 365d936..4df433a 100644 --- a/test/dnsregistrar/TestDNSRegistrar.js +++ b/test/dnsregistrar/TestDNSRegistrar_modified.js @@ -103,6 +103,56 @@ contract('DNSRegistrar', function (accounts) { assert.equal(await ens.owner(namehash.hash('foo.test')), accounts[0]) }) + it.only('ENS records with too long address accepted as valid', async function () { + assert.equal(await registrar.oracle(), dnssec.address) + assert.equal(await registrar.ens(), ens.address) + + const testRrsetExtraChars = (name, account) => ({ + name, + sig: { + name: 'test', + type: 'RRSIG', + ttl: 0, + class: 'IN', + flush: false, + data: { + typeCovered: 'TXT', + algorithm: 253, + labels: name.split('.').length + 1, + originalTTL: 3600, + expiration, + inception, + keyTag: 1278, + signersName: '.', + signature: new Buffer([]), + }, + }, + rrs: [ + { + name: `_ens.${name}`, + type: 'TXT', + class: 'IN', + ttl: 3600, + data: Buffer.from(`a=${account}6666`, 'ascii'), // note extra chars were added + }, + ], + }) + + // construct _ens record with too many chars + const proof = [ + hexEncodeSignedSet(rootKeys(expiration, inception)), + hexEncodeSignedSet(testRrsetExtraChars('gooo.test', accounts[0])), + ] + + // This should fail to run!! + await registrar.proveAndClaim(utils.hexEncodeName('gooo.test'), proof, { + from: accounts[1], + }) + + // This does fail + assert.equal(await ens.owner(namehash.hash('gooo.test')), accounts[0]) + }) + it('allows claims on names that are not TLDs', async function () { const proof = [ hexEncodeSignedSet(rootKeys(expiration, inception)),
#0 - c4-pre-sort
2023-05-01T14:35:20Z
thereksfour marked the issue as duplicate of #198
#1 - c4-judge
2023-05-08T13:07:01Z
dmvt marked the issue as satisfactory