Decent - rouhsamad's results

Decent enables one-click transactions using any token across chains.

General Information

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

Decent

Findings Distribution

Researcher Performance

Rank: 12/113

Findings: 3

Award: $806.42

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DcntEth.sol#L20-L22

Vulnerability details

Impact

setRouter function does not have any access controls, allowing anyone to change router to an arbitrary address and mint unlimited tokens.

Proof of Concept

mint function is onlyRouter, which means router (or exploiter address) is able to mint unlimited tokens: https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DcntEth.sol#L24-L26

Tools Used

  • Manual review

setRouter should be declared as onlyOwner:

function setRouter(address _router) public onlyOwner { router = _router; }

Assessed type

Access Control

#0 - c4-pre-sort

2024-01-24T04:18:40Z

raymondfam marked the issue as sufficient quality report

#1 - c4-pre-sort

2024-01-24T04:18:46Z

raymondfam marked the issue as duplicate of #14

#2 - c4-judge

2024-02-03T13:25:55Z

alex-ppg marked the issue as partial-75

#3 - alex-ppg

2024-02-03T13:26:01Z

Comparative quality of submission is relatively low.

Findings Information

🌟 Selected for report: haxatron

Also found by: Aamir, EV_om, MrPotatoMagic, Topmark, bart1e, deth, rouhsamad

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
duplicate-59

Awards

794.0484 USDC - $794.05

External Links

Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L266-L269 https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L250-L255

Vulnerability details

Proof of Concept

When receiving a cross-chain message, the onOFTReceived function of DecentRouter is invoked. Now, assuming that this message contains a payload for calling the destination (_to) , we can reasonably infer that _to is most likely a smart contract (though it might also be an EOA, but in most cases, it is a smart contract). The issue arises when there is an insufficient amount of WETH in the DecentEthRouter. In this scenario, Decent tokens are sent to _to, which is a smart contract. Consequently, there is a high risk that these Decent tokens might be locked indefinitely in the smart contract, leading to potential loss of funds.

Impact

loss of funds - The likelihood of encountering this issue is very high, given that users utilize the UTB contract for making cross-chain swaps. in this case if the DecentEthRouter at destination lacks a sufficient amount of WETH, Decent tokens are then sent to the DecentBridgeAdapter in the destination chain, which, crucially, lacks any functions for withdrawing these tokens. This situation poses a significant risk as there is no mechanism in place for retrieving or managing the Decent tokens sent under these circumstances.

Tools Used

  • Manual review

In this scenario, I would suggest implementing a check to verify if the message contains a payload. If it does, there is no reason for calling _to when there isn't enough WETH in the contract. It would be more secure to redirect the funds to the refund address. Conversely, if the message doesn't contain a payload and _to is an externally owned account (EOA), it is deemed safe to send Decent tokens to either _to or the refund address.

Assessed type

Invalid Validation

#0 - c4-pre-sort

2024-01-24T23:31:58Z

raymondfam marked the issue as sufficient quality report

#1 - c4-pre-sort

2024-01-24T23:32:06Z

raymondfam marked the issue as duplicate of #59

#2 - c4-judge

2024-02-02T15:15:00Z

alex-ppg marked the issue as satisfactory

Findings Information

Labels

bug
downgraded by judge
grade-b
primary issue
QA (Quality Assurance)
sponsor confirmed
sufficient quality report
edited-by-warden
Q-09

Awards

12.2818 USDC - $12.28

External Links

Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L285-L291 https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L275-L276 https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L313-L319

Vulnerability details

Impact

High - imho the impact of this issue is should be classified as high due to its substantial likelihood, simplicity in execution (requiring only a single function call), and the potential for substantial financial losses. Users face a considerable risk, as demonstrated by scenarios such as swapping 1 Ethereum for 1 Matic, resulting in significant loss of funds.

Proof of Concept

In some chains, such as Polygon/AVAX, Ethereum is not recognized as their gas token. This distinction is addressed in the DecentEthRouter by configuring the gasCurrencyIsEth variable as either true or false. However, some sections of both DecentEthRouter and DecentBridgeExecutor (as mentioned below) are mistaking Ethereum as the native chain token without checking gasCurrencyIsEth.

DecentEthRouter::redeemEth: When a user wants to redeem their Decent tokens (on Polygon/Avax) using this function, the user's Decent tokens are transferred to the contract, WETH is withdrawn (burned), and native chain tokens (e.g., Matic in the Polygon example) are sent to the user. This implies that if, for example, Alice decides to redeem her 10 dcntEth tokens, she will receive 10 Matic tokens, which are worth only 7 USD, resulting in the loss of all her 10 WETH (worth 22,000 usd)

DecentBridgeExecutor::_executeETH: Also in this function, WETH is withdrawn and Matic/Avax are received which might be useless when calling the target. this function results in loss of funds for user (from) and stuck funds in DecentBridgeExecutor which can be withdrawn by a malicious actor.

NOTE: there might not be enough MATIC/AVAX in router in order to be sent to user, however, a malicious actor is able to fund contract with enough MATIC/AVAX.

Code PoC: while the issue is clear, i have decided to put a code PoC (Copy from next line to before Tools Used and paste in a new file, no additional settings needed):

//SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import {Test, console, console2} from "forge-std/Test.sol"; import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol";

interface IWETH { function withdraw(uint amt) external payable;

function deposit() external payable; function balanceOf(address user) external view returns (uint);

}

