#36931 [SC-Critical] critical creators can modifyloccollateral of dynamic loc to release ....
#36931 [SC-Critical] Creators can modifyLOCCollateral of dynamic LOC to release almost all the collateral of LOC
Submitted on Nov 20th 2024 at 05:08:07 UTC by @perseverance for Audit Comp | Anvil: Letters of Credit
Report ID: #36931
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/AcronymFoundation/anvil-contracts/blob/main/contracts/LetterOfCredit.sol
Impacts:
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Protocol insolvency
Description
Description
Brief/Intro
A Letter of Credit (LOC) is a contractual agreement that guarantees payment on time and in full from a buyer (creator) to a seller (beneficiary). The Anvil LOC comprises two elements: the collateral asset, which secures the LOC, and the credited asset, representing its redeemable value. The protocol supports LOC issuance irrespective of the collateral and credit asset types.
The critical aspect of every LOC is the redeemability of its full credited value. To mitigate potential market volatility all LOCs require sufficient overcollateralization. Liquidity must be available for collateral-asset-to-credited-asset conversion, guaranteeing beneficiaries invariably receive the credited asset.
It is important that all LOCs require sufficient overcollateralization.
The beneficiary or seller can based on the creditTokenAmount to sell the buyer something based on this LOC. The Credited amount is guaranteed to be backed by collateral that is worth more than the credited amount. The buy and sell activity is managed outside of the LetterOfCredit contract.
Users can create the Dynamic LOC by using function createDynamicLOC.
For example for the pair WETH and USDC, this creationCollateralFactorBasisPoints can be 6500 that is 65% so that the credited amount should be less than 65% of the collateral. This is to ensure that the all LOCs require sufficient overcollateralization.
Also the creators of the LOC can modify the collateral by calling modifyLOCCollateral to add or releaserelease the collateral, but still need to have the sufficient overcollateralization.
The vulnerability
Vulnerability Details
The vulnerability here is in the function modifyLOCCollateral does not check if the cfBasisPoints is 0
```solidity function modifyLOCCollateral( uint96 _locId, int256 _byAmount, bytes calldata _oraclePriceUpdate, bytes calldata _collateralizableAllowanceSignature ) external payable refundExcess nonReentrant { LOC memory loc = locs[_locId];
```
Here if the newCollateralAmount is very small, then cfBasisPoints might be 0 because the collateralInCredited is 0
```solidity function collateralFactorInBasisPoints( uint256 _collateralTokenAmount, uint256 _creditedTokenAmount, OraclePrice memory _price ) internal pure returns (uint16) { uint256 collateralInCredited = collateralAmountInCreditedToken(_collateralTokenAmount, _price); // Don't divide by 0 if (collateralInCredited == 0) { return 0; } return uint16((_creditedTokenAmount * 10_000) / collateralInCredited); } ```
So the creators can modifyCollateral to withdraw almost all the collateral. By exploiting this bug, the attacker breaks the stability of the system. The system is severely under collateralized and this can make the Protocol Insolvency scenario.
The attacker can modify the LOC and back the Credited Amount with with very small amount of collateral. This will result in victim loss of funds.
By exploiting this bug, the attacker can also create a lot of dynamic LOC with small collateral and can make creditedTokens[_creditedTokenAddress].globalAmountInDynamicUse equal or nearly equal to creditedToken.globalMaxInDynamicUse so other users of the protocol will not be able to create the dynamic LOC. Because users's call to function will failed in createDynamicLOC because of this check
```solidity function _validateAndUpdateCreditedTokenUsageForDynamicLOCCreation( address _creditedTokenAddress, uint256 _creditedTokenAmount ) private { CreditedToken memory creditedToken = creditedTokens[_creditedTokenAddress]; // ... uint256 newCreditedAmountInUse = creditedToken.globalAmountInDynamicUse + _creditedTokenAmount; if (newCreditedAmountInUse > creditedToken.globalMaxInDynamicUse) revert GlobalCreditedTokenMaxInUseExceeded(creditedToken.globalMaxInDynamicUse, newCreditedAmountInUse);
```
Impacts
About the severity assessment
Bug Severity: Critical
Impact category:
Protocol insolvency
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Difficulty of the attack: Easy
It is easy to automate the attack
Link to Proof of Concept
https://gist.github.com/Perseverancesuccess2021/831d502296049011991a7af9afe31abc#file-testletterofcredit-sol
Proof of Concept
Proof of concept
Steps to execute the attack:
Step 1: Attacker create a dynamicLOC for the victim with over-collaterized amount of collateral
Step 2: Attacker call modifyLOCCollateral to release almost all the collateral
Test code to show: https://gist.github.com/Perseverancesuccess2021/831d502296049011991a7af9afe31abc#file-testletterofcredit-sol
```solidity function testModifyLOCCollateral() public { setup_precondition();
```
Explanation:
The price of WETH is 3000 USDC
For this POC, the creator created the LOC with 1 WETH and the credit amount 1900 USDC
Then the creator (attacker) modify the collateral of the LOC to withdraw almost all WETH, just left 2 wei in the LOC, but still backs the CreditedTOken amount of 1900 USDC
Log: https://gist.github.com/Perseverancesuccess2021/831d502296049011991a7af9afe31abc#file-testmodifyloccollateral_241120_1040-log
```Log Ran 1 test for test/testLetterOfCredit.sol:testLetterOfCredit [PASS] testModifyLOCCollateral() (gas: 7835812) Logs: Setup Precondition for the test case Deploying LetterOfCredit Proxy contract that is VisibleBeaconProxy points to LetterOfCredit Initialize LetterOfCredit contract Approve the LetterOfCredit_proxy as the collateralizable contract Balance of token before depositing: 1000000000000000000 Approve the CollateralVault_contract to spend token to deposit Step: Deposit token to CollateralVault for the user Attacker to create a dynamic LOC for victim Mock Price Update for WETH/USDC Mock Price Update for WETH/USDC that is about 3000 USDC for 1 WETH Loc id 1 LOC Details: collateralTokenAddress 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 LOC Details: collateralTokenAmount 1000000000000000000 LOC Details: creditedTokenAddress 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 LOC Details: creditedTokenAmount 1900000000 Attacker to modify the collateral of the LOC After attack: LOC Details: collateralTokenAddress 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 After attack: LOC Details: collateralTokenAmount 2 After attack: LOC Details: creditedTokenAddress 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 After attack: LOC Details: creditedTokenAmount 1900000000 After attack: Balance of WETH for attacker: available 999999999999999998 After attack: Reserved of WETH for attacker: reserved 2
```
Just download the zip file:
https://drive.google.com/file/d/1lKnqYK1Uk8Yp70CDTM_1-J3yzx5Vjbb9/view?usp=sharing
The test code uses Foundry. Just Unzip and run the test case:
```bash forge test --match-test testModifyLOCCollateral -vvvvv
```