Reproduced Exploit
Decent — Anyone can update the Router address in `DcntEth`
1. DcntEth gates mint/burn with onlyRouter, but setRouter is public and unrestricted. 2. Anyone can point router at themselves, mint arbitrary DcntEth, and call DecentEthRouter.redeemWeth to pull real WETH reserves 1:1. 3. The same hijack also enables burning the router's DcntEth and DoSing liquidi…
Chain
Other
Category
access-control
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: 30559-h-01-anyone-can-update-the-address-of-the-router-in-the-dcnt. Standalone Foundry PoC and full write-up: 30559-h-01-anyone-can-update-the-address-of-the-router-in-the-dcnt_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/access-control/missing-caller-check · impact/loss-of-funds/direct-drain · genome/access-roles
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/30559-h-01-anyone-can-update-the-address-of-the-router-in-the-dcnt_exp.sol.
AuditVault taxonomy: lang/solidity · platform/code4rena · has/github · has/poc · severity/high · sector/bridge · sector/dex · genome: frozen-funds · direct-drain · access-roles · dos-resistance
Key info#
| Impact | HIGH — any address can become the DcntEth router, free-mint DcntEth, and redeem 1:1 against WETH liquidity in DecentEthRouter (full reserve drain) |
| Protocol | Decent — Decent ETH bridge (DcntEth OFT + DecentEthRouter) |
| Vulnerable code | DcntEth.setRouter(address) — public, no access control |
| Bug class | Missing access control on privileged configuration |
| Finding | Code4rena — Decent, 2024-01 · #30559 (H-01) · reporter 3 |
| Report | 2024-01-decent |
| Source | AuditVault |
| Status | Audit finding — confirmed by Decent. Reproduced as a standalone local PoC. |
| Compiler | ^0.8.24 (PoC) |
TL;DR#
DcntEthgatesmint/burnwithonlyRouter, butsetRouteris public and unrestricted.- Anyone can point
routerat themselves, mint arbitrary DcntEth, and callDecentEthRouter.redeemWethto pull real WETH reserves 1:1. - The same hijack also enables burning the router's DcntEth and DoSing liquidity functions.
- HARM in the PoC: 100 WETH of LP reserves drained to the attacker after a permissionless
setRouter.
The vulnerable code#
Verbatim shape from decentxyz/decent-bridge@7f90fd4 src/DcntEth.sol:
function setRouter(address _router) public {
router = _router; // @> VULN: no access control
}
function mint(address _to, uint256 _amount) public onlyRouter {
_mint(_to, _amount);
}
function burn(address _from, uint256 _amount) public onlyRouter {
_burn(_from, _amount);
}
Fix: restrict setRouter with onlyOwner (or a dedicated admin role).
Root cause#
onlyRouter correctly protects mint/burn, but the address that is the router is writable by anyone. Becoming the router is equivalent to holding the mint/burn privilege for the bridge accounting token, which is redeemable against real WETH held by the router.
Preconditions#
DcntEthis live withsetRouterunrestricted (audited commit).DecentEthRouterholds WETH reserves from liquidity providers (or UTB escrow).- No privileged role required.
Attack walkthrough#
- LPs deposit WETH via
addLiquidityWeth→ router holds WETH and mints DcntEth to itself. - Attacker calls
dcntEth.setRouter(attacker). - Attacker
mint(attacker, reserves)andapprove+redeemWeth(reserves). - Router WETH balance → 0; attacker holds the full reserve amount.
Diagrams#
Remediation#
- function setRouter(address _router) public {
+ function setRouter(address _router) public onlyOwner {
router = _router;
}
How to reproduce#
cd ~/RustroverProjects/audits/evm-hack-registry/30559-h-01-anyone-can-update-the-address-of-the-router-in-the-dcnt_exp
forge test -vvv
# Fully local -- no fork, no RPC, no anvil_state required.
Sources#
- AuditVault finding: 30559-h-01-anyone-can-update-the-address-of-the-router-in-the-dcnt.md
- Code4rena report: 2024-01-decent
- Vulnerable repo: decentxyz/decent-bridge@7f90fd4 —
setRouter,mint,burn;DecentEthRouter.redeemWeth
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 30559-h-01-anyone-can-update-the-address-of-the-router-in-the-dcnt_exp (evm-hack-registry mirror).
- AuditVault finding: 30559-h-01-anyone-can-update-the-address-of-the-router-in-the-dcnt.
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.