Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Unlimited BEAT Reward Exploit via Transferable BACKSTAGE_PASS (ERC-1155)

Root + Impact

Description

The attendPerformance function allows users to earn rewards such as 3x BEAT tokens when attending a performance with a BACKSTAGE_PASS However the contract only tracks attendance based on the wallet address not on the specific token ID of the pass

Since the BACKSTAGE_PASS is implemented using the ERC1155 standard it is inherently fungible and transferable An attacker can exploit this by transferring a single BACKSTAGE_PASS between multiple wallets Each new wallet can use the same pass to attend the same performance and claim rewards effectively allowing unlimited farming of BEAT tokens

This breaks the intended logic of a single pass per reward and introduces a significant vulnerability that can be economically exploited

@> require(!hasAttended[performanceId][msg.sender], "Already attended this performance");
@> require(block.timestamp >= lastCheckIn[msg.sender] + COOLDOWN, "Cooldown period not met");

Risk

Likelihood: High

This vulnerability allows infinite reward farming by transferring a single BACKSTAGE_PASS (ERC-1155 token) across multiple wallets since the contract only tracks attendance by wallet address and not by token ID a malicious actor can:

  1. Attend with Wallet A → get 3x rewards.

  2. Transfer the same token to Wallet B → attend again → get 3x rewards.

  3. Repeat across unlimited wallets

Impact:


  • Unlimited Reward Farming

    Attackers can continuously transfer the same BACKSTAGE_PASS between different wallets to claim 3x rewards multiple times, leading to excessive and unbounded distribution of reward tokens (BEAT).

  • Token Supply Inflation

    This loophole causes excessive minting of BEAT tokens, which may devalue the token destabilize the in-app economy and undermine the integrity of the reward system

Proof of Concept

// Assume BACKSTAGE_PASS has tokenId = 2
// Attacker Wallet A initially owns the BACKSTAGE_PASS
vm.startPrank(attackerA);
festivalPass.attendPerformance(1); // Attends and gets 3x reward
festivalPass.safeTransferFrom(attackerA, attackerB, 2, 1, ""); // Transfer to Wallet B
vm.stopPrank();
// Wallet B now uses the same pass to attend again
vm.startPrank(attackerB);
festivalPass.attendPerformance(1); // Gets 3x reward again
festivalPass.safeTransferFrom(attackerB, attackerC, 2, 1, ""); // Transfer to Wallet C
vm.stopPrank();
// Wallet C continues the exploit
vm.startPrank(attackerC);
festivalPass.attendPerformance(1); // Gets 3x reward again
vm.stopPrank();

Recommended Mitigation

Make the BACKSTAGE_PASS tokens non-transferable by overriding the safeTransferFrom and safeBatchTransferFrom functions to revert all transfers.
This ensures that once a pass is assigned to a wallet, it cannot be transferred or reused by another user, fully preventing reward farming through token rotation.
function safeTransferFrom(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override {
revert("BACKSTAGE_PASS is non-transferable");
}
function safeBatchTransferFrom(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override {
revert("BACKSTAGE_PASS is non-transferable");
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Unlimited beat farming by transferring passes to other addresses.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.