# A malicious user can steal other user's deposits from Vault.sol

Vault.withdraw burns shares cast to uint96, so withdrawing >2^96 assets burns only 2^96 shares, leaving shares to drain other depositors.

- Payout: Contest reward
- Severity: high
- Program: PoolTogether
- Credited: zzzitron
- Disclosed: 2023-09-27
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-07-pooltogether-findings/issues/439
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-pooltogether%3AH-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:flash-loan vuln:precision-loss

**Target:** ERC4626 Vault.sol withdraw/_burn share accounting
**Root cause:** Vault._burn casts uint256 shares to uint96 when burning, so withdrawing more than type(uint96).max assets burns only type(uint96).max shares, leaving residual shares credited to the attacker.
**Steps:**
1. Deposit 2 * type(uint96).max underlying via two Vault.deposit() calls (each capped at uint96 max).
2. Call Vault.withdraw() for the full 2 * type(uint96).max assets.
3. Observe _withdraw->_burn burns only uint96(_shares) = type(uint96).max shares while transferring full assets.
4. Attacker retains type(uint96).max shares and withdraws again, draining other depositors' assets.
5. Alternatively flashLoan/flashMint to reach the deposit size, withdraw own deposit, repay loan, keep residual shares.
**How found:** Code reading of withdraw/_withdraw/_burn path noticing uint256->uint96 cast in _burn at Vault.sol line 1139/1155, combined with deposit cap of type(uint96).max.
**Exploitation:** Attacker deposits up to uint96 max per tx (twice), withdraws total; only uint96 max shares burned, full assets returned, residual shares used to withdraw other users' funds. Flash loans let a non-first depositor bootstrap the position, repay, and keep shares.
**Escalation:** Residual shares can be repeatedly withdrawn to drain all other depositors; flash loan removes capital requirement.
**Insight:** When a protocol caps deposits at type(uint96).max, check every downstream cast of share/asset amounts to uint96 — truncation on burn/withdraw leaves phantom shares that can be redeemed against other users' funds.
