AI Arena - Archime's results

In AI Arena you train an AI character to battle in a platform fighting game. Imagine a cross between Pokémon and Super Smash Bros, but the characters are AIs, and you can train them to learn almost any skill in preparation for battle.

General Information

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

AI Arena

Findings Distribution

Researcher Performance

Rank: 226/283

Findings: 1

Award: $1.27

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

1.2667 USDC - $1.27

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
edited-by-warden
:robot:_86_group
duplicate-366

External Links

Lines of code

https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L224-L262

Vulnerability details

Impact

The redeemMintPass function of the FighterFarm.sol contract does not verify whether the input parameters are legal. Attackers can generate NFTs with arbitrary attributes.

Proof of Concept

1、In the redeemMintPass function of the FighterFarm.sol contract, the visibility is set to external https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L224-L262 function redeemMintPass( uint256[] calldata mintpassIdsToBurn, uint8[] calldata fighterTypes, uint8[] calldata iconsTypes, string[] calldata mintPassDnas, string[] calldata modelHashes, string[] calldata modelTypes ) external

2、According to the function description "Each input array must correspond to the same index", but the function only checks whether the parameter array lengths are consistent and checks the mintpassIds owner https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L243-L250 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]));

3、When calling the createNewFighter function, directly introduce the input parameters to generate a new Fighter https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L252-L259 _createNewFighter( msg.sender, uint256(keccak256(abi.encode(mintPassDnas[i]))), modelHashes[i], modelTypes[i], fighterTypes[i], iconsTypes[i], [uint256(100), uint256(100)] );

4、According to the documentation, the weight attribute is a major component。 https://docs.aiarena.io/gaming-competition/nft-makeup “The relative composition of the metal alloy determines a fighter’s weight in the game. A fighter’s weight is the primary determinant of its other relative strength and weaknesses (i.e. all other battle attributes are a function of weight). ” According to the function call relationship: _createNewFighter -> _createFighterBase, the attacker can generate the required element, weight, newDna and other variables, and further call the function _aiArenaHelperInstance.createPhysicalAttributes to generate the required attributes. The attacker can try any number of times until satisfied。 https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L476-L531 function _createNewFighter( address to, uint256 dna, string memory modelHash, string memory modelType, uint8 fighterType, uint8 iconsType, uint256[2] memory customAttributes ) private {
require(balanceOf(to) < MAX_FIGHTERS_ALLOWED); uint256 element; uint256 weight; uint256 newDna; if (customAttributes[0] == 100) { (element, weight, newDna) = _createFighterBase(dna, fighterType); } else { element = customAttributes[0]; weight = customAttributes[1]; newDna = dna; } uint256 newId = fighters.length; bool dendroidBool = fighterType == 1; FighterOps.FighterPhysicalAttributes memory attrs = _aiArenaHelperInstance.createPhysicalAttributes( newDna, generation[fighterType], iconsType, dendroidBool ); fighters.push( FighterOps.Fighter( weight, element, attrs, newId, modelHash, modelType, generation[fighterType], iconsType, dendroidBool ) ); _safeMint(to, newId); FighterOps.fighterCreatedEmitter(newId, weight, element, generation[fighterType]); }

Tools Used

Manual、Foundry 1、https://github.com/code-423n4/2024-02-ai-arena/blob/main/test/FighterFarm.t.sol#L241-L281 In the test function testRedeemMintPass of the FighterFarm.t.sol contract, viewing the information shows that the default test value of weight is 83, emit FighterCreated(id: 0, weight: 83, element: 1, generation: 0)

but after modifying the value of "dna" to "dna123", the weight value of the generated fighter is 94 emit FighterCreated(id: 0, weight: 94, element: 1, generation: 0)

function testRedeemMintPass() public { uint8[2] memory numToMint = [1, 0]; bytes memory signature = abi.encodePacked( hex"20d5c3e5c6b1457ee95bb5ba0cbf35d70789bad27d94902c67ec738d18f665d84e316edf9b23c154054c7824bba508230449ee98970d7c8b25cc07f3918369481c" ); string[] memory _tokenURIs = new string; _tokenURIs[0] = "ipfs://bafybeiaatcgqvzvz3wrjiqmz2ivcu2c5sqxgipv5w2hzy4pdlw7hfox42m";

// first i have to mint an nft from the mintpass contract assertEq(_mintPassContract.mintingPaused(), false); _mintPassContract.claimMintPass(numToMint, signature, _tokenURIs); assertEq(_mintPassContract.balanceOf(_ownerAddress), 1); assertEq(_mintPassContract.ownerOf(1), _ownerAddress); // once owning one i can then redeem it for a fighter uint256[] memory _mintpassIdsToBurn = new uint256[](1); string[] memory _mintPassDNAs = new string[](1); uint8[] memory _fighterTypes = new uint8[](1); uint8[] memory _iconsTypes = new uint8[](1); string[] memory _neuralNetHashes = new string[](1); string[] memory _modelTypes = new string[](1); _mintpassIdsToBurn[0] = 1; _mintPassDNAs[0] = "dna123"; _fighterTypes[0] = 0; _neuralNetHashes[0] = "neuralnethash"; _modelTypes[0] = "original"; _iconsTypes[0] = 1; // approve the fighterfarm contract to burn the mintpass _mintPassContract.approve(address(_fighterFarmContract), 1); _fighterFarmContract.redeemMintPass( _mintpassIdsToBurn, _fighterTypes, _iconsTypes, _mintPassDNAs, _neuralNetHashes, _modelTypes ); // check balance to see if we successfully redeemed the mintpass for a fighter assertEq(_fighterFarmContract.balanceOf(_ownerAddress), 1); // check balance to see if the mintpass was burned assertEq(_mintPassContract.balanceOf(_ownerAddress), 0); }

Attributes such as fighterType, iconsType, mintPassDna should be stored in the structure corresponding to tokenID

Assessed type

Invalid Validation

#0 - c4-pre-sort

2024-02-22T07:52:42Z

raymondfam marked the issue as sufficient quality report

#1 - c4-pre-sort

2024-02-22T07:52:48Z

raymondfam marked the issue as duplicate of #33

#2 - c4-pre-sort

2024-02-26T00:53:36Z

raymondfam marked the issue as duplicate of #1626

#3 - c4-judge

2024-03-06T03:12:30Z

HickupHH3 marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter