Closed code423n4 closed 2 years ago
The contract assumes you use the API correctly, through a router or that you know what you are doing.
Looks off as requires code to be used improperly
And Readme explicitly states basic assumptions
Closing as improper usage from docs
Lines of code
https://github.com/code-423n4/2022-10-traderjoe/blob/79f25d48b907f9d0379dd803fc2abc9c5f57db93/src/LBPair.sol#L616-L675
Vulnerability details
Description
The
LBRouter
contract provides external functions intended to remove liquidity from the underlying trading pairs viaremoveLiquity()
andremoveLiquidityAVAX()
. Presumably for integration reasons, the underlyingLBPair.burn()
function that contains the actual token burn functionality is also markedexternal
with no access controls.To use it, a user would send
LBToken
s to theLBPair
contract, and then callLBPair.burn()
. The transfer is accomplished via eitherLBToken.safeTransferFrom()
orLBToken.safeBatchTransferFrom()
, depending on the user's preference/intentions.However,
LBPair.burn()
doesn't contain a check to ensure that the caller owns the tokens being burned. It also allows setting the destination of the transfer of the underlying ERC20 tokens to an arbitrary address.Thus, if the user doesn't submit a batch-style transaction, it is possible for an attacker to submit a valid
burn()
transaction with their own address in the_to
argument.Alternatively, an attacker or bot could frontrun the call to
LBPair.burn()
with the same address substitution.Severity Rationalization
The project is certainly aware of the risks for this function, as they state in comments both in the code and in the contest that
LBPair
should not be directly interacted with by users.That being the case, the warden feels that this should be enforced by the code itself, and not a warning issued to users (via source code comments) who may not understand what "important safety checks" means.
Since this function was likely designed this way to allow for 3rd party integration, it seems like an access control modifier is the best way to allow integration while ensuring safety for users.
Proof of Concept
The test suite already contains a test for burning liquidity without using
LBRouter.removeLiquity()
/LBRouter.removeLiquidityAVAX()
,LiquidityBinPairLiquidityTest.testBurnLiquidity()
. It can be modified to the below version to demonstrate that any user can callburn()
. The test will fail due to theassertEq
, showing thatBOB
did not receive the tokens.Mitigation
First, consider carefully the business case of allowing arbitrary callers to directly interact with this function. A contract access control list could be implemented, so that some security assumptions about the value of
_to
and the caller could be made.Alternatively, there are various helper contracts that provide functions to determine if the caller is an EOA. If a whitelist is seen as undesirable because it would be an administrative burden or is seen as a decentralization risk, an EOA/contract existence check seems like a valid way to address that concern, while significantly raising the bar required for a user to footgun themselves.
Tooling - Foundry, Manual review