Temporary denial-of-service when in-flight messages are not executed before a deprecated Wormhole Guardian set expires
Wormhole exposes a governance action in `Governance::submitNewGuardianSet` to update the Guardian set via Governance VAA.\\n```\\nfunction submitNewGuardianSet(bytes memory _vm) public {\\n    // rest of code\\n\\n    // Trigger a time-based expiry of current guardianSet\\n    expireGuardianSet(getCurrentGuardianSetIndex());\\n\\n    // Add the new guardianSet to guardianSets\\n    storeGuardianSet(upgrade.newGuardianSet, upgrade.newGuardianSetIndex);\\n\\n    // Makes the new guardianSet effective\\n    updateGuardianSetIndex(upgrade.newGuardianSetIndex);\\n}\\n```\\n\\nWhen this function is called, `Setters:: expireGuardianSet` initiates a 24-hour timeframe after which the current guardian set expires.\\n```\\nfunction expireGuardianSet(uint32 index) internal {\\n    _state.guardianSets[index].expirationTime = uint32(block.timestamp) + 86400;\\n}\\n```\\n\\nHence, any in-flight VAAs that utilize the deprecated Guardian set index will fail to be executed given the validation present in `Messages::verifyVMInternal`.\\n```\\n/// @dev Checks if VM guardian set index matches the current index (unless the current set is expired).\\nif(vm.guardianSetIndex != getCurrentGuardianSetIndex() && guardianSet.expirationTime < block.timestamp){\\n    return (false, "guardian set has expired");\\n}\\n```\\n\\nConsidering there is no automatic relaying of Wormhole CCTP messages, counter to what is specified in the documentation (unless an integrator implements their own relayer), there are no guarantees that an in-flight message which utilizes an old Guardian set index will be executed by the `mintRecipient` on the target domain within its 24-hour expiration period. This could occur, for example, in cases such as:\\nIntegrator messages are blocked by their use of the Wormhole nonce/sequence number.\\nCCTP contracts are paused on the target domain, causing all redemptions to revert.\\nL2 sequencer downtime, since the Wormhole CCTP integration contracts do not consider aliased addresses for forced inclusion.\\nThe `mintRecipient` is a contract that has been paused following an exploit, temporarily restricting all incoming and outgoing transfers.\\nIn the current design, it is not possible to update the `mintRecipient` for a given deposit due to the multicast nature of VAAs. CCTP exposes `MessageTransmitter::replaceMessage` which allows the original source caller to update the destination caller for a given message and its corresponding attestation; however, the Wormhole CCTP integration currently provides no access to this function and has no similar functionality of its own to allow updates to the target `mintRecipient` of the VAA.\\nAdditionally, there is no method for forcibly executing the redemption of USDC/EURC to the `mintRecipient`, which is the only address allowed to execute the VAA on the target domain, as validated in `Logic::redeemTokensWithPayload`.\\n```\\n// Confirm that the caller is the `mintRecipient` to ensure atomic execution.\\nrequire(\\n    msg.sender.toUniversalAddress() == deposit.mintRecipient, "caller must be mintRecipient"\\n);\\n```\\n\\nWithout any programmatic method for replacing expired VAAs with new VAAs signed by the updated Guardian set, the source USDC/EURC will be burnt, but it will not be possible for the expired VAAs to be executed, leading to denial-of-service on the `mintRecipient` receiving tokens on the target domain. The Wormhole CCTP integration does, however, inherit some mitigations already in place for this type of scenario where the Guardian set is updated, as explained in the Wormhole whitepaper, meaning that it is possible to repair or otherwise replace the expired VAA for execution using signatures from the new Guardian set. In all cases, the original VAA metadata remains intact since the new VAA Guardian signatures refer to an event that has already been emitted, so none of the contents of the VAA payload besides the Guardian set index and associated signatures change on re-observation. This means that the new VAA can be safely paired with the existing Circle attestation for execution on the target domain by the original `mintRecipient`.
The practicality of executing the proposed Governance mitigations at scale should be carefully considered, given the extent to which USDC is entrenched within the wider DeFi ecosystem. There is a high likelihood of temporary widespread, high-impact DoS, although this is somewhat limited by the understanding that Guardian set updates are expected to occur relatively infrequently, given there have only been three updates in the lifetime of Wormhole so far. There is also potentially insufficient tooling for the detailed VAA re-observation scenarios, which should handle the recombination of the signed CCTP message with the new VAA and clearly communicate these considerations to integrators.
There is only a single address that is permitted to execute a given VAA on the target domain; however, there are several scenarios that have been identified where this `mintReceipient` may be unable to perform redemption for a period in excess of 24 hours following an update to the Guardian set while the VAA is in-flight. Fortunately, Wormhole Governance has a well-defined path to resolution, so the impact is limited.\\nProof of Concept:\\nAlice burns 100 USDC to be transferred to dApp X from CCTP Domain A to CCTP Domain B.\\nWormhole executes a Governance VAA to update the Guardian set.\\n24 hours pass, causing the previous Guardian set to expire.\\ndApp X attempts to redeem 100 USDC on CCTP Domain B, but VAA verification fails because the message was signed using the expired Guardian set.\\nThe 100 USDC remains burnt and cannot be minted on the target domain by executing the attested CCTP message until the expired VAA is reobserved by members of the new Guardian set.
```\\nfunction submitNewGuardianSet(bytes memory _vm) public {\\n    // rest of code\\n\\n    // Trigger a time-based expiry of current guardianSet\\n    expireGuardianSet(getCurrentGuardianSetIndex());\\n\\n    // Add the new guardianSet to guardianSets\\n    storeGuardianSet(upgrade.newGuardianSet, upgrade.newGuardianSetIndex);\\n\\n    // Makes the new guardianSet effective\\n    updateGuardianSetIndex(upgrade.newGuardianSetIndex);\\n}\\n```\\n