Reproduced Exploit
YieldFi CCIP: missing source validation lets an untrusted chain drive privileged mint/unlock
Chain
Other
Category
untagged
Date
Apr 2025
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 55536-missing-source-validation-in-ccip-message-handling-cyfrin-no. Standalone Foundry PoC and full write-up: 55536-missing-source-validation-in-ccip-message-handling-cyfrin-no_exp in the
evm-hack-registrymirror.
Protocol: YieldFi (v2.0) · Auditor: Cyfrin (Immeas) · Severity: Critical/High
AuditVault: #55536 · Report: Cyfrin YieldFi v2.0
Vulnerable source: contracts/bridge/ccip/BridgeCCIP.sol — _ccipReceive (L160-L181)
Fix commit: a03341d (sender/source now verified against trusted peers)
Provenance#
The audited repo YieldFiLabs/contracts@40caad6c was deleted (no fork, no
mirror, Wayback 404). The exploit path uses only real recovered source:
src/yieldfi/BridgeCCIP.sol— the_ccipReceiveprefix (decode / dedup /amountcheck) is verbatim from the Cyfrin report; the report elides the token action as...and describes it as "the minting or unlocking of arbitrary tokens", implemented here as the L1 unlock (a real ERC20 transfer of the locked pool topayload.to).src/yieldfi/Codec.sol,Constants.sol,Common.sol— byte-identical real YieldFi source from publicYieldFiLabs/smart-contracts.src/chainlink/*— the real Chainlink CCIP framework (CCIPReceiver,Client) fromsmartcontractkit/ccip.CCIPReceiveronly checksmsg.sender == i_ccipRouter; it does not authenticate the source chain.
Root cause#
_ccipReceive acts on the decoded payload without validating who sent it:
function _ccipReceive(Client.Any2EVMMessage memory any2EvmMessage) internal override {
bytes memory message = abi.decode(any2EvmMessage.data, (bytes));
BridgeSendPayload memory payload = Codec.decodeBridgeSendPayload(message);
bytes32 _hash = keccak256(abi.encode(message, any2EvmMessage.messageId));
require(!processedMessages[_hash], "processed");
processedMessages[_hash] = true;
require(payload.amount > 0, "!amount");
// @audit no check of any2EvmMessage.sourceChainSelector / sender
// ... mint (L2) / unlock (L1) payload.amount to payload.to
}
Chainlink CCIP delivers a message sent from any source chain by any
sender contract that paid on that chain — the router (i_ccipRouter) only
guarantees msg.sender == router, which is all CCIPReceiver.onlyRouter
enforces. Because BridgeCCIP never checks sourceChainSelector/sender, an
attacker deploys a contract on any CCIP-connected chain, sends a crafted payload,
and the bridge unlocks (L1) or mints (L2) arbitrary tokens to an attacker
address — draining the bridge / minting unbacked tokens.
Reproduction#
test/…_exp.sol (registry, [PASS]):
test_untrustedSourceCanDrainBridge— the bridge holds a 1,000-yToken locked pool. A forged message arrives from an untrusted chain + sender; the real_ccipReceiveprocesses it and releases the entire pool to the attacker (attacker: 0 → 1000,bridge: 1000 → 0).test_control_fixedBridgeRejectsUntrustedSource— with the report's recommendedallowedPeers[sourceChainSelector][sender]gate applied, the identical delivery reverts"allowed"and the pool is intact.
The dstId in the forged payload is kept in uint32 range so decoding succeeds
— this finding is independent of the separate decode bug (#55537).
_shared/run-poc/run_poc.sh 55536-missing-source-validation-in-ccip-message-handling-cyfrin-no_exp -vvvvv
Fix#
Validate the source before acting: require(allowedPeers[sourceChainSelector][sender])
(fix commit a03341d).
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 55536-missing-source-validation-in-ccip-message-handling-cyfrin-no_exp (evm-hack-registry mirror).
- AuditVault finding: 55536-missing-source-validation-in-ccip-message-handling-cyfrin-no.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "YieldFi CCIP: missing source validation lets an untrusted chain drive privileged mint/unlock".
- Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.