Reproduced Exploit
DODO Cross-Chain DEX — unauthorized claim of non-EVM chain refunds
1. Refunds store a walletAddress blob (20 bytes for EVM, longer for BTC/etc.). 2. claimRefund only decodes receiver when length is exactly 20; otherwise receiver stays msg.sender. 3. Auth becomes msg.sender == msg.sender → always true for non-bot callers. 4. Attacker frontruns bots and steals the r…
Chain
Other
Category
access-control
Date
May 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: 58582-h-5-unauthorized-claim-of-non-evm-chain-refunds-in-claimrefu. Standalone Foundry PoC and full write-up: 58582-h-5-unauthorized-claim-of-non-evm-chain-refunds-in-claimrefu_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/access-control/auth-bypass · vuln/loss-of-funds/direct-drain · vuln/logic/address-encoding
Reproduction: a self-contained Foundry PoC that compiles & runs in an isolated project with only
forge-std— no fork, no RPC, noanvil_state. Full trace: output.txt. PoC: test/58582-h-5-unauthorized-claim-of-non-evm-chain-refunds-in-claimrefu_exp.sol.
AuditVault taxonomy: severity/high · sector/bridge · sector/dex · platform/sherlock · flashloan-callback-auth · direct-drain
Key info#
| Impact | HIGH — any caller can claim refunds whose walletAddress is not 20 bytes (Bitcoin, Solana, …) |
| Protocol | DODO Cross-Chain DEX — GatewayCrossChain / GatewayTransferNative claimRefund |
| Vulnerable code | receiver = msg.sender when walletAddress.length != 20, then require(bots[msg.sender] || msg.sender == receiver) |
| Bug class | Auth bypass for non-EVM refund receivers |
| Finding | Sherlock 2025-05-dodo-cross-chain-dex · #58582 · reporter newspacexyz (and others) |
| Report | sherlock-audit/2025-05-dodo-cross-chain-dex-judging |
| Source | AuditVault |
| Status | Audit finding — fixed in PR 24. Reproduced as a standalone local PoC. |
| Compiler | ^0.8.24 (PoC) |
TL;DR#
- Refunds store a
walletAddressblob (20 bytes for EVM, longer for BTC/etc.). claimRefundonly decodesreceiverwhen length is exactly 20; otherwisereceiverstaysmsg.sender.- Auth becomes
msg.sender == msg.sender→ always true for non-bot callers. - Attacker frontruns bots and steals the refund tokens. Fix: non-EVM path must require
bots[msg.sender]only (never equate receiver to caller).
The vulnerable code#
address receiver = msg.sender;
if (refundInfo.walletAddress.length == 20) {
receiver = address(uint160(bytes20(refundInfo.walletAddress)));
}
// FIX: non-EVM → require(bots[msg.sender]); do not set receiver = msg.sender for auth
require(bots[msg.sender] || msg.sender == receiver, "INVALID_CALLER"); // @> VULN
Root cause#
The default receiver = msg.sender was meant as a convenience for the bot path, but it also collapses the authorization predicate whenever the wallet is not a 20-byte EVM address. Non-EVM refunds therefore have no beneficiary check for arbitrary EOAs.
Preconditions#
- A refund exists with
walletAddress.length != 20(failed Bitcoin/Solana outbound, etc.). - Contract holds the refund token balance.
Attack walkthrough#
- Seed refund: 10_000 TOKEN for Bitcoin address
bc1q…(42 bytes). - Attacker (not a bot) calls
claimRefund(externalId). receiver = msg.sender; require passes; tokens transfer to attacker.- Control: 20-byte EVM refund still reverts
INVALID_CALLERfor the same attacker.
Diagrams#
Impact#
Complete theft of all non-EVM-bound refund inventory at gas cost only. Legitimate users on Bitcoin and similar chains permanently lose those funds.
Sources#
- AuditVault finding #58582
- Sherlock judging issue #873
- Reduced source: sherlock-audit/2025-05-dodo-cross-chain-dex —
claimRefund
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 58582-h-5-unauthorized-claim-of-non-evm-chain-refunds-in-claimrefu_exp (evm-hack-registry mirror).
- AuditVault finding: 58582-h-5-unauthorized-claim-of-non-evm-chain-refunds-in-claimrefu.
Alerts & third-party analyses
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.