Ondo Finance - Limbooo's results

Institutional-Grade Finance, Now Onchain.

General Information

Platform: Code4rena

Start Date: 29/03/2024

Pot Size: $36,500 USDC

Total HM: 5

Participants: 72

Period: 5 days

Judge: 3docSec

Total Solo HM: 1

Id: 357

League: ETH

Ondo Finance

Findings Distribution

Researcher Performance

Rank: 6/72

Findings: 2

Award: $2,980.53

🌟 Selected for report: 1

πŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: Limbooo

Also found by: Bigsam

Labels

bug
2 (Med Risk)
primary issue
satisfactory
selected for report
sponsor confirmed
:robot:_124_group
M-02

Awards

2916.3845 USDC - $2,916.38

External Links

Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L426-L429 https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L460

Vulnerability details

Impact

The OUSG Instant Redemption Manager contract contains an oversight in its redeem function, specifically in the handling of BUIDL redemption limits. This oversight can potentially lead to failed redemption attempts when the redemption balance exceeds the BUIDL balance held by the manager contract while it has a right amount if its concatenated with USDC amount left by another redemption process. The impact of this issue is significant as it affects the usability of the redemption feature and can result in user frustration and loss of trust in the system.

Proof of Concept

The following Proof of Concept (POC) demonstrates the issue, use it as part of forge-tests/ousg/OUSGInstantManager/redeem.t.sol file:

Run using this command

npm run test-forge -- --match-test test_POC_reddem_fail_when_alice_redeemtion_balance_is_over_manager_BUIDL_balance
  function test_POC_reddem_fail_when_alice_redeemtion_balance_is_over_manager_BUIDL_balance()
    public
    setupSecuritize(500_000e6, 500_000e6)
  {    
    uint256 aliceOUSGRedeemAmount = 1667e18; 
    uint256 aliceUSDCAmount = 250_100e6;

    uint256 bobOUSGRedeemAmount = 1666e18;
    uint256 bobUSDCAmount = 249_900e6;


    // Mint OUSG tokens for Alice and Bob
    vm.prank(address(ousgInstantManager));
    ousg.mint(alice, aliceOUSGRedeemAmount);

    vm.prank(address(ousgInstantManager));
    ousg.mint(bob, bobOUSGRedeemAmount);

    // Bob redeems OUSG tokens successfully
    vm.startPrank(bob);
    ousg.approve(address(ousgInstantManager), (bobOUSGRedeemAmount));
    ousgInstantManager.redeem(bobOUSGRedeemAmount);
    vm.stopPrank();

    // Alice attempts to redeem OUSG tokens, but the redemption fails due to insufficient BUIDL balance
    vm.startPrank(alice);
    ousg.approve(address(ousgInstantManager), (aliceOUSGRedeemAmount));
    vm.expectRevert('Not enough tokens');
    ousgInstantManager.redeem(aliceOUSGRedeemAmount);
    vm.stopPrank();

    assertEq(USDC.balanceOf(bob), bobUSDCAmount);

    assertEq(
      BUIDL.balanceOf(address(ousgInstantManager)) + USDC.balanceOf(address(ousgInstantManager)),
      aliceUSDCAmount
    );

    // However if Alice try to reddem an amount that will be in usdc amount <= minBUIDLRedeemAmount in ousgInstantManager it will success
    vm.startPrank(alice);
    ousgInstantManager.redeem(aliceOUSGRedeemAmount - 10e18);
    vm.stopPrank();

    // Tokens remaining in Alice's balance after successful redemption
    assertEq(ousg.balanceOf(alice), 10e18);

  }

Tools Used

  • Manual review
  • Foundry

To address this issue, the OUSG Instant Redemption Manager contract should implement a mechanism to ensure that redemption requests do not exceed the available BUIDL balance held by the manager contract. This can be achieved by incorporating proper checks and balances in the redemption process, such as verifying the BUIDL balance before processing redemption requests and adjusting the redemption amount accordingly. Additionally, consider an redeem implementation that concatenate the balance of remaining USDC amount with the BUIDL redeemed balance if the corresponding USDC amount or redeem amount of OUSG is more than minBUIDLRedeemAmount.

