SKALE contest - cmichel's results

The only Ethereum native multichain scaling network.

General Information

Platform: Code4rena

Start Date: 18/02/2022

Pot Size: $125,000 USDC

Total HM: 13

Participants: 24

Period: 14 days

Judge: GalloDaSballo

Total Solo HM: 6

Id: 88

League: ETH

SKALE

Findings Distribution

Researcher Performance

Rank: 1/24

Findings: 6

Award: $31,523.27

๐ŸŒŸ Selected for report: 2

๐Ÿš€ Solo Findings: 2

Findings Information

๐ŸŒŸ Selected for report: cmichel

Labels

bug
3 (High Risk)
sponsor disputed

Awards

19978.213 USDC - $19,978.21

External Links

Lines of code

https://github.com/skalenetwork/ima-c4-audit/blob/11d6a6ae5bf16af552edd75183791375e501915f/contracts/schain/MessageProxyForSchain.sol#L223

Vulnerability details

Impact

The postIncomingMessages function calls _callReceiverContract(fromChainHash, messages[i], startingCounter + 1) which gives control to a contract that is potentially attacker controlled before updating the incomingMessageCounter.

for (uint256 i = 0; i < messages.length; i++) {
    // @audit re-entrant, can submit same postIncomingMessages again
    _callReceiverContract(fromChainHash, messages[i], startingCounter + 1);
}
connectedChains[fromChainHash].incomingMessageCounter += messages.length;

The attacker can re-enter into the postIncomingMessages function and submit the same messages again, creating a replay attack. Note that the startingCounter is the way how messages are prevented from replay attacks here, there are no further nonces.

POC

Attacker can submit two cross-chain messages to be executed:

  1. Transfer 1000 USDC
  2. A call to their attacker-controlled contract, could be masked as a token contract that allows re-entrance on transfer.

Some node submits the postIncomingMessages(params) transaction, transfers 1000 USDC, then calls the attackers contract, who can themself call postIncomingMessages(params) again, receive 1000 USDC a second time, and stop the recursion.

Add a messageInProgressLocker modifier to postIncomingMessages as was done in MessageProxyForMainnet.

#0 - GalloDaSballo

2022-05-08T15:16:56Z

@cstrangedk It seems like the https://github.com/skalenetwork/ima-c4-audit/blob/11d6a6ae5bf16af552edd75183791375e501915f/contracts/schain/bls/SkaleVerifier.sol#L45 is a view function (no state change), as such how would the verify function be able to prove that a signature wasn't reused in the same tx, and in the same block?

I believe that as long as no state change has happened (verify -> _callReceiverContract -> reEntrancy -> verify), you still would be able to us the same signature as messages.length (and no other storage variable) was changed.

What do you think?

#1 - GalloDaSballo

2022-06-01T16:06:25Z

After a conversation with the sponsor we agreed that the codebase in the contest is slightly different from the one they have internally.

As such the reEntrancy is exploitable in this version of the code. Because one signature allows to replay the message multiple times, leading to draining of funds potentially, I agree that High Severity is appropriate

#2 - GalloDaSballo

2022-06-01T16:06:52Z

The Sponsor has mitigated as of the start of the contest, however the code with the vulnerability made it's way into the repo in scope

#3 - cstrangedk

2022-11-11T16:31:11Z

Findings Information

๐ŸŒŸ Selected for report: WatchPug

Also found by: cmichel

Labels

bug
duplicate
2 (Med Risk)
disagree with severity

Awards

2697.0588 USDC - $2,697.06

External Links

Lines of code

https://github.com/skalenetwork/ima-c4-audit/blob/11d6a6ae5bf16af552edd75183791375e501915f/contracts/MessageProxy.sol#L316

Vulnerability details

Impact

A connected chain can be removed which will make all pending messages fail. If the chain is reinitialized again at some point, its incomingMessageCounter will start at zero and allow replay attacks of all previous messages.

function removeConnectedChain(string memory schainName) public virtual override onlyChainConnector {
    
    bytes32 schainHash = keccak256(abi.encodePacked(schainName));
    require(connectedChains[schainHash].inited, "Chain is not initialized");
    delete connectedChains[schainHash];
}

