It is possible to create fake ERC1155 NameWrapper token for subdomain, which is not owned by NameWrapper #84
Labels
3 (High Risk)
Assets can be stolen/lost/compromised directly
bug
Something isn't working
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
Lines of code
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/NameWrapper.sol#L820-L821
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/NameWrapper.sol#L524
https://github.com/code-423n4/2022-07-ens/blob/ff6e59b9415d0ead7daf31c2ed06e86d9061ae22/contracts/wrapper/NameWrapper.sol#L572
Vulnerability details
Impact
Due to re-entrancy possibility in
NameWrapper._transferAndBurnFuses
(called fromsetSubnodeOwner
andsetSubnodeRecord
), it is possible to do some stuff inonERC1155Received
right after transfer but before new owner and new fuses are set. This makes it possible, for example, to unwrap the subdomain, but owner and fuses will still be set even for unwrapped domain, creating fakeERC1155
NameWrapper
token for domain, which is not owned byNameWrapper
.Fake token creation scenario:
Account1
registers and wrapstest.eth
domainAccount1
callsNameWrapper.setSubnodeOwner
forsub.test.eth
subdomain withAccount1
as owner (to make NameWrapper owner of subdomain)Contract1
smart contract is created, which calls unwrap in itsonERC1155Received
function, and a function to sendsub.test.eth
ERC1155 NameWrapper token back toAccount1
Account1
callsNameWrapper.setSubnodeOwner
forsub.test.eth
withContract1
as new owner, which unwraps domain back toAccount1
but due to re-entrancy, NameWrapper sets fuses and ownership toContract1
Account1
calls function to send ERC1155 token fromContract1
back to self.After this sequence of events,
sub.test.eth
subdomain is owned byAccount1
both inENS
registry and inNameWrapper
(with fuses and expiry correctly set to the future date). Lots (but not all) of functions inNameWrapper
will fail to execute for this subdomain, because they expectNameWrapper
to have ownership of the domain inENS
, but some functions will still work, making it possible to make the impression of good domain.At this point, ownership in
NameWrapper
is "detached" from ownership inENS
andAccount1
can do all kinds of malcious stuff with its ERC1155 token. For example:Sell subdomain to the other user, transfering
ERC1155
to that user and burningPARENT_CANNOT_CONTROL
to create impression that he can't control the domain. After receiving the payment,Account1
can wrap the domain again, which burns existing ownership record and replaces with the new one with clear fuses andAccount1
ownership, effectively stealing domain back from unsuspecting user, who thought thatERC1155
gives him the right to the domain (and didn't expect that parent can clear fuses whenPARENT_CANNOT_CONTROL
is set).Transfer subdomain to some other smart contract, which implements
onERC1155Received
, then take it back, fooling smart contract into believing that it has received the domain.Proof of Concept
Copy these to test/wrapper and run:
yarn test test/wrapper/NameWrapperReentrancy.js
https://gist.github.com/panprog/3cd94e3fbb0c52410a4c6609e55b863e
Recommended Mitigation Steps
Consider adding
nonReentrant
modifiers withReentrancyGuard
implementation fromopenzeppelin
. Alternatively just fix this individual re-entrancy issue. There are multiple ways to fix it depending on expected behaviour, for example savingERC1155
data and requiring it to match the data after transfer (restrictingonERC1155Received
to not change any data for the token received):The text was updated successfully, but these errors were encountered: