- Type: Exploit
- Network: Binance Chain
- Total lost: 5K USDT
- Category: Access Control
- Exploited contracts:
- Attack transactions:
- Attack Block:: 23474461
- Date: Nov 29, 2022
- Reproduce:
forge test --match-contract Exploit_MBCToken -vvv
- Request a flash loan
- Swap all
MBC
forBUSD
(after this, the price ofMBC
will be inflated in the pool) - Now call
swapAndLiquifyStepV1
, which will add a certain amount ofMBC
andBUSD
to the pool - Exchange the
MBC
you gained in (2) for theBUSD
now in the pool. The pool belivesMBC
to be very valuable so gives you a lot ofBUSD
. - Repay flash loan.
- Keep the rest.
Understanding this attack needs a small prior on Uniswap router contracts and their methods.
Routers are contracts that facilitate the interaction with Uniswap pools. An important function in a router is addLiquidity
:
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
This will try to add liquidity to the pool where trades of tokenA/tokenB
happen. amountADesired/amountBDesired
is the ideal amount of A
and B
to add, while amountAMin/amountBMin
works as slippage protection.
With that in mind, we can check the MBC Token swapAndLiquifyStepv1()
function:
function swapAndLiquifyStepv1() public {
uint256 ethBalance = ETH.balanceOf(address(this));
uint256 tokenBalance = balanceOf(address(this));
addLiquidityUsdt(tokenBalance, ethBalance);
}
function addLiquidityUsdt(uint256 tokenAmount, uint256 usdtAmount) private {
uniswapV2Router.addLiquidity(
address(_baseToken),
address(this),
usdtAmount,
tokenAmount,
0,
0,
_tokenOwner,
block.timestamp
);
}
In this contract, the _baseToken
is the address 0x55d398326f99059fF775485246999027B3197955
, BUSD Stablecoin
. ETH
refers to the same _baseToken
.
Something important here to stress is that the MBC Token
has a balance of their own (ie: the contract has its own tokens, balanceOf(address(this)) != 0
) because it charges a fee to users on every transaction.
Now, inmediatly after calling swapAndLiquifyStepv1
, the contract will go to the BUSD/MBC
liquidity pool
and add all of its balances of both BUSD and MBC to it.
A small sidenote here, but: why does this contract have this function anyway? It does not seem to do much. The answer appears to be some controversial tokenomics. We haven't been able to pinpoint who invented it, but Safemoon, now facing lawsuits, seems to swear by it.
Anyway, moving on the attack. How does the attacker take advantage of this? They inflate the price of the token in the pool and foce the contract to buy it all up.
In the same transaction, they:
- Request a flash loan
- Swap all
MBC
forBUSD
(after this, the price ofMBC
will be inflated in the pool) - Now call
swapAndLiquifyStepV1
, which will add a certain amount ofMBC
andBUSD
to the pool - Exchange the
MBC
you gained in (2) for theBUSD
now in the pool. The pool belivesMBC
to be very valuable so gives you a lot ofBUSD
. - Repay flash loan.
- Keep the rest.
swapAndLiquifyStepv1
should be made private