Skip to content

Commit ac297da

Browse files
authored
fix(cctp): prevent self-sender in postDispatch (#8519)
1 parent 7eb690c commit ac297da

3 files changed

Lines changed: 30 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hyperlane-xyz/core': minor
3+
---
4+
5+
Added a sender check in CctpBase._postDispatch to prevent misuse when called via transferRemote.

solidity/contracts/token/TokenBridgeCctpBase.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ abstract contract TokenBridgeCctpBase is
6666
error InvalidMintAmount();
6767
error InvalidMintRecipient();
6868
error InvalidMessageId();
69+
error InvalidPostDispatchSender();
6970

7071
uint256 private constant _SCALE = 1;
7172

@@ -345,6 +346,13 @@ abstract contract TokenBridgeCctpBase is
345346
bytes calldata metadata,
346347
bytes calldata message
347348
) internal override {
349+
// Prevent backrunning transferRemote with postDispatch for the same message.
350+
// transferRemote dispatches with sender == address(this). If an attacker
351+
// backruns it with postDispatch, a second Circle "hook" message is created
352+
// that can set isVerified[messageId] = true on the destination without
353+
// executing the Circle mint, permanently stranding the user's burned funds.
354+
if (message.senderAddress() == address(this))
355+
revert InvalidPostDispatchSender();
348356
bytes32 id = message.id();
349357
if (!_isLatestDispatched(id)) revert MessageNotDispatched();
350358

solidity/test/token/TokenBridgeCctp.t.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,23 @@ contract TokenBridgeCctpV1Test is Test {
792792
tbOrigin.postDispatch(bytes(""), message);
793793
}
794794

795+
function test_postDispatch_revertsWhen_senderIsThis(
796+
bytes32 recipient,
797+
bytes calldata body
798+
) public {
799+
bytes memory message = Message.formatMessage(
800+
3,
801+
0,
802+
origin,
803+
address(tbOrigin).addressToBytes32(),
804+
destination,
805+
recipient,
806+
body
807+
);
808+
vm.expectRevert(TokenBridgeCctpBase.InvalidPostDispatchSender.selector);
809+
tbOrigin.postDispatch(bytes(""), message);
810+
}
811+
795812
function test_hookType() public {
796813
assertEq(tbOrigin.hookType(), uint8(IPostDispatchHook.HookTypes.CCTP));
797814
}

0 commit comments

Comments
 (0)