Platform: Code4rena
Start Date: 03/07/2023
Pot Size: $40,000 USDC
Total HM: 14
Participants: 74
Period: 7 days
Judge: alcueca
Total Solo HM: 9
Id: 259
League: ETH
Rank: 9/74
Findings: 2
Award: $762.57
🌟 Selected for report: 0
🚀 Solo Findings: 0
745.0513 USDC - $745.05
https://github.com/code-423n4/2023-07-basin/blob/9403cf973e95ef7219622dbbe2a08396af90b64c/src/Well.sol#L215-L240 https://github.com/code-423n4/2023-07-basin/blob/9403cf973e95ef7219622dbbe2a08396af90b64c/src/functions/ConstantProduct2.sol#L49-L54 https://github.com/code-423n4/2023-07-basin/blob/9403cf973e95ef7219622dbbe2a08396af90b64c/src/functions/ConstantProduct2.sol#L58-L75
The swapping mechanism of a Well using the ConstantProduct2.sol
as it's function is susceptible to a flashloan attack to drain pool tokens, or to a combination of a flashloan + sandwich attack to DoS user swaps.
A malicious actor can drain tokens by creating his own arbitrage with a flashloan, or he can DoS user swaps to effectively freeze swaps under certain conditions, which are realistic. Check POC for more details.
Let us define a basic 2 token Well, that implements the CP2 function (for simplicity's sake). TOKEN A - reserve = 10_000 TOKEN B - reserve = 10_000 With these values, the LP token supply would be 10_000 as well (the square root of 100 million)
Alice wants to swap 1000 of tokenA to tokenB so she uses _swapFrom()
and for a min amount she inputs a value i n the range of [100:900].
In the default scenario the tx will go as follows:
reserveA = 11_000
reserveB = _calcReserve (from the CP2) = LP supply ** 2 / reserveA = 100mill / 11_000 = 9090
This means that the amount out Alice would get is 910 which satisfies the minAmountOut, no matter what value in the specified range it is.
A malicious actor can use a flashloan (or even his own balance depending on the minAmountOut
) to add liquidity to tokenA and greatly decrease the amount Alice would receive by front-running her.
Malicious scenario:
reserveA after flashloan frontrun + Alice's input: 10_000 + 100_000 + 1_000(after Alice) reserveB = _calcReserve = LP supply ** 2 / reserveA = 10_000(reserveBbefore) * reserveA(before Alice) / reserveA = 1_010_000_000 / 111_000 = 9099 This means that the amount out Alice would get is only 101 which can fail is some cases due to the slippage check done. The amount get's even lower for greater pumps.
A malicious actor uses a flashloan to produce a similiar pump to the one mentioned above, but he does it in reverse, aka: reserveA = 10_000 reserveB after flashloan liquidity pump = 10_000 + 100_000 LP supply ** 2 right now is: 1_100_000_000 He begins a regular transaction from A to B, by adding 1000 A tokens so reserveA = 10_000 + 1_000 reserveB now changes as follow: _calcReserve = 1_100_000_000 / 11_000 = 100_000 This means that the actor managed to take out 10K of token B for just 1K token A, effectively draining the currently defined pool when he removes his liquidity to pay back the flashloan.
Manual Review
There should be a default mechanism implemented, such as that even non-maliciously defined Well's that are verified by the protocol team as 'innocent' aren't susceptible to such high reserve movements. You could do a number of things like:
Math
#0 - c4-pre-sort
2023-07-12T02:41:17Z
141345 marked the issue as primary issue
#1 - 141345
2023-07-13T07:58:29Z
previous audit by halborn talks about similar issue: 4.2 (HAL-02) SLIPPAGE MANIPULATION
Not sure if this is known issue
#2 - c4-sponsor
2023-07-16T14:44:58Z
publiuss marked the issue as sponsor disputed
#3 - publiuss
2023-07-16T14:45:57Z
This is an issue that is already present in other AMMs. The lack of a fee just makes the DOS cheaper than in other AMMs. However, it still requires paying for 2 Ethereum transaction fees. The use of a private mempool or a higher priority fee solves this problem.
This is not an issue as the actor is extracting value from themselves and this scenario is also possible in other AMMs. In the shared example, a malicious actor adds one-sided liquidity to drastically manipulate the price, and then swaps in the pool to get a drastically manipulated execution price. However, because they added the one sided liquidity themselves, they are trading against themselves -> The value of the LP tokens they received from adding liquidity decreases significantly. Thus, they are essentially extracting value from themselves and it is not an attack vector.
#4 - alcueca
2023-08-02T21:18:02Z
Vulnerability to sandwich attacks is expected of most AMMs, unless they advertise to the contrary. Likewise if used as griefing.
#5 - c4-judge
2023-08-02T21:18:08Z
alcueca marked the issue as unsatisfactory: Invalid
#6 - c4-judge
2023-08-02T21:20:16Z
alcueca marked the issue as satisfactory
#7 - c4-judge
2023-08-02T21:20:28Z
alcueca marked the issue as partial-50
#8 - alcueca
2023-08-02T21:22:17Z
After reading the much better description on #255, I agree that the lack of fees makes griefing attacks possible. Only partial credit for confusing report. Uneconomical DoS attacks are Medium at best.
#9 - c4-judge
2023-08-02T21:22:25Z
alcueca changed the severity to 2 (Med Risk)
#10 - c4-judge
2023-08-03T14:14:54Z
alcueca marked the issue as satisfactory
#11 - c4-judge
2023-08-05T21:46:15Z
alcueca marked the issue as selected for report
#12 - c4-judge
2023-08-15T19:38:22Z
alcueca marked the issue as duplicate of #255
#13 - c4-judge
2023-08-21T20:40:11Z
alcueca marked the issue as not selected for report
🌟 Selected for report: 0xprinc
Also found by: 0x11singh99, 0xAnah, 0xWaitress, 0xkazim, 2997ms, 33audits, 404Notfound, 8olidity, CRIMSON-RAT-REACH, CyberPunks, DanielWang888, Deekshith99, Eeyore, Eurovickk, Inspecktor, JGcarv, John, Jorgect, Kaysoft, LosPollosHermanos, MohammedRizwan, Qeew, QiuhaoLi, Rolezn, TheSavageTeddy, Topmark, Trust, Udsen, a3yip6, alexzoid, bigtone, codegpt, erebus, fatherOfBlocks, ginlee, glcanvas, hunter_w3b, josephdara, kaveyjoe, kutugu, mahdirostami, max10afternoon, oakcobalt, peanuts, pfapostol, ptsanev, qpzm, radev_sw, ravikiranweb3, sces60107, seth_lawson, te_aut, twcctop, zhaojie, ziyou-
17.5208 USDC - $17.52
In the _addLiquidty
function of the Well contract a user passes an array of uint256
of the amounts of underlying tokens he wants to deposit. This creates room for user error and potential losses on gas and fee-on-transfer token involvement, since there can be multiple wells with the same tokens, ordered differently in the _tokens[]
array. A user can pass the values in the wrong order for the current Well and add the wrong amount of liquidity, forcing him to remove and then redo.
https://github.com/code-423n4/2023-07-basin/blob/c1b72d4e372a6246e0efbd57b47fb4cbb5d77062/src/Well.sol#L413-L444 We can see the possibility for the amounts to be wrong since a user can accidentally pass them wrongly ordered in the array, adding liquidity not the way he intended.
Manual Review
Maybe change up the array from a uint256[] to a struct array holding the target token and the amount for user convenience and to avoid such errors.
Error
#0 - c4-pre-sort
2023-07-11T10:01:12Z
141345 marked the issue as low quality report
#1 - 141345
2023-07-13T08:40:55Z
user's error
maybe QA is more appropriate
#2 - alcueca
2023-08-03T21:36:35Z
This would be good QA advice
#3 - c4-judge
2023-08-03T21:36:40Z
alcueca changed the severity to QA (Quality Assurance)
#4 - alcueca
2023-08-04T21:03:23Z
Using structs would definitely be a good idea.
#5 - c4-judge
2023-08-04T21:03:29Z
alcueca marked the issue as grade-a