Reproduced Exploit
Decent — Failed `execute` refunds a phantom source-adapter address
1. Source DecentBridgeAdapter calls the router; _getCallParams packs msg.sender (the adapter) as from. 2. Destination onOFTReceived → executor.execute(_from, _to, ...). 3. If the target call fails, _executeWeth does weth.transfer(from, amount). 4. Adapters are not CREATE2-aligned across chains → fr…
Chain
Other
Category
untagged
Date
Jan 2024
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: 30561-h-03-when-decentbridgeexecutorexecute-fails-funds-will-be-se. Standalone Foundry PoC and full write-up: 30561-h-03-when-decentbridgeexecutorexecute-fails-funds-will-be-se_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/wrong-condition · impact/loss-of-funds/direct-drain · genome/bridge-message-validation
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/30561-h-03-when-decentbridgeexecutorexecute-fails-funds-will-be-se_exp.sol.
AuditVault taxonomy: lang/solidity · platform/code4rena · has/github · has/poc · severity/high · sector/bridge · genome: wrong-condition · direct-drain · bridge-message-validation
Key info#
| Impact | HIGH — when the destination target call fails, the full bridged amount is refunded to the source-chain adapter address (not the user), which is not a contract on the destination → permanent fund loss |
| Protocol | Decent — DecentEthRouter + DecentBridgeExecutor |
| Vulnerable code | _getCallParams encodes msg.sender as from; _executeWeth/_executeEth refund from on failure |
| Bug class | Wrong refund recipient in cross-chain failure path |
| Finding | Code4rena — Decent, 2024-01 · #30561 (H-03) · reporter ZdravkoHr |
| Report | 2024-01-decent |
| Source | AuditVault |
| Status | Audit finding — confirmed by Decent. Local synthetic reproduces the misdirected refund. |
| Compiler | ^0.8.24 (PoC) |
TL;DR#
- Source
DecentBridgeAdaptercalls the router;_getCallParamspacksmsg.sender(the adapter) asfrom. - Destination
onOFTReceived→executor.execute(_from, _to, ...). - If the target call fails,
_executeWethdoesweth.transfer(from, amount). - Adapters are not CREATE2-aligned across chains →
fromis a phantom address on dest. - HARM in the PoC: 10 WETH refunded to the source-adapter address; user gets nothing.
The vulnerable code#
// _getCallParams (source)
payload = abi.encode(
msgType,
msg.sender, // @> VULN: source adapter as refund `from`
_toAddress,
deliverEth,
additionalPayload
);
// _executeWeth (destination) on failure
if (!success) {
weth.transfer(from, amount); // @> VULN: refunds wrong chain's address
return;
}
Fix: pass / refund the real user (e.g. destination recipient or an explicit refund address), never the source adapter.
Root cause#
The payload's from was intended as "original user" but is actually the immediate msg.sender on the source (the adapter). The failure refund path trusts that field without translating it to a destination-valid user address. Non-CREATE2 deployments make same-address recovery impossible.
Preconditions#
- Cross-chain transfer with payload (UTB / adapter path) so
fromis the adapter. - Destination target call fails (revert / OOG / bad calldata).
- Adapter addresses differ across chains (true for audited deploy scripts).
Attack walkthrough#
- Source adapter builds payload →
from = sourceAdapter. - Destination credits WETH and calls
execute(from, failingTarget, amount, ...). - Target reverts → executor refunds WETH to
from. - WETH sits on a phantom address; user cannot recover it.
Diagrams#
Remediation#
- executor.execute(_from, _to, deliverEth, _amount, callPayload);
+ executor.execute(_to /* or explicit user refund */, _to, deliverEth, _amount, callPayload);
And/or stop encoding the adapter as from in _getCallParams.
How to reproduce#
cd ~/RustroverProjects/audits/evm-hack-registry/30561-h-03-when-decentbridgeexecutorexecute-fails-funds-will-be-se_exp
forge test -vvv
Sources#
- AuditVault finding: 30561-h-03-when-decentbridgeexecutorexecute-fails-funds-will-be-se.md
- Code4rena report: 2024-01-decent
- Vulnerable repo: decentxyz/decent-bridge@7f90fd4 —
_executeWeth/_executeEth;DecentEthRouter._getCallParams
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 30561-h-03-when-decentbridgeexecutorexecute-fails-funds-will-be-se_exp (evm-hack-registry mirror).
- AuditVault finding: 30561-h-03-when-decentbridgeexecutorexecute-fails-funds-will-be-se.
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.