Open code423n4 opened 1 year ago
Valid point, I'm not sure about the severity. In my opinion, this is ultimately a user/integrator error, because someone calls this function and does not adhere to the specifications properly. We can do our best to avoid these errors, but as the warden mentions, there is not really a perfect solution. Using transferFrom
may lead to situations where the NFT is trapped within the receiver and I am not really sure if that is better. This issue here is usually recoverable (you can transfer the CID NFT to a different address that can receive NFTs, at least if the contract that currently owns it has a functionality to transfer it), whereas a trapped subprotocol NFT is not recoverable.
A configurable receiver address could potentially help, but what if that address cannot receive NFTs? Of course that would also be a user/integrator error, but if someone does not adhere to the specs properly with the current design, he also might not do with this updated design and just pass e.g. address(this)
as the NFT receiver.
Ultimately, I'm leaning towards QA because the issue is often recoverable (by transferring), caused by a user error, and not completely avoidable.
OpenCoreCH requested judge review
I agree with the sponsor in https://github.com/code-423n4/2023-01-canto-identity-findings/issues/74#issuecomment-1426194418. Downgrading to QA (Low).
berndartmueller changed the severity to QA (Quality Assurance)
berndartmueller marked the issue as grade-a
Lines of code
https://github.com/code-423n4/2023-01-canto-identity/blob/dff8e74c54471f5f3b84c217848234d474477d82/src/CidNFT.sol#L264 https://github.com/code-423n4/2023-01-canto-identity/blob/dff8e74c54471f5f3b84c217848234d474477d82/src/CidNFT.sol#L270 https://github.com/code-423n4/2023-01-canto-identity/blob/dff8e74c54471f5f3b84c217848234d474477d82/src/CidNFT.sol#L285
Vulnerability details
Impact
TLDR; contracts without
ERC721TokenReceiver
canadd
to theCidNFT
but notremove
.If an address is a contract without
onERC721Received
, they can receive NFTs using standardtransferFrom
calls. This allows them to then call theCidNFT
contract and successfullyadd
the subprotocol NFT.safeTransferFrom
is used in theadd
function, however that performs checks on the NFT recipient only - so it does not make any assertions about themsg.sender
onadd
.Now, the contract which added a subprotocol NFT can never call
remove
. When aremove
is attempted,safeTransferFrom
requires that the recipient implementsonERC721Received
if it is a contract address - without that, the transfer will revert. This may cause the subprotocol NFT to be stuck in theCidNFT
contract.If the type is
primary
then they can not change their association because this will attempt aremove
first. Forordered
andactive
, they can only add new associations.Risk: This seems to fall under the definition of high "Assets can be
stolen/lost/compromiseddirectly" since once the subprotocol NFT has been successfully added in this scenario, they may not be able to recover that asset. However this is mitigated by the fact thatremove
is callablecidNFTOwner
approved addresses as well, assuming that address is an EOA or a contract with that capability then approvals could be leveraged to allow an alternate caller toremove
on their behalf.Proof of Concept
Create a mock contract that interacts with the
cidNFT
contract:Then in
CidNFT.t.sol
add the following test, modeled aftertestAddRemoveByOwner
: (// @audit
comments are used to highlight where this new test differs from the original)Tools Used
forge test
Recommended Mitigation Steps
Don't use
safeTransferFrom
inremove
, instead use the standardtransferFrom
. In this scenario themsg.sender
was previously in possession of the NFT in order toadd
it in the first place. It may be safe to assume that they could handle having it sent right back to them - and likely a better outcome than having the NFT become trapped like it is currently. Not usingsafeTransferFrom
would also save a little gas on theremove
scenario.This is similar to the assumption made in the
mint
function -- "If a contract calls this method, he expects to get an NFT back" == if a contract calls theremove
method, they expect to get an NFT back.However this could lead to assets getting stuck in the case of a non-receivable approved caller removing subprotocol NFTs. So a safer alt may be to keep
safeTransferFrom
in theremove
function but add a param allowing the caller to specify the NFT recipient address.