Daily Security – Telegram
Forwarded from Viktor🔮
Forwarded from Viktor🔮
Forwarded from Viktor🔮
Forwarded from Viktor🔮
Daily Security pinned «—Learn from Reading Audit Reports (Sturdy Report) https://www.youtube.com/watch?v=9-Ye1HTvQIU —My First Bounty Award https://www.youtube.com/watch?v=vNB1t2Rpbtw —Unhacked CTF - Reaper https://www.youtube.com/watch?v=1xnMxdfYOG0 —Over 10k Achieved in Web3…»
(code-423n4/2022-03-lifinance-findings)
Source , Code

Number: 1
Problem: approveERC20() uses unlimited approval
Severity: Medium

Impact:
approveERC20() is using unlimited approval.
Although unlimited approval is used by various DeFi platforms to minimize transaction fees and improve user experience, it introduces security risks as well.

Mitigation steps: approveERC20() -> approve( only required amount) (approve-spend pattern), or rather using increaseERC20Allowance()
/ decreaseERC20Allowance().
-to offer a choice: approve only what they need to spend at the time, or a much larger amount(future) (Zapper.fi, Curve.fi)
- EIP2612

Proof of Concept:
-Bancor, early 2020 (function calling tranferFrom was made public)
-Fucurombo (February 2021)
-Unicats (rugpull, 2020)
-Degen Money(frontend, mindless signing)
-Primitive Finance
-DeFi Safer

#CODE4ARENA #MEDIUM #REPORT #ERC20 #LIFINANCE
(code-423n4/2022-03-lifinance-findings)
Source, Code

Number: 2
Problem: approveMax for malicious routers leads to loss of funds.
AnyswapFacet can be exploited to approve arbitrary tokens.
Severity: Medium

Impact:
In AnyswapFacet.sol we parse arbitrary data in _anyswapData allowing an attacker to drain funds (ERC20 or native tokens) from the LiFi contract.

Functions effected:
-AnyswapFacet.startBridgeTokensViaAnyswap()
-AnyswapFacet.swapAndStartBridgeTokensViaAnyswap()
(multiple call of underlying() that can replace the the token at some point)

Proof of Concept
This attack works -in AnyswapFacet.startBridgeTokensViaAnyswap() by having a malicious _anyswapData.token which may change the value return in IAnyswapToken(_anyswapData.token).underlying() -> allows for transferring these malicious ERC20 tokens to pass the required balance checks.
-execute the following which will either approve or transfer funds to _anyswapData.router for a different underlyingtoken to the one which supplied the funds to LiFi.
-Since _anyswapData.router is an address in the attackers control they either are transferred native tokens or they have an allowance of ERC20 tokens that they can spend arbitrarily.

Mitigation steps:
Consider whitelisting both Anyswap tokens and Anyswap routers (using two distinct whitelists) restricting the attackers ability to use malicious contracts for this attack.
Consider also only calling IAnyswapToken(_anyswapData.token).underlying() once and passing this value to _startBridge().

There can be fund leftover in the contract under normal operation, for example this tx. In fact, ~$300 worth of token is left in the LI.Fi smart contract on ETH mainnet 0x5a9fd7c39a6c488e715437d7b1f3c823d5596ed1 as of block 14597316. I don't think this is High Risk because the max amount lost is no more than allowed slippage, which can be loss to MEV too.

#CODE4ARENA #MEDIUM #REPORT #ERC20 #LIFINANCE
(code-423n4/2022-03-lifinance-findings)
(code-423n4/2022-03-lifinance-findings)
Source, Code

Number: 3
Problem: Can get remains token in this contract
Swapper can be used to steal all the funds from the contract
Severity: Medium

Vulnerability details:
The Swapper allow arbitrary _swapData( _lifiData ) (0x style), this makes it possible for a attacker to steal the funds in the contract.
Based on the context, we believe it's possible that the contract can hold funds.
The funds can be the refunds of failed orders, or fee rebates from bridging or dex aggregators, etc.
See also the permissioned WithdrawFacet.


Proof of Concept:
Given:
There are 100 USDC tokens in the contract.
The attacker can submit a swapTokensGeneric() with USDT as receivingAssetId with the following SwapData[]:

100 USDC to USDT;
As a result, the attacker will receive ~100 USDT with 0 USDC paid.

Mitigation/recommendation
Given that Swapper is a standlone module that can be and should be deployed as a standalone contract, we suggest spin it off from the diamond so that it can no longer access the funds in the diamond contract.

#CODE4ARENA #MEDIUM #REPORT #INVALIDCHECK #LIFINANCE
(code-423n4/2022-03-lifinance-findings)
Source, Code

Number: 4
Problem: Token will remain in the contract
LibSwap: Excess funds from swaps are not returned
Severity: Medium

Impact:
It is probable for _swapData.fromAmount to be greater than the actual amount used (eg. when swapping for an exact output, or when performing another swap after swapping with an exact input). However, these funds aren’t returned back to the user and are left in the lifi contract.

Proof of Concept:
https://github.com/code-423n4/2022-03-lifinance/blob/main/test/facets/AnyswapFacet.test.ts#L153-L194
The test referenced above swaps for MATIC for 1000 USDT exactly. Logging the matic amounts before and after the swap and bridge call, one will find 18.01 MATIC is unused and left in the contract when it should be returned to the user.
Mitigation

