Reproduced Exploit
Basis MultiDistributor — unprotected minimum-distribution list growth
setMinimumDistribution is documented for trusted participants, but anyone can append token addresses. Repeated zero-minimum entries inflate every distribution loop and can eventually cause a gas-limit denial of service.
Chain
Other
Category
access-control
Date
Jan 2022
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
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.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 16760-setminimumdistribution-is-not-protected-trailofbits-basis-pd. Standalone Foundry PoC and full write-up: 16760-setminimumdistribution-not-protected_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/access-control/missing-auth · vuln/dos/unbounded-loop
Reproduction: local synthetic Foundry reduction; the complete passing trace is in output.txt.
Key info#
| Field | Value |
|---|---|
| Loss | An arbitrary caller appends 32 entries, increasing distribution work from 1 to 33 iterations. |
| Vulnerable contract | MultiDistributor.setMinimumDistribution in test/16760-setminimumdistribution-not-protected.sol |
| Attacker EOA | 0x1111111111111111111111111111111111111111 |
| Attack contract | Exploit |
| Attack tx | Local Foundry Exploit.run() |
| Chain · block · date | Ethereum model · block 0 · synthetic |
| Compiler | Solidity ^0.8.24 |
| Bug class | Missing authorization on unbounded list mutation |
TL;DR#
setMinimumDistribution is documented for trusted participants, but anyone can append token addresses. Repeated zero-minimum entries inflate every distribution loop and can eventually cause a gas-limit denial of service.
Background#
The minimum-distribution threshold exists to prevent negligible entries. It is ineffective when untrusted callers can add entries without governance or owner authorization.
The vulnerable code#
function setMinimumDistribution(address token, uint256 tokenMinDistribution) external {
if (tokenIdx[token] == 0) tokens.push(token);
// @> VULN: intended trusted-participant operation has no access check.
minDistribution[token] = tokenMinDistribution;
}
Root cause#
The mutator is public and unbounded while distribute iterates the entire tokens array. No role, allowlist, or cap protects the expensive state transition.
Preconditions#
setMinimumDistributionis externally callable.- Distribution operations iterate all registered tokens.
- An attacker can submit repeated transactions (or a loop in one transaction).
Attack walkthrough#
- Add 32 unique token addresses with minimum distribution zero.
- Invoke
distribute; it loops 33 entries instead of the baseline one. - The passing trace records the enlarged loop at output.txt:4.
Diagrams#
Remediation#
Restrict the setter to an owner, controller, or allowlist and cap the list length. Consider a mapping-based distribution registry or bounded iteration rather than an attacker-controlled array.
How to reproduce#
cd evm-hack-registry/16760-setminimumdistribution-not-protected_exp
forge test -vvvvv
Sources#
Reference: https://github.com/trailofbits/publications/blob/master/reviews/basis.pdf
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 16760-setminimumdistribution-not-protected_exp (evm-hack-registry mirror).
- AuditVault finding: 16760-setminimumdistribution-is-not-protected-trailofbits-basis-pd.
Alerts & third-party analyses
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.