Reproduced Exploit

Pods mulDivUp mints 1 share for 1 wei and it withdraws 9900990099009900990 assets

Queued deposits mint shares with mulDivUp. The vault holds 1000e18 assets and 100 shares, so 1 wei of assets is worth 0 shares if rounded down. Rounding up mints 1 share. Redeeming it pays (1000e18 + 1) / 101 = 9900990099009900990 assets. The round-down control mints 0 shares and withdraws nothing.

Dec 2022•Other•arithmetic•2 min read

Chain

Other

Category

arithmetic

Date

Dec 2022

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: 10416-rounding-up-in-minting-shares-openzeppelin-pods-finance-ethe. Standalone Foundry PoC and full write-up: 10416-rounding-up-in-minting-shares-openzeppelin-pods-finance-ethe_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/arithmetic/rounding-direction

Key info#

ProtocolPods Finance
FindingOpenZeppelin, AuditVault 10416
Vulnerable codeBaseVault._processDeposit
Harm1 extra share, redeemed for 9900990099009900990 assets
Chainlocal synthetic

TL;DR#

Queued deposits mint shares with mulDivUp. The vault holds 1000e18 assets and 100 shares, so 1 wei of assets is worth 0 shares if rounded down. Rounding up mints 1 share. Redeeming it pays (1000e18 + 1) / 101 = 9900990099009900990 assets. The round-down control mints 0 shares and withdraws nothing.

The vulnerable code#

SOLIDITY
// @> VULN: mulDivUp mints a whole share for 1 wei when the share price is above 1
uint256 shares = currentAssets == 0 || supply == 0 ? assets : assets.mulDivUp(supply, currentAssets);

mulDivUp(1, 100, 1000e18) is (1 * 100 + 1000e18 - 1) / 1000e18 = 1.

Root cause#

Share price is 10e18 assets per share. A 1-wei deposit is a fraction of a share. Rounding that fraction up donates a full share, which is then redeemable against the existing assets.

Preconditions#

totalSupply is 100 and currentAssets is 1000e18. The attacker queues a 1-wei deposit. No role is required.

Attack walkthrough#

  1. The attacker deposits 1 wei.
  2. _processDeposit rounds 1 * 100 / 1000e18 up to 1 share. Round-down is 0.
  3. That share is minted.
  4. redeem(1) pays 9900990099009900990 assets.

Diagrams#

flowchart TD A["Vault has 1000e18 assets and 100 shares"] --> B["Attacker queues 1 wei"] B --> C["VULN: mulDivUp mints 1 share"] C --> D["Round-down would mint 0"] D --> E["Redeem pays 9900990099009900990 assets"]

Impact#

One extra share is created for 1 wei and withdrawn for 9900990099009900990 assets, taken from the other shareholders. The control mints 0 shares and the attacker withdraws 0.

Remediation#

Round share minting down (mulDivDown / integer division), so a deposit smaller than one share mints nothing.

How to reproduce#

BASH
cd 10416-rounding-up-in-minting-shares-openzeppelin-pods-finance-ethe_exp
forge test -vv

Sources#


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.