Eywa PortalV2 Axelar ExpressExecute Bypass

On 2026-02-01 18:38:23 UTC (block 24,363,854), tx 0x37d9b911ef710be851a2e08e1cfc61c2544db0f208faeade29ee98cc7506ccc2 on Ethereum called expressExecute on ReceiverAxelar (0xb2185950f5a0a46687ac331916508aada202e063) with sourceChain="berachain" and sourceAddress=0x5eEdDcE72530e4fC96d43E3d70Fe09aD0D037175, bypassing Axelar gateway validation and triggering an EYWA unlock on PortalV2 (0xac8f44ceca92b2a4b30360e5bd3043850a0ffcbe). The ExpressExecuted event is emitted with topic 0x6e18757e81c44a367109cbaa499add16f2ae7168aab9715c3cdc36b0f7ccce92 and commandId=0x5e77d6809707bb0c062a5c82270d7d939c4ad094dc683ccd4738131925cdeb01 in this tx.

Attack vector: cross-chain message validation bypass / access control bypass via AxelarExpressExecutable.expressExecute(bytes32,string,string,bytes) (selector 0x65657636). The function only checks gateway.isCommandExecuted and then calls _execute without validateContractCall (AxelarExpressExecutable.sol L92–108), while ReceiverAxelar._execute only enforces peers[sourceChain] == sourceAddress.toAddress() before forwarding to the Receiver (ReceiverAxelar.sol L51–74). On-chain state shows peers("berachain") equals 0x5eEdDcE7... and ReceiverAxelar has RECEIVER_ROLE on Receiver, so the attacker could spoof a legitimate peer and pass the sole check.

// AxelarExpressExecutable.expressExecute (no gateway validation)
if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted();
emit ExpressExecuted(commandId, sourceChain, sourceAddress, payloadHash, msg.sender);
_setExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash, msg.sender);
_execute(sourceChain, sourceAddress, payload);

// ReceiverAxelar._execute (only peer check then forward)
require(peers[sourceChain] == sourceAddress.toAddress(), "ReceiverAxelar: wrong peer");
IReceiver(receiver).receiveData(sender, uint64(chainIdFrom), payload, requestId);

The payload decodes to receiveData(sender=0xf3792bae7f35dcde2916c6e6a72ccd3a5330d565, chainIdFrom=80094, requestId=0x105b391f...a97c, receivedData=...), and Receiver threshold for (sender,80094) is 1, so Receiver.receiveData immediately executes (Receiver.sol L109–122). In Receiver._execute, the decoded check call is receiveValidatedData(bytes4,bytes32,uint64) and the data call is resume(bytes32,uint8,string[],bytes[],bytes) on the router diamond 0xf3792bae7f35dcde2916c6e6a72ccd3a5330d565 (Receiver.sol L194–203). CoreFacet.receiveValidatedData only checks from == addressBook.routerV3(chainIdFrom) and selector == resume (CoreFacet.sol L92–97), so a forged message with from=routerV3(80094) passes without any Axelar gateway proof.

Call flow (per trace): expressExecute -> ReceiverAxelar._execute -> Receiver.receiveData -> Receiver._execute -> CoreFacet.receiveValidatedData -> CoreFacet.resume -> PortalV2.unlock -> ERC20.transfer. The resume parameters contain operations=["BU"] (burn-unlock) and a SynthParams payload specifying tokenIn=EYWA(0x8cb8c4263eb26b2349d74ea2cb1b27bc40709e12), amountIn=999,887,441.776713115 EYWA, from=0xcda36e1b514fcc52e4ca1238491e6e789a11a8bb, to=0x632400f42e96a5deb547a179ca46b02c22cd25cd, tokenInChainIdFrom=80094. PortalV2’s unlock (PortalV2.sol L78–95) then releases funds because the router diamond is the onlyRouter for chain 1 in AddressBook.

Financial impact: Unlocked event topic 0x9abe13faa6aaae81ab2cd561cc29d1d0fafec53dca1f0d553a759be6f8fb0d74 shows amount=999,887,441.776713115 EYWA from PortalV2 to attacker 0x6324... (tx log data), and ERC20 Transfer events (topic 0xddf252ad...) show 999,787,453.032535444 EYWA to the attacker and 99,988.7441776713 EYWA to treasury 0x4400671b8238b5e0c7c9d7572746d236cd292845 (bridge fee). The loss is borne by PortalV2’s locked EYWA liquidity; affected parties are bridge LPs/users and the protocol’s backing reserves. USD value cannot be derived from on-chain price evidence for this tx (no on-chain USD oracle or major WETH/USDC/USDT pool found for EYWA on Ethereum at this block).

Evidence markers: ExpressExecuted topic 0x6e18757e... with attacker as expressExecutor; Receiver Received/RequestExecuted topics 0x05a8cf3f... and 0xe2a75259... confirm Receiver accepted and executed the request; unlock selector 0xfe4f4b72 is called in the trace; ERC20 Transfer topic 0xddf252ad... shows funds leaving PortalV2.