function _addConnectedChain(bytes32 schainHash) internal onlyChainConnector {
    require(!connectedChains[schainHash].inited,"Chain is already connected");
    connectedChains[schainHash] = ConnectedChainInfo({
        // @audit counters reset to zero
        incomingMessageCounter: 0,
        outgoingMessageCounter: 0,
        inited: true
    });
}

Not sure if a chain should ever be removed. If it's removed once, it should be permanently disabled.

#0 - DimaStebaev

2022-03-10T15:25:19Z

This one duplicates #57.

#1 - GalloDaSballo

2022-06-01T16:04:49Z

Dup of #57

Findings Information

๐ŸŒŸ Selected for report: jayjonah8

Also found by: IllIllI, cmichel

Labels

bug
duplicate
2 (Med Risk)
disagree with severity
sponsor disputed

Awards

1618.2353 USDC - $1,618.24

External Links

Lines of code

https://github.com/skalenetwork/ima-c4-audit/blob/11d6a6ae5bf16af552edd75183791375e501915f/contracts/mainnet/DepositBoxes/DepositBoxERC20.sol#L120

Vulnerability details

Impact

Some mainnet tokens (like USDT) don't correctly implement the EIP20 standard and their transfer/transferFrom function return void instead of a success boolean. Calling these functions with the correct EIP20 function signatures will always revert.

Non-safe transfers are used in:

  • DepositBoxERC20.depositERC20

Tokens that don't correctly implement the latest EIP20 spec, like USDT, will be unusable in the protocol as they revert the transaction because of the missing return value.

For the mainnet contracts, we recommend using OpenZeppelinโ€™s SafeERC20 versions with the safeTransfer and safeTransferFrom functions that handle the return value check as well as non-standard-compliant tokens.

#0 - cstrangedk

2022-03-03T22:14:41Z

Reference/duplicate #8, #9, #10, and #12.Disputed as SKALE Chain owner can customize depositbox contract using custom messaging to use safeTransfer functions, if needed.

#1 - GalloDaSballo

2022-06-01T18:18:17Z

Dup of #8

Findings Information

๐ŸŒŸ Selected for report: 0x1f8b

Also found by: IllIllI, cmichel, gzeon, kirk-baird

Labels

bug
duplicate
2 (Med Risk)
sponsor disputed

Awards

786.4623 USDC - $786.46

External Links

Lines of code

https://github.com/skalenetwork/ima-c4-audit/blob/11d6a6ae5bf16af552edd75183791375e501915f/contracts/mainnet/DepositBoxes/DepositBoxERC20.sol#L120

Vulnerability details

Impact

Some ERC20 tokens make modifications to their ERC20's transfer or balanceOf functions. One type of these tokens is deflationary tokens that charge a certain fee for every transfer() or transferFrom(). Others are rebasing tokens that increase in value over time like Aave's aTokens (balanceOf changes over time).

The schains deploy a mirror standard ERC20 contract for all token transfers from other chains. This standard contract does not have these transfer modifications which leads to issues:

  • Fee-on-transfer mainnet token transfers are reported with the pre-fee amount. It deploys a standard ERC20 chain on the receiving chain and mints this pre-fee amount. When trying to retrieve the pre-fee amount by doing the reverse cross-chain transfer, the contract on the receiving side will be unable to pay out the pre-fee amount as its balance is the post-fee amount.

It seems like the issue with fee-on-transfer tokens could be prevented by only minting the post-fee amount on the receiving chain. Consider computing the actual received token amounts from the transferFrom call and using this amount in the messageProxy.postOutgoingMessage call.

A similar issue exists on schains where no clone ERC20 is deployed but the mainnet equivalent token is used which could be a fee-on-transfer token.

#0 - cstrangedk

2022-03-03T22:34:19Z

Again, for any non-standard token standard, the SKALE Chain owner is free to create custom depositbox and tokenManager contracts to extend the functionality to other token types and manually map any customized ERCXX token logic instead of using automaticdeployed Erc20OnChain contracts. This is all covered in docs linked to the audit readme https://docs.skale.network/ima/1.2.x/managing-erc20#_1_reviewmodify_the_token_contract

