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: 195/283
Findings: 4
Award: $3.36
π Selected for report: 0
π Solo Findings: 0
π 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
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/GameItems.sol#L289-L303 https://github.com/OpenZeppelin/openzeppelin-contracts/blob/ecd2ca2cd7cac116f7a37d0e474bbb3d7d5e1c4d/contracts/token/ERC1155/ERC1155.sol#L134-L146
GameItems.sol is an ERC1155 contract, when creating game items admins are able to specify whether the items are transferable or not. When safeTransferFrom is called there is a check to see if the item is transferable and only then the contract transfers the item but since the contract inherits from OZ's ERC1155 contract, a user is able to circumvent this check by calling inherited safeBatchTransferFrom function.
Nontransferable items are transferable. As of now there is only one game item which is a transferable item but if nontransferable items are added or existing transferability is set to nontransferable, there will be unexpected consequences.
Add the following test to GameItems.t.sol
file after setUp()
function.
function testSafeBatchTransferFrom() public { _fundUserWith4kNeuronByTreasury(_ownerAddress); _gameItemsContract.createGameItem("testName", "testURI", false, //infinite supply false, //transferable 100, 100, 100); _gameItemsContract.mint(1, 1); vm.expectRevert(); _gameItemsContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 1, 1, ""); uint256[] memory batch = new uint256[](1); batch[0] = 1; _gameItemsContract.safeBatchTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, batch, batch, "0x0"); assertEq(_gameItemsContract.balanceOf(_DELEGATED_ADDRESS, 1), 1); assertEq(_gameItemsContract.balanceOf(_ownerAddress, 1), 0); }
Override safeBatchTransferFrom function and add the same check safeTransferFrom has.
function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public override(ERC1155) { for (uint i; i < ids.length; ++i) { require(allGameItemAttributes[ids[i]].transferable); } super.safeBatchTransferFrom(from, to, ids, amounts, data); }
Access Control
#0 - c4-pre-sort
2024-02-22T03:37:38Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T03:37:45Z
raymondfam marked the issue as duplicate of #18
#2 - c4-pre-sort
2024-02-26T00:27:28Z
raymondfam marked the issue as duplicate of #575
#3 - c4-judge
2024-03-05T04:47:39Z
HickupHH3 changed the severity to 3 (High Risk)
#4 - c4-judge
2024-03-05T04:51:27Z
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 redeeming a mint pass, so users are able to specify fighterType
and iconsType
to get rare attributes they are not entitled to.
You can see a mint pass collection on OpenSea, only a small percentage of them are dendroids or have custom icons attributes. Also in the documentation it is mentioned that dendroids are very rare and more difficult to obtain than champions.
Mint passes are ERC721 tokens which get minted to users from AAMintPass, later users can redeem mint passes in FighterFarm
to claim a fighter. The issue is that there are no rules enforced on the inputs of FighterFarm::redeemMintPass
function, so users can set fighterType
and iconsType
however they like.
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L233-L262
function redeemMintPass( uint256[] calldata mintpassIdsToBurn, uint8[] calldata fighterTypes, uint8[] calldata iconsTypes, string[] calldata mintPassDnas, string[] calldata modelHashes, string[] calldata modelTypes ) external { require( mintpassIdsToBurn.length == mintPassDnas.length && mintPassDnas.length == fighterTypes.length && fighterTypes.length == modelHashes.length && modelHashes.length == modelTypes.length ); for (uint16 i = 0; i < mintpassIdsToBurn.length; i++) { require(msg.sender == _mintpassInstance.ownerOf(mintpassIdsToBurn[i])); _mintpassInstance.burn(mintpassIdsToBurn[i]); _createNewFighter( msg.sender, uint256(keccak256(abi.encode(mintPassDnas[i]))), modelHashes[i], modelTypes[i], fighterTypes[i], iconsTypes[i], [uint256(100), uint256(100)] ); } }
Restrict access to the redeemMintPass function or implement validation mechanisms to verify the attributes selected by users, ensuring they match attributes defined on mint pass.
Other
#0 - c4-pre-sort
2024-02-22T07:58:47Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T07:59:04Z
raymondfam marked the issue as duplicate of #33
#2 - c4-pre-sort
2024-02-26T00:53:43Z
raymondfam marked the issue as duplicate of #1626
#3 - c4-judge
2024-03-05T10:56:27Z
HickupHH3 changed the severity to 3 (High Risk)
#4 - c4-judge
2024-03-06T03:34:53Z
HickupHH3 marked the issue as satisfactory
π Selected for report: t0x1c
Also found by: 0rpse, 0xAadi, 0xBinChook, 0xCiphky, 0xDetermination, 14si2o_Flint, AC000123, Aamir, Abdessamed, Blank_Space, CodeWasp, DanielArmstrong, DarkTower, Draiakoo, Honour, Kalogerone, Krace, McToady, Merulez99, MidgarAudits, MrPotatoMagic, PedroZurdo, Silvermist, Tychai0s, VAD37, Velislav4o, VrONTg, WoolCentaur, YouCrossTheLineAlfie, ZanyBonzy, alexxander, aslanbek, btk, csanuragjain, d3e4, dimulski, djxploit, erosjohn, evmboi32, fnanni, forgebyola, forkforkdog, handsomegiraffe, immeas, israeladelaja, juancito, ktg, n0kto, neocrao, ni8mare, okolicodes, peanuts, petro_1912, shaflow2, shaka, swizz, ubermensch, ubl4nk, yotov721
2.0593 USDC - $2.06
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L245 https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L519-L535 https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L439
If staked token amount is very little users do not lose their staked tokens while still being able to get points for fights they win, even though the staked amount is very little this has adverse effects because a user might lose a hundred times consecutively but there are no stakes getting put at risk, so on the first win they can bounce back as they do not have to reclaim stakes at risk. Also their staking factor will be the same with anyone who stakes NRN tokens in the range 0 < x < 4
.
stakeNRN function lets users stake any amount greater than zero. In _getStakingFactor staking factor is calculated as square root of the amount staked, also if staking factor turns out to be 0 it is adjusted to be 1. This means that a user who stakes 1 wei of NRN tokens and a user who stakes 3.999... NRN tokens both have staking factor of 1. Furthermore when results are being updated, _addResultPoints will calculate current stake at risk but due to rounding this value can be zero with dust amounts of stake, therefore no stakes will be put to risk.
Add the following test to RankedBattle.t.sol
:
function testStakeDustAmount() public { address staker = vm.addr(3); _mintFromMergingPool(staker); _fundUserWith4kNeuronByTreasury(staker); vm.prank(staker); _rankedBattleContract.stakeNRN(999, 0); //stake dust assertEq(_rankedBattleContract.amountStaked(0), 999); assertEq(_rankedBattleContract.stakingFactor(0), 1); //staking factor is 1 address claimee = vm.addr(4); _mintFromMergingPool(claimee); _fundUserWith4kNeuronByTreasury(claimee); vm.prank(claimee); _rankedBattleContract.stakeNRN(4_000 * 10 ** 18, 1); assertEq(_rankedBattleContract.amountStaked(1), 4_000 * 10 ** 18); for (uint i; i < 100; i++) { vm.prank(address(_GAME_SERVER_ADDRESS)); _rankedBattleContract.updateBattleRecord(0, 50, 2, 1500, false); //staker loses fights } assertEq(_stakeAtRiskContract.stakeAtRisk(0, 0), 0); vm.prank(address(_GAME_SERVER_ADDRESS)); _rankedBattleContract.updateBattleRecord(1, 50, 0, 1500, true); vm.prank(address(_GAME_SERVER_ADDRESS)); _rankedBattleContract.updateBattleRecord(0, 50, 0, 1500, true); //staker wins 1 fight _rankedBattleContract.setNewRound(); assertEq(_rankedBattleContract.accumulatedPointsPerAddress(staker, 0), 750); assertEq(_rankedBattleContract.accumulatedPointsPerAddress(claimee, 0), 47250); vm.prank(staker); _rankedBattleContract.claimNRN(); assertEq(_rankedBattleContract.amountClaimed(staker) > 0, true); vm.prank(claimee); _rankedBattleContract.claimNRN(); assertEq(_rankedBattleContract.amountClaimed(claimee) > 0, true); }
Minimum amount allowed to stake should not be one wei, rather it should atleast be that the user risks staked tokens and stakingFactor should be adjusted to one only if staked amount is close to one.
Other
#0 - c4-pre-sort
2024-02-22T16:01:10Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-02-22T16:01:18Z
raymondfam marked the issue as duplicate of #38
#2 - c4-judge
2024-03-07T02:58:22Z
HickupHH3 changed the severity to 3 (High Risk)
#3 - c4-judge
2024-03-07T03:15:51Z
HickupHH3 marked the issue as satisfactory
π Selected for report: klau5
Also found by: 0rpse, 0xBinChook, 0xDetermination, 0xGreyWolf, 0xLogos, 0xWallSecurity, 0xaghas, 0xgrbr, 0xkaju, 0xlyov, AlexCzm, BARW, Blank_Space, BoRonGod, Daniel526, DanielArmstrong, Draiakoo, FloatingPragma, Giorgio, Greed, Jorgect, Matue, McToady, MidgarAudits, Nyxaris, PUSH0, PedroZurdo, Pelz, PoeAudits, Silvermist, SpicyMeatball, Tekken, Tricko, Tumelo_Crypto, VAD37, WoolCentaur, Zac, alexzoid, andywer, aslanbek, bgsmallerbear, cats, d3e4, desaperh, dimulski, dutra, erosjohn, evmboi32, favelanky, fnanni, forkforkdog, gesha17, givn, grearlake, haxatron, honey-k12, iamandreiski, immeas, juancito, kaveyjoe, ke1caM, kiqo, klau5, korok, lil_eth, lsaudit, n0kto, ni8mare, niser93, pa6kuda, peanuts, peter, shaka, sl1, soliditywala, solmaxis69, t0x1c, tallo, thank_you, tpiliposian, visualbits, vnavascues, web3pwn, yotov721
0.0264 USDC - $0.03
In FighterFarm
contract users are presented with the ability to reroll their fighters' physical appearance, element and weight. The price of this action is 1000 NRN
tokens however rerolling function lacks genuine randomness and the outcome of rerolls are predictable.
This predictability allows users to potentially manipulate the system by avoiding rerolls if the anticipated outcome is unfavorable. Adverse impacts on revenue generation and user engagement.
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L379-L389 As all of the variables and the computation that contribute to rerolling are known a user is able to guess the outcome.
Implement proper randomness mechanism for rerolling, such as integrating verifiable random functions or oracles.
Other
#0 - c4-pre-sort
2024-02-23T03:44:15Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-02-23T03:44:27Z
raymondfam marked the issue as duplicate of #53
#2 - raymondfam
2024-02-23T03:44:37Z
Inadequate elaboration.
#3 - c4-judge
2024-03-06T03:46:16Z
HickupHH3 marked the issue as partial-75
#4 - c4-judge
2024-03-06T03:49:26Z
HickupHH3 changed the severity to 3 (High Risk)
#5 - c4-judge
2024-03-15T02:10:55Z
HickupHH3 changed the severity to 2 (Med Risk)
#6 - c4-judge
2024-03-20T01:04:24Z
HickupHH3 marked the issue as duplicate of #376