Platform: Code4rena
Start Date: 08/06/2022
Pot Size: $115,000 USDC
Total HM: 26
Participants: 72
Period: 11 days
Judge: leastwood
Total Solo HM: 14
Id: 132
League: ETH
Rank: 4/72
Findings: 7
Award: $4,690.98
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0x52
Also found by: csanuragjain
A approved realyer can steal from sponsor vault by self initiating transaction via xcall with little to no relayer fees. Then Relayer can himself execute the transaction and claim the fees which he gave and also from the sponsor vault. Thus stealing from sponsor vault
Relayer makes xcall using destination domain as his own or some other where he is the approved relayer and also have some sponsor vault
Since relayer fees on this transaction is very low so no relayer would be interested in taking this transaction.
The malicious Relayer simply execute his own transaction due to non competetion and also in process obtains the fees from sponsor vault for his own transaction
There should be a min cap for relayer fees
#0 - LayneHaber
2022-06-24T16:33:26Z
Duplicate of #234
🌟 Selected for report: GimelSec
Also found by: Czar102, Lambda, csanuragjain, minhquanym, shenwilly
The rescindDiamondCut function will set acceptanceTimes of _diamondCut to 0 which is incorrect. This will simply bypass the delay at LibDiamond.sol#L101
uint256 acceptance = block.timestamp + _delay; diamondStorage().acceptanceTimes[keccak256(abi.encode(_diamondCut))] = acceptance;
Ideally Admin has to wait till block.timestamp + _delay to execute this Diamond cut
But this can be bypaased
Admin can simply call rescindDiamondCut function which sets
diamondStorage().acceptanceTimes[keccak256(abi.encode(_diamondCut))] = 0;
require( diamondStorage().acceptanceTimes[keccak256(abi.encode(_diamondCut))] < block.timestamp, "LibDiamond: delay not elapsed" );
Revise the condition at LibDiamond.sol#L100 to
require( diamondStorage().acceptanceTimes[keccak256(abi.encode(_diamondCut))] != 0 && diamondStorage().acceptanceTimes[keccak256(abi.encode(_diamondCut))] < block.timestamp, "LibDiamond: delay not elapsed" );
#0 - LayneHaber
2022-06-26T01:15:47Z
Duplicate of #215
🌟 Selected for report: Chom
Also found by: csanuragjain
In case the asset id used in the xcall takes transfer fees while transferring then execution will fail causing DOS
User A makes an xcall with an asset id taking transfer fees
Relayer executes this transaction
s.sponsorVault.reimburseLiquidityFees at BridgeFacet.sol#L836 will initiate reimburseLiquidityFees function on SponsorVault.sol#L196
IERC20(_token).safeTransfer(msg.sender, sponsoredFee); will transfer the sponsoredFee to BridgeFacet.sol
Since _token has a transfer fees so amount transferred to BridgeFacet.sol will be sponsoredFee-delta
This becomes a problem at BridgeFacet.sol#L839 which will fail since (IERC20(_asset).balanceOf(address(this)) != starting + sponsored) because of the transfer fees
Hence fast execution will not be possible on this transaction
Sponsored vault should return the actual amount which is transferred in the sponsored variable
#0 - LayneHaber
2022-06-26T00:58:56Z
Duplicate of #196
🌟 Selected for report: xiaoming90
Also found by: csanuragjain
1169.1572 USDC - $1,169.16
Relayer and Router can combine to steal funds by taking over most incoming transactions
Relayer checks the mempool for any user who has called xcall function
Once relayer gets a transaction with heavy amount, relayer can note all args passed in the xcall by the user
Now assuming both Relayer and Router are working together on this fraud, Relayer already has the Router signature
Relayer can simply use execute function to complete this transaction using the args obtained in frontrun which will result in same transaction id.
This bypasses the Sequencer bid and give unfair advantage to this Relayer
Relayer can gain all the fees now once user transaction arrives via nomad and can share his fees profit between himself and router. All other relayers will be disadvantaged
Keep a tab on total number of transactions relayer is able to execute. If only certain relayer are executing most transactions, probably check if above issue is not happening
#0 - jakekidd
2022-06-25T00:47:59Z
This is 100% correct and is also largely why we have relayer whitelist. In the current design, there is a trusted partnership between the network and the relayer that they will only accept requests from the sequencer agent.
This is a Low Risk / QA issue, since there's no losses - it would just grief the router network of some potential returns.
EDIT: Removing disagree with severity tag as level 2 / Med Risk seems appropriate in hindsight.
#1 - 0xleastwood
2022-08-15T08:18:49Z
Duplicate of #147.
🌟 Selected for report: SmartSek
Also found by: csanuragjain, hansfriese
https://github.com/code-423n4/2022-06-connext/blob/main/contracts/contracts/core/connext/facets/StableSwapFacet.sol#L279 https://github.com/code-423n4/2022-06-connext/blob/main/contracts/contracts/core/connext/facets/StableSwapFacet.sol#L305
Even when contract is in pause state both swapExactOut and addSwapLiquidity can still be called due to missing modifier check whenNotPaused
Observe the swapExactOut function
Observe that the function is missing whenNotPaused modifier
This means that swapping will be possible even when Admin has kept the contract in paused state (due to some emergency). This is incorrect behaviour
Add modifier whenNotPaused for both swapExactOut and addSwapLiquidity function
function swapExactOut( bytes32 canonicalId, uint256 amountOut, address assetIn, address assetOut, uint256 maxAmountIn, uint256 deadline ) external payable nonReentrant deadlineCheck(deadline) whenNotPaused returns (uint256)
#0 - ecmendenhall
2022-06-20T15:54:41Z
Duplicate of #175
#1 - jakekidd
2022-06-26T18:37:54Z
🌟 Selected for report: BowTiedWardens
Also found by: 0x1f8b, 0x29A, 0x52, 0xNazgul, 0xNineDec, 0xf15ers, 0xkatana, 0xmint, Chom, ElKu, Funen, IllIllI, JMukesh, Jujic, Kaiziron, Lambda, MiloTruck, Ruhum, SmartSek, SooYa, TerrierLover, TomJ, WatchPug, Waze, _Adam, asutorufos, auditor0517, bardamu, c3phas, catchup, cccz, ch13fd357r0y3r, cloudjunky, cmichel, cryptphi, csanuragjain, defsec, fatherOfBlocks, hansfriese, hyh, jayjonah8, joestakey, k, kenta, obtarian, oyc_109, robee, sach1r0, shenwilly, simon135, slywaters, sorrynotsorry, tintin, unforgiven, xiaoming90, zzzitron
141.8552 USDC - $141.86
Oracle returns Chainlink latestRoundData without proper validation.
In getPriceFromChainlink function at ConnextPriceOracle.sol#L122, there is no check to see if returned price of aggregator.latestRoundData() is not stale. More details at https://docs.chain.link/docs/faq/#how-can-i-check-if-the-answer-to-a-round-is-being-carried-over-from-a-previous-round
Modify the function as below:
(uint80 round, int256 latestPrice, , uint256 latestTimestamp, uint80 answeredInRound) = aggregator.latestRoundData(); require(feedPrice > 0, "Chainlink price <= 0"); require(answeredInRound >= round, "Stale price"); require(latestTimestamp != 0, "Round not complete");
#0 - ecmendenhall
2022-06-20T05:30:07Z
Duplicate of #190
#1 - jakekidd
2022-06-24T21:38:23Z
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 0xmint, BowTiedWardens, ElKu, Fitraldys, Funen, Kaiziron, Lambda, Metatron, MiloTruck, Randyyy, Ruhum, SmartSek, TomJ, Tomio, UnusualTurtle, Waze, _Adam, apostle0x01, asutorufos, c3phas, catchup, csanuragjain, defsec, fatherOfBlocks, hansfriese, hyh, ignacio, joestakey, k, kaden, nahnah, oyc_109, rfa, robee, sach1r0, simon135, slywaters
84.4604 USDC - $84.46
Function swapFromLocalAssetIfNeededForExactOut: If _amount is 0 then no need to perform any other step in swapFromLocalAssetIfNeededForExactOut function
if (_amount == 0) { return (true, _amount, _asset); }
_setOwner: This function can be defined as private instead of internal
#0 - 0xleastwood
2022-08-16T20:04:08Z
The first optimisation doesn't seem to be possible because this code path is not hit unless _amount != 0
. See its use in _reconcileProcessPortal
for context on why.
The second optimisation is minor.