contract MockRouter is Test { IWETH polygon_weth = IWETH(0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619); IWETH wrappedMatic = IWETH(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270);

ERC20Mock dcntToken; address alice; modifier onlyIfWeHaveEnoughReserves(uint256 amount) { require( polygon_weth.balanceOf(address(this)) > amount, "not enough reserves" ); _; } function setUp() public { vm.createSelectFork("https://polygon.meowrpc.com"); alice = makeAddr("Alice"); dcntToken = new ERC20Mock(); dcntToken.mint(alice, 10 ether); //transfer WETH to router vm.startPrank(0x62ac55b745F9B08F1a81DCbbE630277095Cf4Be1); IERC20(address(polygon_weth)).transfer(address(this), 11 ether); vm.stopPrank(); } function redeemEth( uint256 amount ) public onlyIfWeHaveEnoughReserves(amount) { dcntToken.transferFrom(msg.sender, address(this), amount); polygon_weth.withdraw(amount); payable(msg.sender).transfer(amount); } function testWithdrawWETH() public { //alice deciding to withdraw his 10 Dcnt tokens for ether assertEq(dcntToken.balanceOf(alice), 10 ether); assertEq(alice.balance, 0); //no matics assertEq(polygon_weth.balanceOf(alice), 0); //no WETH assertEq(wrappedMatic.balanceOf(alice), 0); //alice doesn't have any MATIC vm.startPrank(alice); dcntToken.approve(address(this), 10 ether); this.redeemEth(10 ether); wrappedMatic.deposit{value: alice.balance}(); assertEq(dcntToken.balanceOf(alice), 0);//tokens sent to the contract assertEq(alice.balance, 0); //no matics assertEq(polygon_weth.balanceOf(alice), 0); //no WETH assertEq(wrappedMatic.balanceOf(alice), 10 ether); //alice has 10 matics instead of WETH vm.stopPrank(); } receive() external payable {}

}

Tools Used

Manual review Forge testing

Mitigation steps

While I don't have a clear solution in mind yet, I would suggest first checking gasCurrencyIsEth to determine whether the chain supports ETH as the gas token or not. If it does not (false), then proceed to swap WETH for the native chain token (e.g., WETH => MATIC) and send MATIC tokens to the user. Alternatively, consider not allowing certain functions to be executed if the chain does not support ETH as the native gas token.

Assessed type

Invalid Validation

#0 - c4-pre-sort

2024-01-25T00:45:11Z

raymondfam marked the issue as sufficient quality report

#1 - raymondfam

2024-01-25T00:45:37Z

Would indeed be a serious issue if the vulnerability is valid.

#2 - c4-pre-sort

2024-01-25T00:45:40Z

raymondfam marked the issue as primary issue

#3 - c4-sponsor

2024-01-31T16:01:10Z

wkantaros (sponsor) confirmed

#4 - alex-ppg

2024-02-01T13:53:19Z

The Warden demonstrates a discrepancy in the system concerning the weth token and how it is treated in the overall system.

I investigated the overall deployment system of the Sponsor and indeed validated that the DecentEthRouter is deployed with the WETH address rather than a wrapped native asset variant (i.e. wMATIC for Polygon).

The problem lies in the actual exploitability of the function paths defined. The entire system would not really behave as the Warden details.

Specifically, DecentEthRouter::redeemEth mandates that the contract has a sufficient WETH balance. Router deployments that are made outside the Ethereum network will have a 0 balance and thus the user will not be able to redeem their Decent ETH (unless someone sent WETH to the contract). This idea is flawed, as its pre-conditions are:

  • Incorrect invocation of DecentEthRouter::redeemEth on a non-Ethereum network by the victim
  • Malicious user sacrificing the full amount they intend to grief the victim of
  • Net negative for victim and attacker of substantial amount with no apparent result that the attacker benefits from

In relation to the DecentBridgeExecutor, the code will "never" execute in non-Ethereum chains. This function is invoked solely by DecentEthRouter::onOFTReceived which would evaluate its own weth balance, detect that it does not have a sufficient one, and simply transfer the credited DcntEth to the intended _to recipient.

While overall flawed, the described execution paths behave as expected and cannot be exploited to force fund loss for the system maliciously. Cross-chain transfers of the DcntEth token will execute "correctly" as the DecentEthRouter::onOFTReceived function will end early and simply transfer the DcntEth to their intended recipient.

As a result of all this, I consider the submission invalid but the Sponsor should definitely re-evaluate how they have approached multi-chain deployments of their system as they are presently illegible and permit reverting functionality that should be inaccessible due to a more descriptive error message. I also invite the Sponsor to re-evaluate and whether they have detected a valid exploitation path based on the data supplied by the Warden.

#5 - c4-judge

2024-02-01T13:53:47Z

alex-ppg marked the issue as unsatisfactory: Invalid

#6 - alex-ppg

2024-02-02T09:26:35Z

After discussions with the Sponsor, I stand by my original invalidation judgment due to the impossibility of exploiting the vulnerability. To note, for a user to be attacked the attacker would have to sacrifice an equal value of the funds they wish to damage the user of. This is not acceptable as a vulnerability of HM risk but rather a QA (L) issue of code misbehavior that should be prevented. Based on this, I will proceed with downgrading the submission to QA (L) as it details a valid problem in the code concerning DecentEthRouter::onOFTReceived.

#7 - c4-judge

2024-02-02T09:26:42Z

alex-ppg removed the grade

#8 - c4-judge

2024-02-02T09:26:59Z

alex-ppg changed the severity to QA (Quality Assurance)

#9 - alex-ppg

2024-02-04T22:59:38Z

I will award this submission a B grade as it goes into great detail about a potentially valid issue in the code. The reason this does not constitute a medium risk vulnerability is out of "luck" due to how the system is laid out, and the submission goes into great detail about all functions affected as well as their potential impact.

#10 - c4-judge

2024-02-04T22:59:42Z

alex-ppg marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter