Open code423n4 opened 2 years ago
Fixed in lifinance/lifi-contracts@f35ed79a266a69b363d72332b7861d15d18b98cb
CBridgeFacet amount -- Fixed in lifinance/lifi-contracts@a8d6336c2ded97bdbca65b64157596b33f18f70d
I'm reasonably certain AnyswapRouterV4 does implement anySwapNative as it inherits from V3 and the function is present there (0x332730a4F6E03D9C55829435f10360E13cfA41Ff)
Summary
We list 5 low-critical findings and 1 non-critical finding here:
address(0)
as_anyswapData.token
and reverts without warning_cBridgeData.amount
of native assets to be sent to it_nxtpData.invariantData.sendingChainId
equals currentchainId
_nxtpData.invariantData.callTo
and_nxtpData.invariantData.callDataHash
equals current chainidinitializeDiamondCut
implementation disagrees with EIP-2535In summary of recommended security practices, it's better to check native assets, chainId and 3rd party bridge versions to avoid abuse. Also, it’s better to follow EIP-2535 proposal and check the exact amount for better practices.
(LOW) AnyswapFacet implicitly rejects
address(0)
as_anyswapData.token
and reverts without warningImpact
AnyswapFacet has a different interface for accepting native assets compared to other facets. This might lead to confusion and misuse of the facet, and result in loss of gas for contract users.
The logic of deciding which token to use in Anyswap is more complex than other facets. Overall, the decision is done twice, first for checking (and transferring funds) from the user, and then for deciding which Anyswap router function to call.
logic for
startBridgeTokensViaAnyswap
_anyswapData.token == address(0)
, native asset is used_anyswapData.token.underlying() == _anyswapData.router.wNATIVE()
, native asset is used_anyswapData.token.underlying() == address(0)
,_anyswapData.token
is used_anyswapData.token.underlying()
is usedlogic for
_startBridge
_anyswap.token.underlying() == _anyswapData.router.wNATIVE()
, call anySwapOutNative()_anyswapData.token == address(0)
, do nothing (note that this path would revert upon callingIAnyswapToken(_anyswapData.token).underlying()
in_startBridge
_anyswapData.token.underlying() == address(0)
, call anySwapOut()anySwapOutUnderlying()
The main mismatch rises when
_anyswapData.token == address(0)
,startBridgeTokensViaAnyswap
accepts those cases and uses native asset, while_startBridge
does not handle this and implicitly reverts.Since other facets usually use
address(0)
as native asset, we judged that users might be confused by the behaviour of AnyswapFacet, especially since it does not rejectaddress(0)
early in call, and only reverts at a relatively late stage.Proof of Concept
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L35 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L37 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L49 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L74 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L80 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L95 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L131 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L136 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L145
Recommended Mitigation Steps
It would be more appropriate to follow the same pattern as other facets, and add code to handle
address(0)
in_startBridge
. Or at the very least, disallowaddress(0)
early instartBridgeTokensViaAnyswap
andswapAndStartBridgeTokensViaAnyswap
and make it explicit thataddress(0)
as token is not allowed.(LOW) CBridgeFacet allows more than
_cBridgeData.amount
of native assets to be sent to itImpact
Allowing excessive native assets to be sent is unnecessary, and would potentially lead to loss of the caller's asset.
Proof of Concept
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/CBridgeFacet.sol#L68
Recommended Mitigation Steps
Use
==
rather than>=
(LOW) Anyswap router version confusion
Impact
Anyswap router used in deployment/test is not compatible with AnyswapFacet function calls.
The provided Anyswap router address in
test/facet/AnyswapFacet.test.ts
andconfig/anyswap.ts
are both implementingAnyswapV4Router
. However,anySwapOutNative
is only implemented in later versions of Anyswap router (e.g.AnyswapV6Router
). While this is not strictly included in audit scope, we deem it appropriate to bring attention to the mismatch, in case of incorrect deployment in the future.Proof of Concept
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L137 https://github.com/code-423n4/2022-03-lifinance/blob/main/config/anyswap.ts#L14 https://github.com/code-423n4/2022-03-lifinance/blob/main/test/facets/AnyswapFacet.test.ts#L14
Recommended Mitigation Steps
Use a newer Anyswap router address in deployment and test script instead.
(LOW) NXTPFacet does not check whether
_nxtpData.invariantData.sendingChainId
equals currentchainId
Impact
Diamond might be abused to serve as router and call prepare on the receiving chain.
In the nxtp protocol,
prepare
is called by the sender in the sending chain and byrouter
in the receiving chain. Diamond only intended to serve as the sender, but since it does not check_nxtpData.invariantData.sendingChainId
against the currentchainId
, it is possible to abuse the field and callprepare
as arouter
instead.While the above abuse is theoretically possible, there are several checks in the nxtp protocol that must be satisfied in order to perform such actions. Including getting Diamond approved as a valid router, making bids in place of Diamond and so on. Overall, the trick is quite difficult to pull off.
Proof of Concept
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L46 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L85 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L185
Recommended Mitigation Steps
Validate
_nxtpData.invariantData.sendingChainId
againstchainId
.(LOW) NXTPFacet does not check
_nxtpData.invariantData.callTo
and_nxtpData.invariantData.callDataHash
equals current chainidImpact
Intended fulfill call back can be skipped or replaced with other functions.
The design of the NXTPFacet is that the two fulfill functions will be called on the receiving chain. However, there are no checks against whether the two helper functions
completeBridgeTokensViaNXTP
andswapAndCompleteBridgeTokensViaNXTP
are passed into as calldata, and whether Diamond is even assigned ascallTo
. This allows users to assign arbitrary callbacks on the receiving chain.While this does not pose any direct threats, it would be appropriate to check the two calling fields and conform to contract usage design.
Proof of Concept
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L46 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L85 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L185
Recommended Mitigation Steps
Validate
_nxtpData.invariantData.callTo
and_nxtpData.invariantData.callDataHash
against the intended callback functions.(Non-Critical)
initializeDiamondCut
implementation disagrees with EIP-2535Impact
The original proposal of Diamond specifies that
_calldata
is allowed to carry additional information even when_init
is address(0). Current implementation disagrees with the proposal.Proof of Concept
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Libraries/LibDiamond.sol#L185
Recommended Mitigation Steps
Refer to EIP-2535: https://eips.ethereum.org/EIPS/eip-2535