Platform: Code4rena
Start Date: 19/01/2024
Pot Size: $36,500 USDC
Total HM: 9
Participants: 113
Period: 3 days
Judge: 0xsomeone
Id: 322
League: ETH
Rank: 41/113
Findings: 2
Award: $78.81
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: NPCsCorp
Also found by: 0x11singh99, 0xAadi, 0xBugSlayer, 0xE1, 0xPluto, 0xSimeon, 0xSmartContract, 0xabhay, 0xdice91, 0xprinc, Aamir, Aymen0909, CDSecurity, DadeKuma, DarkTower, EV_om, Eeyore, GeekyLumberjack, GhK3Ndf, Giorgio, Greed, Inference, JanuaryPersimmon2024, Kaysoft, Krace, Matue, MrPotatoMagic, NentoR, Nikki, PUSH0, Soliditors, Tendency, Tigerfrake, Timeless, Timenov, ZanyBonzy, ZdravkoHr, abiih, adeolu, al88nsk, azanux, bareli, boredpukar, cu5t0mpeo, d4r3d3v1l, darksnow, deth, dutra, ether_sky, haxatron, ke1caM, kodyvim, m4ttm, mgf15, mrudenko, nmirchev8, nobody2018, nuthan2x, peanuts, piyushshukla, ravikiranweb3, rouhsamad, seraviz, simplor, slylandro_star, stealth, th13vn, vnavascues, wangxx2026, zaevlad
0.1172 USDC - $0.12
The impact of this issue is critical, it allows anyone the set their own address as the router and mint unlimited tokens.
Here we can see that the setRouter
is left wide open for anyone to change.
function setRouter(address _router) public { router = _router; }
function mint(address _to, uint256 _amount) public onlyRouter { _mint(_to, _amount); }
After changing the router Address to his own, an attacker can directly mint himself DcntEth
tokens.
Manual review
Add access control to the setRouter function.
-- function setRouter(address _router) public { ++ function setRouter(address _router) public onlyOwner{ router = _router; }
Access Control
#0 - c4-pre-sort
2024-01-24T16:52:48Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-01-24T16:52:55Z
raymondfam marked the issue as duplicate of #14
#2 - c4-judge
2024-02-03T13:21:37Z
alex-ppg marked the issue as satisfactory
78.6887 USDC - $78.69
https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentBridgeExecutor.sol#L24-L65 https://github.com/LayerZero-Labs/solidity-examples/blob/2d68d812260d1f96d0a7a46abd6ade4e8962aae1/contracts/token/oft/v2/OFTCoreV2.sol#L216-L222 https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L278-L282
When a transaction is forwarded to be executed to the executor contract from the router. If the transaction fails the funds are returned to the msg.sender address, however it is not garanteed that the msg.sender can receive the funds on a different chain.
In the DecentEthRouter::_getCallParams()
function, we encode the args for the cross chain call.
} else { payload = abi.encode( msgType, msg.sender, _toAddress, deliverEth, additionalPayload );
These are the arguments that are used to be forwarded to the executor. https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L280
executor.execute(_from, _to, deliverEth, _amount, callPayload);
The _executeEth and the _executeWeth both have a catch function in case the initial call fails.
either
if (!success) { payable(from).transfer(amount); }
or
if (!success) { weth.transfer(from, amount); return; }
This catch function will try to send back the eth or weth to the from, which is the msg.sender on the other chain.
Given that the protocol supports transfers between any chains, Decent allows for single click transactions on any chain, paid for from any source chain / token. , which is enabled through the implementation of the OFTV2 token. We have to consider the fact that although cross chain wallets can share the same private key, the public addresses will be because different cryptographic curves used by each chain. For example a Solana public address might be different than an Ethereum public address even though they share the same private key. So in case the executor transaction reverts, the funds will be sent back to a different address, resulting in a loss for the user.
manual review.
Allow the user to specify a return address on the destination chain in case the cross transaction was to fail.
payload = abi.encode( msgType, msg.sender, ++ returnAddress, _toAddress, deliverEth, additionalPayload ); }
function _executeEth( -- address from, ++ address returnAddress, address target, uint256 amount, bytes memory callPayload ) private { weth.withdraw(amount); (bool success, ) = target.call{value: amount}(callPayload); if (!success) { payable(from).transfer(amount); } } `` This will allow the user to control where the funds are being sent to in case of a revert on the end chain. ## Assessed type Other
#0 - c4-pre-sort
2024-01-25T00:03:49Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-01-25T00:03:57Z
raymondfam marked the issue as duplicate of #27
#2 - c4-judge
2024-02-02T17:22:49Z
alex-ppg marked the issue as partial-75
#3 - c4-judge
2024-02-04T23:04:02Z
alex-ppg changed the severity to 3 (High Risk)