this issue would not allow to access other users funds and only happens if the user passes these kind of swaps himself. The multi-swap feature is mainly used to allow swapping multiple different tokens into one, which is then fully swapped.

#CODE4ARENA #MEDIUM #REPORT #TRANFER #LIFINANCE
(code-423n4/2022-03-lifinance-findings)
Source, LibSwap, Swapper

Number: 5
Problem: msg.value inside the loop
Any native assets in the LiFiDiamond can be took by anyone
Severity: Medium

Proof of Concept:
-Never use msg.value inside a loop.
1) LibSwap.swap use msg.value when swapping native assets.

(bool success, bytes memory res) = _swapData.callTo.call{ value: msg.value }(_swapData.callData);

2) Swapper call LibSwap.swap inside a loop.

for (uint8 i; i < _swapData.length; i++) {
require(
ls.dexWhitelist[_swapData[i].approveTo] == true && ls.dexWhitelist[_swapData[i].callTo] == true,
"Contract call not allowed!"
);

LibSwap.swap(_lifiData.transactionId, _swapData[i]);
}

3) Take GenericSwapFacet as a example
Attacker can call swapTokensGeneric with 1 ether with _swapData.length == 10. This will swap 10 ether to SwapData.callTo.

function swapTokensGeneric(LiFiData memory _lifiData, LibSwap.SwapData[] calldata _swapData) public payable {
uint256 receivingAssetIdBalance = LibAsset.getOwnBalance(_lifiData.receivingAssetId);

// Swap
_executeSwaps(_lifiData, _swapData);

uint256 postSwapBalance = LibAsset.getOwnBalance(_lifiData.receivingAssetId) - receivingAssetIdBalance;

LibAsset.transferAsset(_lifiData.receivingAssetId, payable(msg.sender), postSwapBalance);

Mitigation
:
The amount of native assets to be sent should be specified in _swapData. Check that total amount <= msg.value.

#CODE4ARENA #MEDIUM #REPORT #MSGVALUE #LIFINANCE
(code-423n4/2022-03-lifinance-findings)
Source, Code

Number: 6
Problem: No handling external call with msg.value
CBridgeFacet is not sending native asset to CBridge
Severity: Medium

Impact: _startBridge will always fail when sending native token.

Proof of Concept:
1. CBridgeFacet:_startBridge is not sending native asset will calling sendNative
ICBridge(bridge).sendNative(

2. sendNative will revert when msg.value != _amount

Mitigation:
ICBridge(bridge).sendNative{value: msg.value}(

#CODE4ARENA #MEDIUM #REPORT #MSGVALUE #LIFINANCE
(code-423n4/2022-03-lifinance-findings)
Source, Code

Number: 7
Problem: Return while loop
DexManagerFacet: batchRemoveDex() removes first dex only
Severity: Medium

Impact:
The function intends to allow the removal of multiple dexes approved for swaps. However, the function will only remove the first DEX because return is used instead of break in the inner for loop.

if (s.dexs[j] == _dexs[i]) {
_removeDex(j);
// should be replaced with break;
return;
}
This error is likely to have gone unnoticed because no event is emitted when a DEX is added or removed.

Mitigation: Replace return with break.
if (s.dexs[j] == _dexs[i]) {
_removeDex(j);
break; (will end the current inner loop)
}
In addition, it is recommended to emit an event whenever a DEX is added or removed.

#CODE4ARENA #MEDIUM #REPORT #LOOP #LIFINANCE
(code-423n4/2022-03-lifinance-findings)
Source, Code

Number: 8
Problem: Loss the msg.value
ERC20 bridging functions do not revert on non-zero msg.value
Severity: Medium

Impact:
Any native funds mistakenly sent along with plain ERC20 bridging calls will be lost. AnyswapFacet, CBridgeFacet, HopFacet and NXTPFacet have this issue.
For instance, swapping function might use native tokens, but the functions whose purpose is bridging solely have no use of native funds, so any mistakenly sent native funds to be frozen on the contract balance.
Placing the severity to be medium as in combination with other issues there is a possibility for user funds to be frozen for an extended period of time (if WithdrawFacet's issue plays out) or even lost (if LibSwap's swap native tokens one also be triggered).
In other words the vulnerability is also a wider attack surface enabler as it can bring in the user funds to the contract balance.
Medium despite the fund loss possibility as the native funds in question here are mistakenly sent only, so the probability is lower compared to direct leakage issues.


#CODE4ARENA #MEDIUM #REPORT #MSGVALUE #LIFINANCE
(code-423n4/2022-03-lifinance-findings)
Source, Code

Number: 9
Problem: Overpaying the msg.value
Caller can lose ETH using the CBridgeFacet
Severity: Medium

Impact: A user wanting to bridge ETH via CBridge could lose some amount of ETH.

Proof of concept:
The function startBridgeTokenViaCBridge checks the amount of ETH transfered with msg.value >= _cBridgeData.amount in case the token address is zero.
If a user accidentally sends more ETH than _cBridgeData.amount, that ETH would be held unaccounted for in the contract and be lost for the user.

Mitigation:
Refactor the check to msg.value == _cBridgeData.amount.

#CODE4ARENA #MEDIUM #REPORT #MSGVALUE #LIFINANCE