# FixedTokenBSwap: fake-token oracle manipulation drains RTV

FixedTokenBSwap trusted a user-supplied Uniswap path and a fake token's balanceOf, letting an attacker drain 500 RTV for ~0.01 ETH.

- Payout: Real-world hack
- Severity: high
- Program: FixedTokenBSwap
- Credited: DeFiHackLabs
- Disclosed: 2025-06-15
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2025-06/FixedTokenBSwap_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20250615-fixedtokenbswap
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:source-review vuln:business-logic vuln:oracle-manipulation

**Target:** Solidity swap contract using UniswapV2 getAmountsIn pricing
**Root cause:** swap() derives the required input via UniswapV2 getAmountsIn on an attacker-controlled path and then pays out a fixed 10 RTV after a no-op transferFrom, so a fake token with inflated balanceOf makes the quote trivially cheap.
**Steps:**
1. Deploy FakeToken whose balanceOf returns 100 ether and transferFrom is a no-op.
2. Create UniswapV2 pair FakeToken/RTV, seed it with RTV, call sync() to set reserves.
3. Call FixedTokenBSwap.swap([fakeToken, RTV], max) from a fresh contract; getAmountsIn quotes tiny fake-token input, transferFrom no-ops, contract sends 10 RTV.
4. Repeat from 50 fresh sender contracts to bypass the one-swap-per-address daily gate, accumulating 500 RTV.
5. Sell accumulated RTV for ETH on UniswapV2 router.
**How found:** Recognized that swap() trusts a caller-supplied path for pricing and pays a fixed amount after transferFrom, so a fake token with lying balanceOf/transferFrom could satisfy the quote for free.
**Exploitation:** Attacker seeds a fake-token/RTV Uniswap pair with a small ETH->RTV buy, then loops 50 fresh helper contracts each calling swap() with the fake path; each call yields 10 RTV for a no-op transferFrom, then dumps 490 RTV to ETH for ~0.4+ ETH profit.
**Escalation:** Fresh per-call contracts bypass the one-swap-per-sender-per-day limit, scaling 10 RTV per call to 500 RTV.
**Insight:** When a contract prices via UniswapV2 getAmountsIn on a caller-supplied path, check whether the input token's balanceOf/transferFrom are trusted; a fake token with inflated balanceOf and no-op transferFrom makes any fixed-payout swap free. Also check per-address rate limits for fresh-contract bypass.
