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: 226/283
Findings: 1
Award: $1.27
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L224-L262
The redeemMintPass function of the FighterFarm.sol contract does not verify whether the input parameters are legal. Attackers can generate NFTs with arbitrary attributes.
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]);
}
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
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