Reproduced Exploit

TraitForge generation rollover is permanently blocked by the wrong modifier

TraitForgeNft is configured as EntropyGenerator.allowedCaller, but the audited function uses onlyOwner. On every generation rollover the NFT calls initializeAlphaIndices; msg.sender is the NFT, not the owner, so the call reverts. The first capped mint in this reduction is therefore uncallable.

Jul 2024Otheraccess-control3 min read

Loss

Generation rollover reverts, bricking mintToken, mintWithBudget, and forge at the boundary.

Chain

Other

Category

access-control

Date

Jul 2024

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 37920-h-06-minttoken-mintwithbudget-and-forge-in-the-traitforgenft. Standalone Foundry PoC and full write-up: 37920-h-06-entropy-generator-initialize-alpha-indices-wrong-modifier-code4rena_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/access-control/missing-modifier · vuln/dos/init-constraint · vuln/logic/wrong-condition Reproduction: local synthetic Foundry test; output.txt and test source.

Key info#

LossGeneration rollover reverts, bricking mintToken, mintWithBudget, and forge at the boundary.
Vulnerable contractEntropyGenerator.initializeAlphaIndices()
Attacker EOAAny caller can trigger the normal mint/forge path once the generation cap is reached.
Attack contractExploit (local synthetic harness)
Attack txExploit.run() — catches the NFT mint revert and asserts no state persisted.
Chain / block / dateLocal synthetic chain · block 0 · 2024-07 report
Compilersolc ^0.8.24
Bug classAccess-control mismatch causing a generation-boundary denial of service.

TL;DR#

TraitForgeNft is configured as EntropyGenerator.allowedCaller, but the audited function uses onlyOwner. On every generation rollover the NFT calls initializeAlphaIndices; msg.sender is the NFT, not the owner, so the call reverts. The first capped mint in this reduction is therefore uncallable.

The vulnerable code#

The finding quotes the exact declaration:

SOLIDITY
function initializeAlphaIndices() public whenNotPaused onlyOwner { // @> VULN
    // initialize alpha indices
}

The fix is the one-line modifier change:

DIFF
- function initializeAlphaIndices() public whenNotPaused onlyOwner {
+ function initializeAlphaIndices() public whenNotPaused onlyAllowedCaller {

Root cause and preconditions#

The contract has two distinct authorities: the deployment owner and the NFT contract allowed to update alpha indices. The function protects the latter operation with the former role. No unusual setup is needed; ordinary traffic that reaches the generation cap activates the broken call.

Attack walkthrough#

  1. Exploit deploys EntropyGenerator, then TraitForgeNft, and configures the NFT as allowedCaller.
  2. mintToken() reaches the one-token generation cap and calls _incrementGeneration().
  3. _incrementGeneration() calls initializeAlphaIndices() from the NFT address. onlyOwner rejects that caller and the complete mint reverts.
  4. Exploit.run() catches the revert and asserts total supply, generation, and alpha-index version remain unchanged. The owner-only control call succeeds, isolating the bug to the modifier.

Diagrams#

sequenceDiagram participant U as User participant N as TraitForgeNft participant E as EntropyGenerator U->>N: mintToken() at generation cap N->>N: _incrementGeneration() N->>E: initializeAlphaIndices() E-->>N: revert caller is not owner N-->>U: mint reverts generation cannot advance

Impact and remediation#

The protocol's minting and forging flows can be permanently unavailable at each generation boundary. Replace onlyOwner with onlyAllowedCaller on initializeAlphaIndices, then retain owner-only protection for administrative functions such as setting the allowed caller.

How to reproduce#

BASH
cd 37920-h-06-entropy-generator-initialize-alpha-indices-wrong-modifier-code4rena_exp
forge test -vvvvv

This is a local synthetic with no RPC, fork, or fabricated token profit.

Sources#

Reference: Code4rena TraitForge 2024-07, finding #37920.


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.