Seaman
Total Losses
$7.0K+
Date
Network
Categories
business logic price manipulationStep-by-step
- Flashloan some USDC
- Use the flashloan to buy all GVC in a pool
- Call
transfer()
so contract buys GVC at current high price - Sell your GVC
- Return flashloan
Detailed Description
This is very similar to the attack on MBC. It actually involves the same method, swapAndLiquifyV1
.
Every time someone called _transfer
on Seaman, the swapAndLiquify
calls where made. These methods would exchange the accumulated fee on the contract for GVC
, passing through the BUSD
pool.
function _transfer(
address from,
address to,
uint256 amount
) internal override {
// ...
if( uniswapV2Pair.totalSupply() > 0 && balanceOf(address(this)) > balanceOf(address(uniswapV2Pair)).div(10000) && to == address(uniswapV2Pair)){
if (
!swapping &&
_tokenOwner != from &&
_tokenOwner != to &&
!ammPairs[from] &&
!(from == address(uniswapV2Router) && !ammPairs[to])&&
swapAndLiquifyEnabled
) {
swapping = true;
swapAndLiquifyV3();
swapAndLiquifyV1();
swapping = false;
}
}
// ...
}
function swapAndLiquifyV1() public {
uint256 canlpAmount = lpAmount.sub(lpTokenAmount);
uint256 amountT = balanceOf(address(uniswapV2Pair)).div(10000);
if(balanceOf(address(this)) >= canlpAmount && canlpAmount >= amountT){
if(canlpAmount >= amountT.mul(5))
canlpAmount = amountT.mul(5);
lpTokenAmount = lpTokenAmount.add(canlpAmount);
uint256 beflpBal = lpToken.balanceOf(address(this));
swapTokensFor(canlpAmount,address(lpToken),address(this));
uint256 newlpBal = lpToken.balanceOf(address(this)).sub(beflpBal);
lpDivTokenAmount = lpDivTokenAmount.add(newlpBal);
isLpProc = true;
}
}
function swapAndLiquifyV3() public {
uint256 canhAmount = hAmount.sub(hTokenAmount);
uint256 amountT = balanceOf(address(uniswapV2Pair)).div(10000);
if(balanceOf(address(this)) >= canhAmount && canhAmount >= amountT){
if(canhAmount >= amountT.mul(5))
canhAmount = amountT.mul(5);
hTokenAmount = hTokenAmount.add(canhAmount);
uint256 befhBal = hToken.balanceOf(address(this));
swapTokensFor(canhAmount,address(hToken),address(this));
uint256 newhBal = hToken.balanceOf(address(this)).sub(befhBal);
hDivTokenAmount = hDivTokenAmount.add(newhBal);
isHProc = true;
}
}
This makes it possible for an attacker to manipulate the price and force the contract to buy tokens at a high price. In this case, the attacker influenced the price of GVC
by requesting a flashloan of BUSD
and buying up all the available liquidity of GVC in the pool and then calling transfer()
on Seaman.
Possible mitigations
- Prevent users to manipulate contract balances via low liquidity pair interactions.
- Do not automatically perform trades without a sanity check on the prices