Platform: Code4rena
Start Date: 08/09/2023
Pot Size: $70,000 USDC
Total HM: 8
Participants: 84
Period: 6 days
Judge: gzeon
Total Solo HM: 2
Id: 285
League: ETH
Rank: 15/84
Findings: 2
Award: $870.10
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: bin2chen
Also found by: castle_chain, maanas, merlin, mert_eren, nobody2018
857.3144 USDC - $857.31
centrifuge/blob/main/src/gateway/routers/axelar/Router.sol#L44
AxelarRouter.execute has the onlyCentrifugeChainOrigin modifier, which assumes msg.sender
is axelarGateway. This is wrong because axelarGateway never does this. msg.sender
is the relayer from Axelar. Therefore, this function will revert due to the check in onlyCentrifugeChainOrigin, which has two impacts:
According to axelar's docs, we can understand that cross-chain messages need to go through 3 phrases (6 steps). Let's see how messages are processed:
callContract
(or callContractWithToken
) function on the Axelar Gateway contract to initiate a call. Once the call is initiated, the user can see its status at [https://axelarscan.io/gmp/txHash] or programmatically track it via the AxelarJS SDK.In Axelar Gateway, each message will calculated as a hash value via keccak256
, which represents the stored slot. If the value of this slot is true, it means that the message can be executed. The 5th step is to set the slot corresponding to the message to true. Next, the executor service from Axelar (commonly known as relayer) calls the execute
method of the target contract of the message. In this case, it's Router.execute.
File: src\gateway\routers\axelar\Router.sol 73: function execute( 74: bytes32 commandId, 75: string calldata sourceChain, 76: string calldata sourceAddress, 77: bytes calldata payload 78:-> ) public onlyCentrifugeChainOrigin(sourceChain, sourceAddress) { 79: bytes32 payloadHash = keccak256(payload); 80: require( 81: axelarGateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash), 82: "Router/not-approved-by-gateway" 83: ); 84: 85: gateway.handle(payload); 86: } 43: modifier onlyCentrifugeChainOrigin(string calldata sourceChain, string calldata sourceAddress) { 44:-> require(msg.sender == address(axelarGateway), "AxelarRouter/invalid-origin"); ...... 54: }
So, at L44, tx will revert since msg.sender
is not axelarGateway.
L80-83, This implementation method has guaranteed the validity of the message. axelarGateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash)
will internally check whether the slot corresponding to the message is true. If it is true, set it to false and return the original bool.
Manual Review
Remove the check at L44.
DoS
#0 - c4-pre-sort
2023-09-15T16:55:35Z
raymondfam marked the issue as low quality report
#1 - c4-pre-sort
2023-09-15T16:56:03Z
raymondfam marked the issue as duplicate of #26
#2 - c4-judge
2023-09-26T16:14:51Z
gzeon-c4 marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2023-09-26T16:16:22Z
gzeon-c4 marked the issue as not a duplicate
#4 - c4-judge
2023-09-26T16:16:32Z
gzeon-c4 marked the issue as duplicate of #537
#5 - c4-judge
2023-09-26T18:12:38Z
gzeon-c4 marked the issue as satisfactory
#6 - c4-judge
2023-09-26T18:35:26Z
gzeon-c4 changed the severity to 2 (Med Risk)
🌟 Selected for report: castle_chain
Also found by: 0xAadi, 0xHelium, 0xLook, 0xblackskull, 0xfuje, 0xmystery, 0xnev, 0xpiken, 7ashraf, BARW, Bauchibred, Bughunter101, Ch_301, JP_Courses, Kaysoft, Krace, MohammedRizwan, SanketKogekar, Sathish9098, alexzoid, ast3ros, btk, catellatech, degensec, fatherOfBlocks, grearlake, imtybik, jkoppel, jolah1, klau5, lsaudit, m_Rassska, merlin, mrudenko, nobody2018, rokinot, rvierdiiev, sandy
12.7917 USDC - $12.79
https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/InvestmentManager.sol#L291
Users can request decreasing the outstanding deposit orders via decreaseDepositRequest which will send a cross-chain message to the Centrifuge chain. After the message is processed, Centrifuge's bot will decrease the outstanding deposit orders and send another cross-chain message back to the source chain to notify that the request has been processed. This will eventually call handleExecutedDecreaseInvestOrder, which internally transfers the asset from escrow
to user
via asset.transferFrom
. Assets can be tokens with blacklist mechanisms such as USDC and USDT. If the user is blacklisted, it will cause USDC.transferFrom
revert. Therefore, the message cannot be successfully processed, which means that the user cannot obtain the decreased asset, resulting in a loss of funds.
When a message of type Call.ExecutedDecreaseInvestOrder
is executed by relayer, the whole flow is as follows:
Router.execute //src\gateway\routers\axelar\Router.sol gateway.handle investmentManager.handleExecutedDecreaseInvestOrder ......some checks skipped SafeTransferLib.safeTransferFrom(_currency, address(escrow), user, currencyPayout)
Let's take a look at the code of handleExecutedDecreaseInvestOrder
:
File: src\InvestmentManager.sol 277: function handleExecutedDecreaseInvestOrder( 278: uint64 poolId, 279: bytes16 trancheId, 280: address user, 281: uint128 currency, 282: uint128 currencyPayout 283: ) public onlyGateway { 284: require(currencyPayout != 0, "InvestmentManager/zero-payout"); 285: 286: address _currency = poolManager.currencyIdToAddress(currency); 287: address liquidityPool = poolManager.getLiquidityPool(poolId, trancheId, _currency); 288: require(liquidityPool != address(0), "InvestmentManager/tranche-does-not-exist"); 289: require(_currency == LiquidityPoolLike(liquidityPool).asset(), "InvestmentManager/not-tranche-currency"); 290: 291:-> SafeTransferLib.safeTransferFrom(_currency, address(escrow), user, currencyPayout); 292: }
L291, if _currency
is USDC or USDT and user
is blacklisted, tx will revert.
Manual Review
It is recommended not to transfer the asset directly to the user, but to record the amount of the asset and provide the claim
function to allow the user to specify the receiver.
DoS
#0 - c4-pre-sort
2023-09-15T20:40:26Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-09-15T20:41:01Z
raymondfam marked the issue as duplicate of #32
#2 - c4-judge
2023-09-25T14:22:15Z
gzeon-c4 changed the severity to QA (Quality Assurance)
#3 - c4-judge
2023-09-26T18:25:46Z
gzeon-c4 marked the issue as grade-c
#4 - c4-judge
2023-09-29T11:55:22Z
gzeon-c4 marked the issue as grade-b