Reproduced Exploit
Gondi — distribute() lacks access control (Pool accounting corruption)
1. distribute() is permissionless. 2. Attacker crafts a loan: principalAddress = Junk, sole lender = Pool. 3. Junk is transferred to the Pool; loanLiquidation does totalAssets += _received. 4. Attacker with a dust USDC deposit withdraws ~2× real USDC, draining victims.
Chain
Other
Category
untagged
Date
Apr 2024
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: 35205-h-03-function-distribute-lacks-access-control-allowing-anyon. Standalone Foundry PoC and full write-up: 35205-h-03-function-distribute-lacks-access-control-allowing-anyon_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/access-roles · vuln/direct-drain · vuln/liquidation-logic
Reproduction: self-contained Foundry PoC with only
forge-std— no fork, no RPC. Full trace: output.txt. PoC: test/35205-h-03-function-distribute-lacks-access-control-allowing-anyon.sol.
Key info#
| Impact | HIGH — anyone calls LiquidationDistributor.distribute with a junk ERC20 as principalAddress and the Pool as lender; Pool credits junk as real assets; dust depositor drains victim USDC |
| Protocol | Gondi — NFT lending |
| Vulnerable code | LiquidationDistributor.distribute — no access control; Pool.loanLiquidation treats _received as asset inflow without a token check |
| Bug class | Missing access control → accounting inflation → fund theft |
| Finding | Code4rena — Gondi, 2024-04 · #35205 · reporter zhaojie |
| Report | code4rena.com/reports/2024-04-gondi |
| Source | AuditVault |
| Status | Audit finding — confirmed and mitigated (caller check added) |
| Compiler | ^0.8.24 (PoC) |
TL;DR#
distribute()is permissionless.- Attacker crafts a loan:
principalAddress = Junk, sole lender = Pool. - Junk is transferred to the Pool;
loanLiquidationdoestotalAssets += _received. - Attacker with a dust USDC deposit withdraws ~2× real USDC, draining victims.
The vulnerable code#
/// @> VULN: no access control
function distribute(address originator, Loan memory loan, uint256 amount) external {
MockERC20 token = MockERC20(loan.principalAddress);
token.transferFrom(msg.sender, address(this), amount);
...
Pool(lender).loanLiquidation(..., _sent, ...);
}
Fix: only allow Loan contracts to call distribute.
Root cause#
distribute trusts caller-supplied loan struct fields. The Pool's loanLiquidation never receives principalAddress, so it cannot reject junk tokens — any amount reported as _received inflates share pricing.
Attack walkthrough#
- Victim deposits 1000 USDC; attacker deposits 1 USDC dust.
- Attacker calls
distributewith 1000 Junk → Pool as lender. totalAssetsbecomes 2001 while real USDC is still 1001.- Attacker withdraws 1 share → ~1.999 USDC stolen excess.
Diagrams#
Impact#
Direct theft of depositor USDC/WETH via share-price inflation. Permissionless surface means any address can spam and corrupt accounting.
Taxonomy#
- genome: liquidation-logic, variant, direct-drain, access-roles, liquidation-underwater, timestamp-dependence
- sector: lending, stable, staking-pool, token
- severity: high
- platform: code4rena
Sources#
- AuditVault finding #35205
- Code4rena report 2024-04-gondi
- Reduced from code-423n4/2024-04-gondi
LiquidationDistributor.distribute+ PoolloanLiquidation
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 35205-h-03-function-distribute-lacks-access-control-allowing-anyon_exp (evm-hack-registry mirror).
- AuditVault finding: 35205-h-03-function-distribute-lacks-access-control-allowing-anyon.
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.