Open c4-bot-3 opened 1 month ago
koolexcrypto marked the issue as unsatisfactory: Insufficient quality
This report clearly demonstrates the presence of a bug.
Consider the following scenario:
NukeFund
contract as an operator.(Alice called setApprovalForAll(nukeFund, true)
.)approve(Bob's address, the entity's tokenId)
.)nuke(the entity's tokenId)
.In the scenario described above, it should be allowed for Bob to call nuke(the entity's tokenId)
.
However, Bob's call will be reverted due to the bug highlighted in the report.
nftContract.getApproved(tokenId)
returns Bob, not address(this)
. Additionally, msg.sender
at L160 is Bob who have not approved the NukeFund
contract as an operator. Consequently, the nuke()
function will be reverted unexpectedly.
So, msg.sender
at L160 should be replaced by nftContract.ownerOf(tokenId)
.
require(
nftContract.getApproved(tokenId) == address(this) ||
160: nftContract.isApprovedForAll(msg.sender, address(this)),
'Contract must be approved to transfer the NFT.'
);
This unexpected reversal also takes place when Alice approves Bob as an operator in step 3 of the scenario.
Therefore, I believe this issue is valid.
Add the following code into test/NukeFund.test.ts
:
In the PoC, Alice is the owner
and Bob is the user1
.
it('should revert to nuke a token', async function () {
const tokenId = 1;
// Mint a token
await nft.connect(owner).mintToken(merkleInfo.whitelist[0].proof, {
value: ethers.parseEther('1'),
});
// Send some funds to the contract
await user1.sendTransaction({
to: await nukeFund.getAddress(),
value: ethers.parseEther('1'),
});
const prevNukeFundBal = await nukeFund.getFundBalance();
// Ensure the token can be nuked
expect(await nukeFund.canTokenBeNuked(tokenId)).to.be.true;
const prevUserEthBalance = await ethers.provider.getBalance(
await owner.getAddress()
);
// await nft.connect(owner).approve(await nukeFund.getAddress(), tokenId);
await nft.connect(owner).approve(user1, tokenId);
await nft.connect(owner).setApprovalForAll(await nukeFund.getAddress(), true);
const finalNukeFactor = await nukeFund.calculateNukeFactor(tokenId);
const fund = await nukeFund.getFundBalance();
// await expect(nukeFund.connect(owner).nuke(tokenId))
await expect(nukeFund.connect(user1).nuke(tokenId)).to.be.revertedWith("Contract must be approved to transfer the NFT.");
});
Agree with @KupiaSecAdmin's PJQA comment. To add to what's already stated, my report from the validation repo also explains how nuking is currently broken for approved personnels due to the wrong check and should be duplicated to this: https://github.com/code-423n4/2024-07-traitforge-validation/issues/304
To summarize this, Alice approves Bob, Bob calls nuke
but it reverts because it expect Bob to approve the contract which shouldn't be a requirement.
I believe this is a valid issue due to the broken functionality.
would be appreciated, If you have a dup of this, please add it on your own issue, not here.
koolexcrypto removed the grade
koolexcrypto marked the issue as primary issue
koolexcrypto marked the issue as satisfactory
@c4-judge +1 on this and I believe its worth looking into. My report also holds a coded POC that shows the same behaviour.
which one is your report ?
koolexcrypto marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/NukeFund/NukeFund.sol#L153-L182
Vulnerability details
Impact
Approved users can't nuke the owner's NFT.
Proof of Concept
In the
NukeFund.nuke
function, it checks whethernftContract.isApprovedForAll(msg.sender, address(this))
is true from L160 and tries to burn the NFT token oftokenID
from L176.https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/NukeFund/NukeFund.sol#L153-L182
In the
TraitForgeNft.burn
function, it checks whetherisApprovedOrOwner(msg.sender, tokenId)
is true from L143.https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/TraitForgeNft/TraitForgeNft.sol#L141-L151
msg.sender
at L143 isNukeFund
contract. In theERC721._isApprovedOrOwner
, it checks as following:Totally, nuking tokens requires
nftContract.isApprovedOrOwner(msg.sender, tokenId) && (nftContract.getApproved(tokenId) == address(this) ||nftContract.isApprovedForAll(msg.sender, address(this)))
wherethis
isNukeFund
contract. And burning tokens requiresisApprovedOrOwner(msg.sender, tokenId)
wheremsg.sender
isNukeFund
contract. CheckingisApprovedOrOwner(msg.sender, tokenId)
is same asmsg.sender == owner || getApproved(tokenId) == msg.sender || isApprovedForAll(owner, msg.sender)
. So,nuke
function should checknftContract.isApprovedForAll(nftContract.ownerOf(tokenId), address(this))
instead ofnftContract.isApprovedForAll(msg.sender, address(this))
Tools Used
Manual review
Recommended Mitigation Steps
It is recommended to change the code as following:
Assessed type
Access Control