# Attackers can call UToken.redeem() and drain the funds in assetManager

UToken.redeem() lacks a zero-check on uTokenAmount, so rounding lets an attacker redeem underlying tokens while burning zero uTokens, draining assetManager.

- Payout: Contest reward
- Severity: medium
- Program: Union
- Credited: chaduke
- Disclosed: 2023-02-07
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-02-union-judging/issues/12
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-02-union%3AM-4
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:precision-loss

**Target:** UToken.redeem() in union-v2-contracts market contract
**Root cause:** In the amountOut branch, uTokenAmount = (amountOut * WAD) / exchangeRate truncates to 0 when amountOut < exchangeRate/WAD, and redeem() does not revert on zero uTokenAmount, so underlyingAmount is paid out without burning any uTokens.
**Steps:**
1. Assume exchangeRate = 1000 WAD (each uToken = 1000 underlying).
2. Call `redeem(0, 999)` so amountIn=0, amountOut=999.
3. uTokenAmount = 999*WAD/1000WAD = 0; underlyingAmount = 999.
4. redeem() proceeds, pays 999 underlying, burns 0 uTokens.
5. Loop `redeem(0, exchangeRate/WAD - 1)` in one tx, recalculating amountOut each iteration as exchangeRate shifts, to drain assetManager.
**How found:** Code review of redeem()'s amountOut branch noticing integer division truncation could yield uTokenAmount=0 and that no zero-check guards the payout.
**Exploitation:** Attacker calls redeem with amountIn=0 and amountOut just below exchangeRate/WAD so the uToken burn truncates to zero while underlying is still transferred; repeating in a loop within a single transaction extracts funds from assetManager.
**Escalation:** Batching the zero-burn redeem in a loop within one transaction to drain assetManager rather than a single small theft.
**Insight:** In redeem-style functions with two input modes, check the truncating division branch: when amountOut < exchangeRate/WAD the burned share rounds to zero but the payout does not—always revert on zero shares minted/burned.