Assessed type

Error

#0 - 0xRobocop

2024-04-04T01:34:37Z

From the PoC:

assertEq(
      BUIDL.balanceOf(address(ousgInstantManager)) + USDC.balanceOf(address(ousgInstantManager)),
      aliceUSDCAmount
 );

Same problem as #235. Even if the current BUIDL balance + USDC balance of the contract is enough to cover the redeem, the contract cannot redeem BUIDL tokens if it does not have a minimum of 250k.

There is a BUIDL redemption minimum requirement that can assumed to be 250,000 BUIDL

#1 - c4-pre-sort

2024-04-04T01:34:41Z

0xRobocop marked the issue as duplicate of #235

#2 - c4-judge

2024-04-09T13:22:05Z

3docSec marked the issue as unsatisfactory: Out of scope

#3 - khalidfsh

2024-04-10T13:18:58Z

Hi,

I would like to respectfully address the assessment provided for this issue.

Upon careful analysis and consideration, I believe there are aspects of this issue that distinguish it from issue #235, indicating that it is not a duplicate nor out of scope. Allow me to provide a detailed breakdown:

Issue Distinctions:

  1. In the PoC of this issue, the Instant Manager contract has the minimum requirement for BUIDL redemption when Alice fails to redeem his share amount. With all respect, pre-sort evident is not true! instant Manager balance at the failed redeem transaction is 250_000e6 BUIDL. This line from the assertion BUIDL.balanceOf(address(ousgInstantManager)) + USDC.balanceOf(address(ousgInstantManager)) represents Alice's share after Bob's redemption, which is equivalent to 250_000e6 + 100e6.

  2. In the primary issue it points the fail of the transaction in this require statement:

    require(
      buidl.balanceOf(address(this)) >= minBUIDLRedeemAmount,
      "OUSGInstantManager::_redeemBUIDL: Insufficient BUIDL balance"
    );

while in this issue, when Alice try to redeem his OUSG token the revert expected in the PoC specifically by an error that coming from BUIDL redeemer contract (Error:'Not enough tokens'):

 vm.expectRevert('Not enough tokens');
 ousgInstantManager.redeem(aliceOUSGRedeemAmount);

Clarification on Flow of Tokens From PoC:

This flow of tokens elucidates the sequence of events and the impact on user balances during redemption attempts from the PoC.

  1. Starting with these balances:
TokensInstant Manager Contract BalancesAlice's BalanceBob's Balance
OUSG01,6671,666
BUIDL500,00000
USDC000
  1. After Bob redeem his tokens:
TokensInstant Manager Contract BalancesAlice's BalanceBob's Balance
OUSG01,6670
BUIDL250,00000
USDC1000249,900
  1. Now when Alice try to redeem his 1667e18 OUSG tokens will fail since the Instant Manager contract try to redeem BUIDL above its balance (250_100e6 BUIDL). However, Alice can only redeem an equivalent amount of OUSG less than or equal to the minimum requirement for BUIDL redemption.

In this PoC Alice only lost 100e6 USDC as it can not be redeemed, but it can be bigger as long as it is less than the minimum redemption requirement.


Given the distinctions outlined above and the implications for system functionality and user experience, I respectfully request a reconsideration of the categorization of this issue. It addresses a separate aspect of the contract's behavior and presents a valid concern that warrants attention.

Thank you for your time and consideration.

#4 - 3docSec

2024-04-11T08:08:25Z

Hi,

I've done some debugging on your PoC and I thank you for having included a runnable PoC because it helped me understand how it's indeed different than #285.

