# Users can create extra ENS records at no cost

ENS ETHRegistrarController passes user-controlled resolver calldata to functionCall, letting a registrant invoke BaseRegistrar.register with controller privileges to mint a free junk ENS NFT.

- Payout: Contest reward
- Severity: medium
- Program: ENS
- Credited: wastewa
- Disclosed: 2022-12-13
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-07-ens-findings/issues/132
- Library page: https://bugbountylibrary.com/r/c4%3A2022-07-ens%3AM-13
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** ETHRegistrarController.register / _setRecords resolver functionCall
**Root cause:** register() forwards arbitrary calldata to the user-supplied resolver via functionCall, only checking the nodehash matches; the controller's elevated privileges let that calldata call BaseRegistrar.register directly.
**Steps:**
1. Build commitment with resolver=baseRegistrar.address and data=`baseRegistrar.interface.encodeFunctionData('register(uint256,address,uint)',[node, registrantAccount, 788400000])`
2. Call `controller.commit(commitment)`, advance time past minCommitmentAge
3. Call `controller.register(label, registrantAccount, REGISTRATION_TIME, secret, baseRegistrar.address, [encodedRegister], false, 0, 0, {value: BUFFERED_REGISTRATION_COST})`
4. Observe baseRegistrar.ownerOf(node) == registrantAccount, minting a second NFT for 25 years at no extra cost
**How found:** Code reading of _setRecords/functionCall: noticed the only validation is the nodehash check while the rest of the calldata is unchecked, and that the controller holds elevated privileges across ENS contracts; hypothesized arbitrary privileged calls could be smuggled through the resolver param.
**Exploitation:** Attacker registers a normal name but supplies the BaseRegistrar address as resolver and embeds a register(uint256,address,uint) call in the resolver data; the controller executes it with its own privileges, minting an extra ERC721 whose label is the 32-byte nodehash (a junk `0x123...abc.eth` name) for an arbitrary long duration without additional payment.
**Insight:** When a contract performs a low-level call/functionCall to a user-supplied address with its own privileged identity, check whether any sibling contract grants that caller special rights (e.g. registrar minting) — the nodehash guard alone doesn't constrain the selector or target.
