DeFiHardhatFoundry
250,000 USDC
View results
Submission Details
Severity: medium
Invalid

Insufficient Chain Differentiation in Permit Signatures Enables Cross-Chain Replay Attacks

Summary

The permitDeposit and permitDeposits functions of ApporveFacet utilizes EIP-2612 permits and EIP-712 for typed structured data hashing and signing. The vulnerability arises when a hard fork occurs, resulting in two parallel chains that retain the same chain ID. In such a scenario, a signature created on the pre-fork chain remains valid on both resulting chains, allowing malicious actors to reuse the signature to transfer funds on both chains.

https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/8c8710df547f7d7c5dd82c5381eb6b34532e4484/protocol/contracts/libraries/Silo/LibSiloPermit.sol#L98C1-L123C6

https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/8c8710df547f7d7c5dd82c5381eb6b34532e4484/protocol/contracts/beanstalk/silo/ApprovalFacet.sol#L118C3-L132C6

https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/8c8710df547f7d7c5dd82c5381eb6b34532e4484/protocol/contracts/beanstalk/silo/ApprovalFacet.sol#L146C3-L158C6

Proof Of Concept

1: Initial Setup on Mainnet:

  • Bob holds tokens worth $1,000 on the Ethereum mainnet.

  • Bob submits a signed permit allowing Eve to spend those tokens on his behalf using the permitDeposits function.

2: Permit Generation:

  • Bob's wallet generates the permit using EIP-712 structured data hashing. The data includes:

    • owner: Bob's address.

    • spender: Eve's address.

    • tokens: Array of token addresses.

    • values: Array of values corresponding to the tokens.

    • nonce: A nonce value specific to Bob's address.

    • deadline: A timestamp until when the permit is valid.

  • The permit data is hashed using _hashTypedDataV4, which includes the chain ID in the domain separator.

3:

  • Bob signs the hash with his private key, producing the signature components v, r, and s.

4: Permit Submission:

  • Eve submits the permit to the either the permitDeposit or PermitDeposits on the Ethereum mainnet using the permitDeposits function. The contract verifies the signature and sets the approval for Eve to transfer Bob's tokens.

5: Hard Fork:

  • The Ethereum mainnet undergoes a hard fork, resulting in two chains (Chain A and Chain B). Both chains retain the same chain ID (1).

6: Post-Fork Exploitation:

  • Since the chain ID is the same, the signature Bob generated before the fork is valid on both Chain A and Chain B.

  • Eve can submit the same permit on both Chain A and Chain B, allowing her to transfer Bob's tokens on both chains.

Impact

The primary impact of this vulnerability is that any signed permit allowing a spender to transfer tokens on behalf of an owner before the fork can be reused on both chains post-fork. This can lead to unintended double-spending or unauthorized transfers on one of the chains, compromising the security and integrity of the system.

Tools Used

Manual review

Recommendations

1: Replace LEGACY_CHAIN_ID with Dynamic Chain ID: Instead of using a constant LEGACY_CHAIN_ID, use the current chain's ID:

function getChainId() internal view returns (uint256) {
return block.chainid;
}

2: Add a Unique Network Identifier: Introduce a unique identifier for each network that's set at contract deployment:

bytes32 private immutable NETWORK_IDENTIFIER;
constructor(bytes32 _networkIdentifier) {
NETWORK_IDENTIFIER = _networkIdentifier;
}

3: Modify _domainSeparatorV4(): Update the function to include all these elements:

function _domainSeparatorV4() internal view returns (bytes32) {
return keccak256(
abi.encode(
EIP712_TYPE_HASH,
MIGRATION_HASHED_NAME,
MIGRATION_HASHED_VERSION,
block.chainid,
address(this),
NETWORK_IDENTIFIER
)
);
}

4: Consider Using OpenZeppelin's EIP712 Implementation: OpenZeppelin provides a well-tested implementation of EIP712 that includes many best practices:

import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

Replay attack in case of hard fork - Hardcoded chainId 712

lordofterra Submitter
11 months ago

Appeal created

inallhonesty Lead Judge
11 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

Replay attack in case of hard fork - Hardcoded chainId 712

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.