#1 - GalloDaSballo

2022-06-01T16:50:25Z

Dup of #50

Findings Information

๐ŸŒŸ Selected for report: cmichel

Labels

bug
2 (Med Risk)
sponsor disputed

Awards

5993.4639 USDC - $5,993.46

External Links

Lines of code

https://github.com/skalenetwork/ima-c4-audit/blob/11d6a6ae5bf16af552edd75183791375e501915f/contracts/schain/tokens/ERC721OnChain.sol#L73

Vulnerability details

Impact

In the ERC721OnChain implementation the token owner can set the token's URI using setTokenURI. Usually, this is token URI points to data defining the NFT (attributes, images, etc.). It's usually set by the contract owner. A user that owns an NFT can just spoof any other NFT data by changing the token URI to any of the other NFTs.

Disallow the owner of an NFT to change its token URI

#0 - DimaStebaev

2022-03-11T10:36:29Z

ERC721OnChain is a default implementation. If the token is sensitive to URI change SKALE chain owner can use another one. Not all ERC721 require that URI can't be changed.

#1 - GalloDaSballo

2022-06-01T18:26:40Z

Because the argument for this being a setting can be made I am excluding a high severity.

However the code was brought into scope, the implementation under scrutiny does allow the owner to change the URI which is a known admin privilege.

For those reasons I believe the finding to be valid and of medium severity

Findings Information

๐ŸŒŸ Selected for report: defsec

Also found by: 0x1f8b, 0xwags, cmichel, csanuragjain, gzeon, jayjonah8, kenta, kirk-baird, kyliek, leastwood, rfa, robee, ye0lde

Labels

bug
QA (Quality Assurance)
disagree with severity

Awards

449.8419 USDC - $449.84

External Links

Lines of code

https://github.com/skalenetwork/ima-c4-audit/blob/11d6a6ae5bf16af552edd75183791375e501915f/contracts/schain/MessageProxyForSchain.sol#L223 https://github.com/skalenetwork/ima-c4-audit/blob/11d6a6ae5bf16af552edd75183791375e501915f/contracts/MessageProxy.sol#L425

Vulnerability details

Impact

The postIncomingMessages function iterates over all messages and calls _callReceiverContract with a counter value of startingCounter + 1 for all of them. It should be startingCounter + i.

If a call fails the PostMessageError event is emitted with startingCounter + 1 instead of the actual counter. I'm not sure how the nodes handle failed messages but I assume they would undo/refund the original transaction based on these PostMessageError events that use the counter to identify a message.

This could then lead to loss of funds. Imagine I post 2 messages:

  1. Something that always reverts
  2. Transfer of 1000 USDC

The first message will revert but emit that the second message reverted. I receive the 1000 USDC on the receiving chain and also get a refund of 1000 USDC, stealing funds.

- _callReceiverContract(fromChainHash, messages[i], startingCounter + 1);
+ _callReceiverContract(fromChainHash, messages[i], startingCounter + i);

#0 - cstrangedk

2022-03-03T22:56:18Z

Acknowledged this is a bug, but no stealing of funds are possible as this only relates to PostMessageError event emitted with the wrong counter. Suggest: 0 (Non-critical)

#1 - GalloDaSballo

2022-06-01T16:10:15Z

@cstrangedk it seems like the warden could: PostMessage([transferUSDCMessage, revertMessage])

Which will cause the first message to go through and the second one to trigger a Error Event (no revert as you have try/catch)

Are the events used to generate messages, or is you disagreement because the events are not used for any validator / message posting logic ?

#2 - cstrangedk

2022-06-01T19:39:41Z

@GalloDaSballo event error has no impact, it simply emits an error with no further logic/consequence.

#3 - GalloDaSballo

2022-06-01T20:02:30Z

Given the context given by the sponsor, the finding is valid and informational in nature, as such I'll downgrade to non-critical

#4 - JeeberC4

2022-06-01T20:45:51Z

Creating QA Report for warden as judge has downgraded. Preserving original title: Wrong counter in PostMessageError events

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax ยฉ 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter