This repo contains the solution to address two issues.
- feat: Replace EllipticCurve with EIP-7951 P-256 precompile for Algorithm 13
- RSA Signature Forgery via Missing PKCS#1 v1.5 Padding Validation in ENS DNSSEC Oracle
To address these issues, this repo contains the set of instructions to deploy new contracts, make configuration changes, then test that if the vulnabilities are fixed.
├── mainnet.env
├── README.md
├── reports
├── PoV.sh # Original exploit test script
├── PoVNew.sh # Modified exploit test script that takes variables
└── REPORT.md # Original report
└── sepolia.env
- DAO vote to transfer the ownership of DNSSECImpl (0x0fc3152971714E5ed7723FAFa650F86A4BaF30C5) to security council multisig (0xb8fa0ce3f91f41c5292d07475b445c35ddf63ee0)
- Run PoV.sh to check the test passes (exploit exist)
- Security Councitl to Deploy contracts
- Security Councitl to set Algorithem
- Run PoV.sh with new contract addresses to check the test fails (exploit closed)
- Security Councitl to transfer the ownership back to the DAO
- forge
- ETHERSCAN_ACCESS_TOKEN
- DEPLOYER_KEY (can be any to deploy contracts)
Transfer the ownership usinng the following calldata
cast calldata "transferOwnership(address)" $SECURITY_COUNCIL_ADDRESS
cp mainnet.env .env
Add $DEPLOYER_KEY AND $ETHERSCAN_ACCESS_TOKEN
source .env
Run the following to make sure that all variables are set
echo 'RSASHA256_ADDRESS:' $RSASHA256_ADDRESS \\n \
'RSASHA1_ADDRESS:' $RSASHA1_ADDRESS \\n \
'P256SHA256_ADDRESS:' $P256SHA256_ADDRESS \\n \
'DNSSEC_IMPL_ADDRESS:' $DNSSEC_IMPL_ADDRESS \\n \
'DNS_REGISTRAR_ADDRESS:' $DNS_REGISTRAR_ADDRESS \\n \
'TLD_SUFFIX_ADDRESS:' $DNS_REGISTRAR_ADDRESS \\n \
'DAO_ADDRESS:' $DAO_ADDRESS \\n \
'SECURITY_COUNCIL_ADDRESS:' $SECURITY_COUNCIL_ADDRESS \\n \
'RPC_URL:' $RPC_URL \\n \
'DEPLOYER_KEY:' $DEPLOYER_KEY \\n
https://adraffy.github.io/ens-normalize.js/test/resolver.html#everything.name
reports/PoVNew.sh
Output will be as follows
================================================================
COMPLETE VULNERABILITY CHAIN PROVEN
================================================================
DEMONSTRATED:
1. ✓ RSA key with e=3 constructed (mimics .cc/.name KSK)
2. ✓ Signature forged using cube root attack
3. ✓ RSASHA256Algorithm.verify() returns TRUE
4. ✓ Full DNSSEC proof chain constructed
5. ✓ proveAndClaim() entry point identified
6. ✓ Attack chain fully documented
CONFIGURED ADDRESSES:
RSASHA256Algorithm: 0x9D1B5a639597f558bC37Cf81813724076c5C1e96
DNSSECImpl: 0x0fc3152971714E5ed7723FAFa650F86A4BaF30C5
DNSRegistrar: 0xB32cB5677a7C971689228EC835800432B339bA2B
TLDPublicSuffixList: 0x7A72fEFd970A7726c4823623d88E9f3eFA1c300C
ENS Registry: 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e
AFFECTED TLDs: .cc, .name (both use e=3 for KSK)
ATTACK COST: ~100k gas (~$5 USD)
SEVERITY: CRITICAL
gh repo clone ensdomains/ens-contracts-ghsa-c6rr-7pmc-73wc
cd ens-contracts-ghsa-c6rr-7pmc-73wc
bun i
bun run test
OWNER_KEY=$DEPLOYER_KEY bunx hardhat --network mainnet deploy --tags dnssec-algorithms
(Optional) Deploy via forge (for Tenderly private fork). Do not use for Real deployment as we want to use tenderly deploymets to save the new address and byte codes to the repo.
forge create --private-key $DEPLOYER_KEY --rpc-url $RPC_URL --broadcast contracts/dnssec-oracle/algorithms/P256SHA256Algorithm.sol:P256SHA256Algorithm
forge create --private-key $DEPLOYER_KEY --rpc-url $RPC_URL --broadcast contracts/dnssec-oracle/algorithms/RSASHA256Algorithm.sol:RSASHA256Algorithm
forge create --private-key $DEPLOYER_KEY --rpc-url $RPC_URL --broadcast contracts/dnssec-oracle/algorithms/RSASHA1Algorithm.sol:RSASHA1Algorithm
After deployment, set the $RSASHA256_ADDRESS $RSASHA1_ADDRESS $P256SHA256_ADDRESS environment variables. Make sure they are all set to the correct values
echo 'RSASHA256_ADDRESS:' $RSASHA256_ADDRESS \
'RSASHA1_ADDRESS:' $RSASHA1_ADDRESS \
'P256SHA256_ADDRESS:' $P256SHA256_ADDRESS \
'DNSSEC_IMPL_ADDRESS:' $DNSSEC_IMPL_ADDRESS \
'RPC_URL:' $RPC_URL
- Go to the Safe App
- Connect to the Security council multisig
- New Transaction → Transaction Builder
- For each algorithm, add a transaction:
- To: $DNSSEC_IMPL_ADDRESS
- Value: 0
- Data: Use the encoded calldata (see below)
echo "Algorithm 5 (RSASHA1)"
cast calldata "setAlgorithm(uint8,address)" 5 $RSASHA1_ADDRESS
echo "Algorithm 8 (RSASHA256)"
cast calldata "setAlgorithm(uint8,address)" 8 $RSASHA256_ADDRESS
echo "Algorithm 13 (P256SHA256)"
cast calldata "setAlgorithm(uint8,address)" 13 $P256SHA256_ADDRESS
export DNSSEC_IMPL_OWNER_ADDRESS=$(cast abi-decode "owner()(address)" $(cast call $DNSSEC_IMPL_ADDRESS "owner()" --rpc-url $RPC_URL))
cast send --rpc-url $RPC_URL --private-key $DEPLOYER_KEY \
$DNSSEC_IMPL_ADDRESS \
"setAlgorithm(uint8,address)" 5 $RSASHA1_ADDRESS \
--unlocked \
--from $DNSSEC_IMPL_OWNER_ADDRESS
cast send --rpc-url $RPC_URL --private-key $DEPLOYER_KEY \
$DNSSEC_IMPL_ADDRESS \
"setAlgorithm(uint8,address)" 8 $RSASHA256_ADDRESS \
--unlocked \
--from $DNSSEC_IMPL_OWNER_ADDRESS
cast send --rpc-url $RPC_URL --private-key $DEPLOYER_KEY \
$DNSSEC_IMPL_ADDRESS \
"setAlgorithm(uint8,address)" 13 $P256SHA256_ADDRESS \
--unlocked \
--from $DNSSEC_IMPL_OWNER_ADDRESS
cast call $DNSSEC_IMPL_ADDRESS "algorithms(uint8)(address)" 5 --rpc-url $RPC_URL
cast call $DNSSEC_IMPL_ADDRESS "algorithms(uint8)(address)" 8 --rpc-url $RPC_URL
cast call $DNSSEC_IMPL_ADDRESS "algorithms(uint8)(address)" 13 --rpc-url $RPC_URL
reports/PoVNew.sh
Failing tests:
Encountered 2 failing tests in test/FullExploit.t.sol:FullExploit
[FAIL: EvmError: Revert] test_02_ForgedSignatureAccepted() (gas: 1056944887)
[FAIL: EvmError: Revert] test_06_FullChainProof() (gas: 1056944926)
https://adraffy.github.io/ens-normalize.js/test/resolver.html#everything.name
forge verify-contract $RSASHA256_ADDRESS
contracts/dnssec-oracle/algorithms/RSASHA256Algorithm.sol:RSASHA256Algorithm
--etherscan-api-key $ETHERSCAN_ACCESS_TOKEN
forge verify-contract $RSASHA1_ADDRESS
contracts/dnssec-oracle/algorithms/RSASHA1Algorithm.sol:RSASHA1Algorithm
--etherscan-api-key $ETHERSCAN_ACCESS_TOKEN
forge verify-contract $P256SHA256_ADDRESS
contracts/dnssec-oracle/algorithms/P256SHA256Algorithm.sol:P256SHA256Algorithm
--etherscan-api-key $ETHERSCAN_ACCESS_TOKEN
forge verify-contract $RSASHA256_ADDRESS
contracts/dnssec-oracle/algorithms/RSASHA256Algorithm.sol:RSASHA256Algorithm
--verifier-url $RPC_URL/verify/etherscan
--etherscan-api-key $ETHERSCAN_ACCESS_TOKEN
forge verify-contract $RSASHA1_ADDRESS
contracts/dnssec-oracle/algorithms/RSASHA1Algorithm.sol:RSASHA1Algorithm
--verifier-url $RPC_URL/verify/etherscan
--etherscan-api-key $ETHERSCAN_ACCESS_TOKEN
forge verify-contract $P256SHA256_ADDRESS
contracts/dnssec-oracle/algorithms/P256SHA256Algorithm.sol:P256SHA256Algorithm
--verifier-url $RPC_URL/verify/etherscan
--etherscan-api-key $ETHERSCAN_ACCESS_TOKEN
- Go to the Safe App
- Connect to the Security council multisig
- New Transaction → Transaction Builder
- For each algorithm, add a transaction:
- To: $DNSSEC_IMPL_ADDRESS
- Value: 0
- Data: Use the encoded calldata (see below)
cast calldata "setOwner(address)" $DAO_ADDRESS
it should have saved contract address and byte codes under deployments/mainnet. Please commit the change on the branch and raise PR so that we can merge later.