DYAD - Dudex_2004's results

The first capital efficient overcollateralized stablecoin.

General Information

Platform: Code4rena

Start Date: 18/04/2024

Pot Size: $36,500 USDC

Total HM: 19

Participants: 183

Period: 7 days

Judge: Koolex

Id: 367

League: ETH

DYAD

Findings Distribution

Researcher Performance

Rank: 83/183

Findings: 2

Award: $37.28

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

32.4128 USDC - $32.41

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
edited-by-warden
:robot:_28_group
duplicate-397

External Links

Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L150

Vulnerability details

The user deposits the collateral tokens into the system to mint Dyad tokens and these collateral tokens are currently wETH and wstETH, and will soon include LSTs and LRTs, other types of yield-bearing collateral, as well as Kerosene. The mintDyad function mints Dyad token to users at their 150% collateral ratio while the withdraw function sends the user deposited collateral to receiver which could be any collateral token mentioned above. However, the function only checks that the existing collateral token should be nonKerosene tokens for the dNFT id.

  function withdraw(
    uint    id,
    address vault,
    uint    amount,
    address to
  ) 
    public
      isDNftOwner(id)
  {
    if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock();
    uint dyadMinted = dyad.mintedDyad(address(this), id);
    Vault _vault = Vault(vault);
    uint value = amount * _vault.assetPrice() 
                  * 1e18 
                  / 10**_vault.oracle().decimals() 
                  / 10**_vault.asset().decimals();
@>  if (getNonKeroseneValue(id) - value < dyadMinted) revert NotEnoughExoCollat();
    _vault.withdraw(id, to, amount);
    if (collatRatio(id) < MIN_COLLATERIZATION_RATIO)  revert CrTooLow(); 
  }
  function mintDyad(
    uint    id,
    uint    amount,
    address to
  )
    external 
      isDNftOwner(id)
  {
    uint newDyadMinted = dyad.mintedDyad(address(this), id) + amount;
@>  if (getNonKeroseneValue(id) < newDyadMinted)     revert NotEnoughExoCollat();
    dyad.mint(id, to, amount);
    if (collatRatio(id) < MIN_COLLATERIZATION_RATIO) revert CrTooLow(); 
    emit MintDyad(id, amount, to);
  }

So the problem is here that if the user has only deposited Kerosene tokens to mind Dyad tokens instead of nonKerosene tokens then the withdraw and mintDyad function will not be executed because these function checks that the exogeneous collateral should be deposited. Cause user only deposited Kerosene tokens these functions will revert.

Impact

User will not be able to withdraw his deposited kerosene tokens. User will not be able to mint Dyad tokens.

Proof of Concept

  1. User deposits Kerosene tokens.
  2. The value of the user collateral has increased significantly since the initial deposit, the user may decide to withdraw it to realize profits.
  3. Now it is impossible for user to withdraw Kerosene tokens.

Or

  1. User deposits kerosene tokens to mint Dyad token.
  2. It is impossible to mint Dyad.

Tools Used

Manual review

Add getTotalUsdValue(id) instead of getNonKeroseneValue(id) in both functions and change the revert name as NotEnoughCollat.

-    if (getNonKeroseneValue(id) - value < dyadMinted) revert NotEnoughExoCollat();
+    if (getTotalUsdValue(id) - value < dyadMinted) revert NotEnoughExoCollat();

Assessed type

Other

#0 - c4-pre-sort

2024-04-26T21:46:31Z

JustDravee marked the issue as duplicate of #397

#1 - c4-pre-sort

2024-04-29T08:48:20Z

JustDravee marked the issue as sufficient quality report

#2 - c4-judge

2024-05-11T19:22:35Z

koolexcrypto marked the issue as satisfactory

Awards

4.8719 USDC - $4.87

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
edited-by-warden
:robot:_67_group
duplicate-67

External Links

Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/Vault.kerosine.unbounded.sol#L50

Vulnerability details

Impact

The attacker could perform asset price manipulation to make the apparent value of an asset to be much higher or much lower than the true value of the asset. Following are some risks of asset price manipulation:

An attacker can increase the value of dyad and the kerosine tokens by increasing the supply of it into the system. An attacker can decrease the value of dyad by redeeming the dyad from the system.

Proof of Concept

The assetPrice function within the Vault.kerosine.unbounded.sol contract is vulnerable to asset price manipulation because the price can be increased or decreased by inflating the totalSupply of dyad or kerosine tokens.

 function assetPrice() 
    public 
    view 
    override
    returns (uint) {
      uint tvl;
      address[] memory vaults = kerosineManager.getVaults();
      uint numberOfVaults = vaults.length;
      for (uint i = 0; i < numberOfVaults; i++) {
        Vault vault = Vault(vaults[i]);
        tvl += vault.asset().balanceOf(address(vault)) 
                * vault.assetPrice() * 1e18
                / (10**vault.asset().decimals()) 
                / (10**vault.oracle().decimals());
      }
      uint numerator   = tvl - dyad.totalSupply();
      uint denominator = kerosineDenominator.denominator();
      return numerator * 1e8 / denominator;
  }

The Vault.kerosine.unbounded.sol inherits from KerosineVault and allow us to deposit and withdraw the kerosines as well as it calculates the getUsdValue according to the assetPrice.

 function getUsdValue(
    uint id
  )
    public
    view 
    returns (uint) {
      return id2asset[id] * assetPrice() / 1e8;
  }

But the problem is here that this assetPrice is inflated by the attacker by targeting the totalSupply of dyad or kerosine tokens. Which will effect the numerater or denominator and calculate assetPrice accordingly.

uint numerator   = tvl - dyad.totalSupply();
function denominator() external view returns (uint) {
    // @dev: We subtract all the Kerosene in the multi-sig.
    //       We are aware that this is not a great solution. That is
    //       why we can switch out Denominator contracts.
    return kerosine.totalSupply() - kerosine.balanceOf(MAINNET_OWNER);
  } 

Tools Used

Manual review.

Consider implementing TWAP so that the price of asset cannot be inflated or deflated.

Assessed type

Other

#0 - c4-pre-sort

2024-04-28T06:05:10Z

JustDravee marked the issue as duplicate of #67

#1 - c4-pre-sort

2024-04-29T09:06:17Z

JustDravee marked the issue as sufficient quality report

#2 - c4-judge

2024-05-08T11:50:06Z

koolexcrypto marked the issue as unsatisfactory: Invalid

#3 - c4-judge

2024-05-08T12:52:59Z

koolexcrypto marked the issue as nullified

#4 - c4-judge

2024-05-08T12:53:02Z

koolexcrypto marked the issue as not nullified

#5 - c4-judge

2024-05-11T19:37:43Z

koolexcrypto marked the issue as satisfactory

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