Reproduced Exploit

Vultisig — whitelist index zero lets every unlisted buyer purchase

The whitelist map returns zero for an account that has never been added. The validation only rejects an index above the configured maximum. With a maximum of 10, index zero is not above the maximum, so an unlisted account passes exactly as though it had a valid entry.

Jun 2024Otherlogic3 min read

Chain

Other

Category

logic

Date

Jun 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: 35754-h-02-vultisig-whitelisting-can-be-bypassed-by-anyone-code4re. Standalone Foundry PoC and full write-up: 35754-h-02-vultisig-whitelisting-can-be-bypassed-by-anyone-code4re_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/logic/wrong-condition · vuln/access-control/missing-validation · vuln/logic/missing-check

Reproduction: self-contained Foundry PoC with no fork, RPC, or cheatcodes. Full trace: output.txt. Driver: test/35754-h-02-vultisig-whitelisting-can-be-bypassed-by-anyone-code4re_exp.sol.

AuditVault taxonomy: lang/solidity · sector/wallet · platform/code4rena · has/github · has/poc · severity/high · genome: wrong-condition · permanent · account-ownership

Key info#

ImpactHIGH — any account with the default whitelist index zero passes a launch gate configured for an index limit above zero.
ProtocolVultisig
Vulnerable codewhitelist purchase validation
FindingCode4rena Vultisig, 2024-06 · #35754 (H-02) · reporter leegh
StatusAudit finding; local reduction shows an unlisted buyer receiving 100 VULT.
Compiler^0.8.24 (local reduction)

TL;DR#

The whitelist map returns zero for an account that has never been added. The validation only rejects an index above the configured maximum. With a maximum of 10, index zero is not above the maximum, so an unlisted account passes exactly as though it had a valid entry.

The local PoC enables a limit of 10, does not grant the attacker a whitelist index, and completes a 100-token purchase. The asserted balance demonstrates the launch restriction has been bypassed.

The vulnerable code#

SOLIDITY
if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
    revert NotWhitelisted();
} // @> VULN: default index zero passes when allowed index is nonzero

The predicate bounds only the upper edge of the accepted range; it never proves that the caller has a membership index.

Root cause#

The sentinel value for missing membership overlaps the accepted range. The check needs both an existence test and a range test, but only the range test is present.

Preconditions#

  • A whitelist-limited sale has _allowedWhitelistIndex > 0.
  • Unlisted accounts retain the default mapping value zero.
  • The attacker can invoke the normal purchase route.

Attack walkthrough#

  1. The sale operator configures an allowed whitelist index of 10.
  2. The attacker remains absent from the whitelist and therefore reads index zero.
  3. 0 > 10 is false, so the check does not revert.
  4. The sale mints 100 VULT to the unwhitelisted attacker.

Diagrams#

flowchart TD A[Unlisted buyer has index 0] --> B[Sale allows indexes through 10] B --> C{0 greater than 10} C -->|No| D[Validation passes] D --> E[Buyer receives VULT despite no membership]

Remediation#

Reject the zero sentinel in addition to values above the permitted index:

SOLIDITY
uint256 index = _whitelistIndex[to];
if (_allowedWhitelistIndex == 0 || index == 0 || index > _allowedWhitelistIndex) {
    revert NotWhitelisted();
}

Use a separate boolean membership map where possible, and test the default mapping value explicitly in whitelist tests.

How to reproduce#

BASH
cd /workspaces/RustroverProjects/audits/evm-hack-registry/35754-h-02-vultisig-whitelisting-can-be-bypassed-by-anyone-code4re_exp
forge test -vvv

Sources#

Reference: Code4rena Vultisig finding H-02, curated by AuditVault.


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.