The problem you are outlining is that OUSGInstantManager unnecessarily makes the full-amount _redeemBUIDL(usdcAmountToRedeem); swap, because it has sufficient USDC balance to cover the request with a smaller _redeemBUIDL(buidl.balanceOf(address(this))) swap (given that the balance is at least equal to minBUIDLRedeemAmount + using its USDC balance.

I see this problem therefore closer to #109, but not quite the same, because the #109 fix won't fix this issue either.

I also agree that this one does not entirely fit the out-of-scope:

We are aware that if all BUIDL has been redeemed from our contract, we will not be able to provide instant redemptions for OUSG or rOUSG. We are also aware that there may be some USDC leftover in the contract that wouldn't be redeemable in this scenario.

because the balance of BUIDL being at least equal to minBUIDLRedeemAmount means that we are not in the if all BUIDL has been redeemed from our contract condition.

#5 - c4-judge

2024-04-11T08:08:34Z

3docSec marked the issue as not a duplicate

#6 - 3docSec

2024-04-11T08:13:37Z

Hi @cameronclifton we'd appreciate having a sponsor review on this one... could you please share your opinion? πŸ™

#7 - cameronclifton

2024-04-11T16:41:12Z

This was known and supposed to be covered in: ```We are aware that if all BUIDL has been redeemed from our contract, we will not be able to provide instant redemptions for OUSG or rOUSG. We are also aware that there may be some USDC leftover in the contract that wouldn't be redeemable in this scenario."``

I guess the wording we chose for the above doesn't technically cover this, but it was very much intended to be.

Our operational processes will ensure that there is sufficient BUIDL in our contract at all times relative to our global redemption limits, and do not expect this edge case to be hit.

#8 - 3docSec

2024-04-12T08:26:57Z

I see the point @cameronclifton and I agree with you that the intention is reasonable, but this issue does not clearly fall into the out-of-scope that was set in the README.

It may look like nitpicking, but we want to be fair with the warden's effort and if the finding is valid as per the contest rules, it deserves a reward.

This doesn't mean you will have to fix it, you still have the option to acknowledge it if as you say the processes outside the contest scope are enough to mitigate it.

#9 - c4-judge

2024-04-12T08:27:29Z

3docSec marked the issue as satisfactory

#10 - c4-judge

2024-04-12T08:27:32Z

3docSec marked the issue as selected for report

#11 - c4-judge

2024-04-13T17:08:10Z

3docSec marked the issue as primary issue

#12 - Brivan-26

2024-04-14T09:32:28Z

Hi @3docSec sorry for dropping the comment at this moment, but I just saw this issue is upgraded. There are some contradictions between the warden's description and the contest README phrases

Quoting from the warden's description:

The OUSG Instant Redemption Manager contract contains an oversight in its redeem function, specifically in the handling of BUIDL redemption limits. This oversight can potentially lead to failed redemption attempts when the redemption balance exceeds the BUIDL balance held by the manager contract while it has a right amount if its concatenated with USDC amount left by another redemption process..

This is wrong if we compare what was mentioned in the contest README, Quoting from contest README:

USDC may be utilized at a later time for servicing redemptions less than the minimumBUIDLRedemption amount OR retrieved through the permissioned retrieveTokens function.

It is mentioned that USDC left in the ousgManager contract will be used only to serve redemptions less than the minimumBUIDLRedemption

Also, the warden has referenced this code snippet which is a wrong assumption that the contract may not have enough BUIDL balance. Quoting from contest README:

Ondo Finance will be responsible for ensuring enough BUIDL is in the contract at all times to satisfy investor redemptions.

#13 - radeveth

2024-04-14T09:48:03Z

Hi @3docSec sorry for dropping the comment at this moment, but I just saw this issue is upgraded. There are some contradictions between the warden's description and the contest README phrases

Quoting from the warden's description:

The OUSG Instant Redemption Manager contract contains an oversight in its redeem function, specifically in the handling of BUIDL redemption limits. This oversight can potentially lead to failed redemption attempts when the redemption balance exceeds the BUIDL balance held by the manager contract while it has a right amount if its concatenated with USDC amount left by another redemption process..

This is wrong if we compare what was mentioned in the contest README, Quoting from contest README:

USDC may be utilized at a later time for servicing redemptions less than the minimumBUIDLRedemption amount OR retrieved through the permissioned retrieveTokens function.

It is mentioned that USDC left in the ousgManager contract will be used only to serve redemptions less than the minimumBUIDLRedemption

Also, the warden has referenced this code snippet which is a wrong assumption that the contract may not have enough BUIDL balance. Quoting from contest README:

Ondo Finance will be responsible for ensuring enough BUIDL is in the contract at all times to satisfy investor redemptions.

Good Point, @Brivan-26!

#14 - lanrebayode

2024-04-14T10:37:47Z

Apologies for commenting at this point but there is no contradiction here,

USDC may be utilized at a later time for servicing redemptions less than the minimumBUIDLRedemption amount OR retrieved through the permissioned retrieveTokens function.

"may be utilized" not "will be utilised only for this". May is an if statement and for every if there is an else. Hope this clears the air. Thank you, once again apologies for commenting.

#15 - khalidfsh

2024-04-14T10:41:21Z

Hi @Brivan-26 Thank you for sharing your thought! You are quoting from the impact of the issue, there is no description in this report. I encourage you to read the PoC for more in-depth details of the issue.

If you see the PoC section, even if the instant manager has enough BUIDL balance to cover all redemptions that can be done, it will fail at some point if this edge case occurs (where the USDC balance leftover from another redemption is part of the issue but it is not the issue itself). Also, here is the full quoting of the known issue you are referring to clearer to be subjected here:

We are aware that USDC may be leftover and held within OUSGInstantManager when a user performs a redemption that is more than the minimumRedemption amount and less than the minimumBUIDLRedemption amount. We are also aware that this may cause temporary "cash drag" for the overall OUSG portfolio as USDC in the OUSGInstantManager contract does not earn yield. USDC may be utilized at a later time for servicing redemptions less than the minimumBUIDLRedemption amount OR retrieved through the permissioned retrieveTokens function.

You can see that this talks about something else! and this issue dose not fall under this known issue.

Maybe a part of the mitigation suggest to use USDC leftover from other redemption to solve this issue, but the issue is here and the impact is clear. So, enough BUIDL balance is not preventing failed redemption if users redeem the full global redemption limit and this edge case occurs (which is proofed under PoC section)! it should be a sufficient BUIDL balance relative to global redemption limits where it counts this edge case and prevent it from happening.

Apologies for commenting at this point.

#16 - radeveth

2024-04-14T11:11:07Z

Hello,

I appreciate the discussion and the points raised. However, after revisiting the README and the specific operational mechanisms of the OUSG Instant Redemption Manager, I maintain that the identified issue is invalid based on the intentional design and operational safeguards implemented by Ondo Finance. Here’s a detailed breakdown of why this bug is considered out of scope or a non-issue:

  1. The Ondo Finance protocol is designed with specific operational checks that ensure the adequacy of BUIDL within the OUSGInstantManager. According to the protocol's operational procedures:

    "Ondo Finance will be responsible for ensuring enough BUIDL is in the contract at all times to satisfy investor redemptions."

    This statement implies a proactive management approach to maintaining BUIDL balances, which directly addresses concerns about potential redemption failures due to insufficient BUIDL.

  2. The concerns raised about the potential for redemption failures when BUIDL balances are low seem to misinterpret the protocol's built-in safeguards. The protocol clearly outlines mechanisms for managing these situations:

    "USDC may be utilized at a later time for servicing redemptions less than the minimumBUIDLRedemption amount OR retrieved through the permissioned retrieveTokens function."

    This indicates that leftover USDC in the ousgManager contract is a considered part of the system's design, intended to handle smaller redemptions effectively without causing operational disruptions.

  3. The phrase "may be utilized" does not imply exclusivity but rather flexibility in handling scenarios where BUIDL is temporarily insufficient. It provides a fallback mechanism to ensure that user redemptions can be processed even under constrained conditions. This flexibility is critical in managing the liquidity needs of users without compromising the integrity of the operational process.

  4. The Proof of Concept provided seems to highlight a scenario that is already accounted for in the operational guidelines of the protocol. The test cases assume a situation that the protocol's management strategy is designed to prevent. Furthermore, the focus on this specific edge case does not reflect the typical or intended use case scenarios that the protocol is designed to handle.

Based on the detailed operational guidelines and the flexible management of BUIDL and USDC within the Ondo Finance protocol, the reported issue does not constitute a valid bug but rather a misunderstanding of the system's intended functionalities and safeguards.

Apologies for commenting at this point.

#17 - Henrychang26

2024-04-15T05:28:26Z

Hi,

I would like to respectfully address the assessment provided for this issue.

Upon careful analysis and consideration, I believe there are aspects of this issue that distinguish it from issue #235, indicating that it is not a duplicate nor out of scope. Allow me to provide a detailed breakdown:

Issue Distinctions:

  1. In the PoC of this issue, the Instant Manager contract has the minimum requirement for BUIDL redemption when Alice fails to redeem his share amount. With all respect, pre-sort evident is not true! instant Manager balance at the failed redeem transaction is 250_000e6 BUIDL. This line from the assertion BUIDL.balanceOf(address(ousgInstantManager)) + USDC.balanceOf(address(ousgInstantManager)) represents Alice's share after Bob's redemption, which is equivalent to 250_000e6 + 100e6.
  2. In the primary issue it points the fail of the transaction in this require statement:
    require(
      buidl.balanceOf(address(this)) >= minBUIDLRedeemAmount,
      "OUSGInstantManager::_redeemBUIDL: Insufficient BUIDL balance"
    );

while in this issue, when Alice try to redeem his OUSG token the revert expected in the PoC specifically by an error that coming from BUIDL redeemer contract (Error:'Not enough tokens'):

 vm.expectRevert('Not enough tokens');
 ousgInstantManager.redeem(aliceOUSGRedeemAmount);

Clarification on Flow of Tokens From PoC:

This flow of tokens elucidates the sequence of events and the impact on user balances during redemption attempts from the PoC.

  1. Starting with these balances:

Tokens Instant Manager Contract Balances Alice's Balance Bob's Balance OUSG 0 1,667 1,666 BUIDL 500,000 0 0 USDC 0 0 0 2. After Bob redeem his tokens:

Tokens Instant Manager Contract Balances Alice's Balance Bob's Balance OUSG 0 1,667 0 BUIDL 250,000 0 0 USDC 100 0 249,900 3. Now when Alice try to redeem his 1667e18 OUSG tokens will fail since the Instant Manager contract try to redeem BUIDL above its balance (250_100e6 BUIDL). However, Alice can only redeem an equivalent amount of OUSG less than or equal to the minimum requirement for BUIDL redemption.

In this PoC Alice only lost 100e6 USDC as it can not be redeemed, but it can be bigger as long as it is less than the minimum redemption requirement.

Given the distinctions outlined above and the implications for system functionality and user experience, I respectfully request a reconsideration of the categorization of this issue. It addresses a separate aspect of the contract's behavior and presents a valid concern that warrants attention.

Thank you for your time and consideration.

Warden seems to make the wrong assumption that as long as OUSGInstantManager holds at least minBUIDLRedeemAmount amount, redemption should be successful. This assumption is not explicitly stated anywhere in the documentation. minBUIDLRedeemAmount simply represents the minimum amount of BUIDL that must be redeemed in a single redemption.

The root cause of this case is the lack of sufficient BUIDL tokens in OUSGInstantManager, which falls the on the responsibility of the protocol. This was clearly noted in Automated Findings / Publicly Known Issues:

We are aware that if all BUIDL has been redeemed from our contract, we would not be able to provide instant redemptions for OUSG or rOUSG. We are also aware that there may be some USDC leftover in the contract that wouldn't be redeemable in this scenario.

Also protocol does not explicitly guarantee redemption if amount of BUIDL + USDC >= usdcAmountToRedeem since it acknowledges leftover USDC to be used at later time.

We are aware that USDC may be leftover and held within OUSGInstantManager when a user performs a redemption that is more than the minimumRedemption amount and less than the minimumBUIDLRedemption amount. We are also aware that this may cause temporary "cash drag" for the overall OUSG portfolio as USDC in the OUSGInstantManager contract does not earn yield. USDC may be utilized at a later time for servicing redemptions less than the minimumBUIDLRedemption amount OR retrieved through the permissioned retrieveTokens function.

Apologies for commenting at this point.

#18 - c4-sponsor

2024-04-21T16:40:40Z

cameronclifton (sponsor) confirmed

#19 - cameronclifton

2024-04-22T22:45:59Z

Due to changing requirements, the contract will now concatenate the USDC amount with BUIDL when performing redemptions. (This should mitigate this already known issue).

Findings Information

Awards

64.1515 USDC - $64.15

Labels

bug
2 (Med Risk)
satisfactory
:robot:_26_group
duplicate-32

External Links

Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/rOUSG.sol#L624

Vulnerability details

Impact

The current implementation of the burn function in the rOUSG contract restricts the ability of the admin with the BURNER_ROLE to burn tokens from addresses that have either been removed from the KYC list (KYCRegistry::removeKYCAddresses) or added to the external chainalysis's sanction list. This is because the _beforeTokenTransfer function enforces KYC checks for all token transfer operations, including burning tokens. As a result, if an address is no longer KYC-approved or has been sanctioned, the burn function will fail to execute, preventing the admin from burning tokens as intended. This limitation can hinder essential operations, affecting the integrity and functionality of the protocol.

Proof of Concept

Let’s check the burn function which calls the internal _burnShares function (see line 632) :

contracts/ousg/rOUSG.sol:
618:   /**
619:    * @notice Admin burn function to burn rOUSG tokens from any account
620:    * @param _account The account to burn tokens from
621:    * @param _amount  The amount of rOUSG tokens to burn
622:    * @dev Transfers burned shares (OUSG) to `msg.sender`
623:    */
624:   function burn(
625:     address _account,
626:     uint256 _amount
627:   ) external onlyRole(BURNER_ROLE) {
628:     uint256 ousgSharesAmount = getSharesByROUSG(_amount);
629:     if (ousgSharesAmount < OUSG_TO_ROUSG_SHARES_MULTIPLIER)
630:       revert UnwrapTooSmall();
631: 
632:     _burnShares(_account, ousgSharesAmount);
633: 
634:     ousg.transfer(
635:       msg.sender,
636:       ousgSharesAmount / OUSG_TO_ROUSG_SHARES_MULTIPLIER
637:     );
638:     emit Transfer(address(0), msg.sender, getROUSGByShares(ousgSharesAmount));
639:     emit TransferShares(_account, address(0), ousgSharesAmount);
640:   }

554:   function _burnShares(
555:     address _account,
556:     uint256 _sharesAmount
557:   ) internal whenNotPaused returns (uint256) {
558:     require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");
559: 
560:     _beforeTokenTransfer(_account, address(0), _sharesAmount);
561: 
562:     uint256 accountShares = shares[_account];
563:     require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");
564: 
565:     totalShares -= _sharesAmount;
566: 
567:     shares[_account] = accountShares - _sharesAmount;
568: 
569:     return totalShares;
570:   }

As can be seen in _burnShares function it calls _beforeTokenTransfer in line 560 with the account that we want to burn tokens form. Here is the code of _beforeTokenTransfer:

contracts/ousg/rOUSG.sol:
586:   function _beforeTokenTransfer(
587:     address from,
588:     address to,
589:     uint256
590:   ) internal view {
591:     // Check constraints when `transferFrom` is called to facliitate
592:     // a transfer between two parties that are not `from` or `to`.
593:     if (from != msg.sender && to != msg.sender) {
594:       require(_getKYCStatus(msg.sender), "rOUSG: 'sender' address not KYC'd");
595:     }
596: 
597:     if (from != address(0)) {
598:       // If not minting
599:       require(_getKYCStatus(from), "rOUSG: 'from' address not KYC'd");
600:     }
601: 
602:     if (to != address(0)) {
603:       // If not burning
604:       require(_getKYCStatus(to), "rOUSG: 'to' address not KYC'd");
605:     }
606:   }

The _beforeTokenTransfer function enforces KYC checks for all token transfers. if we see code block in L597-L600 we can see the require statement that lock the ability of burning token from account that has false in its KYC status, since this function is also used when unwrap rOUSG it is very acceptable in its nature. However, the burn function should not incorporate these checks. As a result, the admin with the BURNER_ROLE cannot burn tokens from addresses that are no longer KYC-approved or have been added to the sanctions list.

If we dig deeper to see more about _getKYCStatus which is part of KYCRegistryClientUpgradeable contract:

contracts/kyc/KYCRegistryClient.sol:
64:   function _getKYCStatus(address account) internal view returns (bool) {
65:     return kycRegistry.getKYCStatus(kycRequirementGroup, account);
66:   }


contracts/kyc/KYCRegistry.sol:
119:   /**
120:    * @notice Get KYC status of `account` for the provided
121:    *         `kycRequirementGroup`. In order to return true, `account`'s state
122:    *         in this contract must be true and additionally pass a
123:    *         `sanctionsList` check.
124:    *
125:    * @param kycRequirementGroup KYC group to check KYC status for
126:    * @param account             Addresses to check KYC status for
127:    */
128:   function getKYCStatus(
129:     uint256 kycRequirementGroup,
130:     address account
131:   ) external view override returns (bool) {
132:     return
133:       kycState[kycRequirementGroup][account] &&
134:       !sanctionsList.isSanctioned(account);
135:   }

We can tell that the state of the KYC is managed by separate roles in a separate contract and can be revoked at any given moment either by:

  1. Address has been added to sanctions list where it checked in KYCRegistry#L134: !sanctionsList.isSanctioned

  2. Address is revoked when utilizing KYCRegistry::removeKYCAddresses by another role:

contracts/kyc/KYCRegistry.sol:
169:   /**
170:    * @notice Remove addresses from KYC list
171:    *
172:    * @param kycRequirementGroup KYC group associated with `addresses`
173:    * @param addresses           List of addresses to revoke KYC'd status
174:    */
175:   function removeKYCAddresses(
176:     uint256 kycRequirementGroup,
177:     address[] calldata addresses
178:   ) external onlyRole(kycGroupRoles[kycRequirementGroup]) {
179:     uint256 length = addresses.length;
180:     for (uint256 i = 0; i < length; i++) {
181:       kycState[kycRequirementGroup][addresses[i]] = false;
182:     }
183:     emit KYCAddressesRemoved(msg.sender, kycRequirementGroup, addresses);
184:   }

Thus, This inconsistency in enforcing KYC requirements hinders the admin's ability to perform critical token burning operations, impacting the overall functionality of the protocol.

Similar findings

Tools Used

  • Manual review

Consider these analyzings before solving the issue

  1. If the burn function will be used only by the admin; consider burning shares without checking the KYC status of the address we are burning from. But be caution here as we can not make changes to _burnShares function since it used in unwrap! perhaps another enforced burn shares function can do the job.

  2. If the burn function would be use by another contracts as of a tokenomics features like the one used by ousgInstantManager and OUSG token, then add another burn function that would be used only by the admin and enforce the burn regardless of the the KYC status of the address we are burning from.

Assessed type

Error

#0 - c4-pre-sort

2024-04-04T05:11:45Z

0xRobocop marked the issue as duplicate of #237

#1 - c4-judge

2024-04-09T08:30:33Z

3docSec 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