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: 47/113
Findings: 2
Award: $43.55
🌟 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
Users can change the router of DcntEth at will. An attacker can forge a router contract and then call the mint and burn functions at will.
setRouter
is implemented as follows
function setRouter(address _router) public { router = _router; }
Through the implementation of the function, we can know that this function can be called by anyone, and the affected functions mainly include mint
and burn
Manual Review
Change it to the following:
function setRouter(address _router) public onlyOwner{ router = _router; }
Access Control
#0 - c4-pre-sort
2024-01-24T15:34:10Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-01-24T15:34:16Z
raymondfam marked the issue as duplicate of #14
#2 - c4-judge
2024-02-03T13:23:19Z
alex-ppg marked the issue as satisfactory
43.4302 USDC - $43.43
This will cause the amount to be stored in the Adapter contract, which is not in line with the sponsor's ideas, and will cause the user to lose part of the amount.
Fund Accumulation: Other than the UTBFeeCollector, and DcntEth, the contracts are not intended to hold on to any funds or unnecessary approvals. Any native value or erc20 flowing through the protocol should either get delivered or refunded.
But when the user calls the function _bridgeWithPayload
, he sets the refundAddress
of endpoint.send
to msg.sender
function _bridgeWithPayload( uint8 msgType, uint16 _dstChainId, address _toAddress, uint _amount, uint64 _dstGasForCall, bytes memory additionalPayload, bool deliverEth ) internal { ... ICommonOFT.LzCallParams memory callParams = ICommonOFT.LzCallParams({ refundAddress: payable(msg.sender), zroPaymentAddress: address(0x0), adapterParams: adapterParams }); ... dcntEth.sendAndCall{value: gasValue}( address(this), // from address that has dcntEth (so DecentRouter) _dstChainId, destinationBridge, // toAddress _amount, // amount payload, //payload (will have recipients address) _dstGasForCall, // dstGasForCall callParams // refundAddress, zroPaymentAddress, adapterParams ); }
When the transmitted gas cost is more than the actual usage cost, the excess amount will be returned to refundAddress, but since the caller is an adapter, the amount will be returned to the DecentBridgeAdapter
contract
function bridge( uint256 amt2Bridge, SwapInstructions memory postBridge, uint256 dstChainId, address target, address paymentOperator, bytes memory payload, bytes calldata additionalArgs, address payable refund ) public payable onlyUtb returns (bytes memory bridgePayload) { require( destinationBridgeAdapter[dstChainId] != address(0), string.concat("dst chain address not set ") ); uint64 dstGas = abi.decode(additionalArgs, (uint64)); bridgePayload = abi.encodeCall( this.receiveFromBridge, (postBridge, target, paymentOperator, payload, refund) ); SwapParams memory swapParams = abi.decode( postBridge.swapPayload, (SwapParams) ); if (!gasIsEth) { IERC20(bridgeToken).transferFrom( msg.sender, address(this), amt2Bridge ); IERC20(bridgeToken).approve(address(router), amt2Bridge); } router.bridgeWithPayload{value: msg.value}( lzIdLookup[dstChainId], destinationBridgeAdapter[dstChainId], swapParams.amountIn, false, dstGas, bridgePayload ); }
Finally, the excess fees will not be returned to the user, but will eventually remain in the DecentBridgeAdapter
contract
Manual Review
Added method to determine whether the adapter contract is called. If so, the refund object will be passed using account as a parameter.
Other
#0 - c4-pre-sort
2024-01-24T15:35:41Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-01-24T15:35:55Z
raymondfam marked the issue as duplicate of #27
#2 - c4-judge
2024-02-02T16:57:13Z
alex-ppg marked the issue as not a duplicate
#3 - c4-judge
2024-02-02T16:58:37Z
alex-ppg marked the issue as duplicate of #262
#4 - c4-judge
2024-02-02T17:03:03Z
alex-ppg marked the issue as satisfactory