Closed howlbot-integration[bot] closed 3 months ago
koolexcrypto marked the issue as primary issue
That is possible, but we are targeting this case by using the Claim event, so if users do this they loose their points
That is possible, but we are targeting this case by using the Claim event, so if users do this they loose their points
koolexcrypto changed the severity to QA (Quality Assurance)
koolexcrypto marked the issue as grade-c
Lines of code
https://github.com/code-423n4/2024-05-loop/blob/main/src/PrelaunchPoints.sol#L240-L266 https://github.com/code-423n4/2024-05-loop/blob/main/src/PrelaunchPoints.sol#L448-L464
Vulnerability details
Impact
The main invariant : "Withdrawals are only active on emergency mode or during 7 days after loopActivation is set" is broken
This means a user can get protocol points for free, without having to trade their
LRT tokens
forlpETH
Proof of concept
When users stake
LRT
tokens in the protocol, they earn an amount of points corresponding to their deposit which is tracked in a backend server. Users are allowed towithdraw()
theirLRT
but only up to7 days
after the protocol's admin has calledPrelaunchPoints::setLoopAddresses()
. Doing so, they "lose all their points".7 days after the
PrelaunchPoints::setLoopAddresses()
call, the admins can triggerPrelaunchPoints::convertAllETH()
which sets thestartClaimDate
and allows users toclaim()
theirlpETH
.When a user
claim()
, its stakedLRT
s are swapped tonative ETH
through theexchangeProxy
contract. TheseETH
are then sent to thelpETH
contract which mints the corresponding amount oflpETH
https://github.com/code-423n4/2024-05-loop/blob/main/src/PrelaunchPoints.sol#L259-L263
Once
startClaimDate
is set, users can'twithdraw()
anymore to get theirLRT
back.The issue here is when the swap occurs through the
_fillQuote()
function, the_data
parameter supplied can be crafted to retrieve the user'sLRT
tokens (without losing their points).Before the swap is executed, the flow first reaches the
_validateData()
function which verifies that thedata
sent is a payload that executes either the function corresponding toUNI_SELECTOR (0x803ba26d)
orTRANSFORM_SELECTOR (0x415565b0)
The
UNI_SELECTOR
is the selector that corresponds to the following functionsellTokenForEthToUniswapV3(bytes,uint256,uint256,address)
wherebytes
is a UniswapV3encodedPath
that describes the entire path of the swap.Since this
encodedPath
is never checked, a malicious user can create a malicious token (calledEvil Token
) and 2 malicious pools (LRT-EVIL
andEVIL-WETH
) that will be inserted in the middle of theLRT to WETH
swap like such :At the end of the swap, the
LRT
tokens will be stored in one of the user's malicious pools and since he controls one side of it with hisEvil Token
he can easily extract theLRT
back.For the demonstration, we are going to use the
ezETH
token asLRT token
Here is a summary of the exploit path :
ezETH
ezETH
inPrelaunchPoints
(and gets points)startClaimDate
to be reachedEVIL
)ezETH-EVIL
EVIL-WETH
claim()
to trigger the exploitezETH-EVIL
pool holds the 1ezETH
the user staked inPrelaunchPoints
EVIL
forezETH
to retrieve its stake from the poolFirst, we need to install some dependencies :
Then add the following in a new file called
test/EvilERC20.sol
Then create a new test file called
test/WithdrawInvariant.t.sol
(which was made based on thePrelaunchPoints.t.sol
provided)Finally the PoC can be run using :
At the end of it, the user lost only dust from swap fees
Tools used
Foundry + Manual review
Recommended mitigation steps
There is no obvious mitigation steps to patch the issue as the protocol expects the provided data to come from 0x API
One idea would be to parse the swap path in
_decodeUniswapV3Data()
to make sure it swaps directly fromLRT
toWETH/ETH
Another way (that might not fit with the protocol's policy) would be to not rely on
0x API
for the swap and process a swap directly on a DEX with suitable swap parametersAssessed type
Context