Algo Ssstablecoinsss

First Flight #30
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: high
Invalid

Insufficient Validation of DSC Token Mint and Burn Success

Summary

The _mint_dsc and _burn_dsc functions in the DSC Engine contract were found to lack sufficient validation when interacting with the external DSC.mint and DSC.burn_from functions. The absence of success checks led to potential inconsistencies between the contract's internal state and the actual token balances. This could cause incorrect assumptions about the system's financial health, exposing the contract to potential exploits.

Vulnerability Details

Root Cause:

  • The functions originally used extcall DSC.mint and extcall DSC.burn_from without verifying the return status.

  • No post-operation balance verification was performed, allowing silent failures of the mint or burn operations.

Vulnerability Mechanics:

  • If the mint or burn_from function in the DSC token implementation failed internally but did not revert, the contract’s internal records (user_to_dsc_minted) would still be modified.

  • This could lead to a dangerous mismatch between the recorded and actual token balances.

Impact

  • State Inconsistency: Internal records of minted or burned tokens could become desynchronized from the actual token balances.

  • Financial Risk: The system could experience miscalculations during liquidation or accounting processes, leading to insolvency or other financial risks.

  • Exploit Potential: A malicious token implementation could manipulate the state without performing mint or burn operations, causing irreversible inconsistencies.

Proof

To demonstrate the vulnerability, a test was constructed using a mock implementation of the DSC contract where mint function intentionally failed without reverting. After calling the mint function from the DSC Engine contract, the internal state of the system still reflected successful operations, while actual balances remained unchanged.

Unit Test

# Test to demonstrate why we need to check mint/burn success
def test_mint_burn_success_check(dsce, weth, eth_usd):
USER = boa.env.generate_address()
DEPOSIT_AMOUNT = to_wei(1, "ether")
MINT_AMOUNT = to_wei(100, "ether")
# Setup
INITIAL_PRICE = 2_000 * 10**8 # $2000 USD
eth_usd.updateAnswer(INITIAL_PRICE)
# Deploy failing DSC mock
failing_dsc = boa.load('tests/mocks/FailingDSC.vy')
# Deploy new DSC Engine with failing DSC - using arrays of size 2
dsce_vulnerable = boa.load(
'src/dsc_engine.vy',
[weth.address, weth.address], # Need 2 token addresses
[eth_usd.address, eth_usd.address], # Need 2 price feeds
failing_dsc.address
)
# Give user some WETH and approve DSC Engine
weth.mint(USER, DEPOSIT_AMOUNT)
with boa.env.prank(USER):
weth.approve(dsce_vulnerable.address, DEPOSIT_AMOUNT)
dsce_vulnerable.deposit_collateral(weth, DEPOSIT_AMOUNT)
# Try to mint - this should fail but doesn't
dsce_vulnerable.mint_dsc(MINT_AMOUNT)
# Check the inconsistency
minted_amount = dsce_vulnerable.user_to_dsc_minted(USER)
actual_balance = failing_dsc.balanceOf(USER)
print(f"Internal record of minted DSC: {minted_amount/1e18}")
print(f"Actual DSC balance: {actual_balance/1e18}")
# This shows the dangerous state inconsistency
assert minted_amount != actual_balance, "State should be inconsistent due to failed mint"
assert minted_amount > 0, "Internal state updated despite failed mint"
assert actual_balance == 0, "No tokens should have been minted"

Mock DSC to be added in tests/mocks

# @version 0.4.0
"""
@title Mock DSC that fails silently on mint
"""
from ethereum.ercs import IERC20
implements: IERC20
totalSupply: public(uint256)
balanceOf: public(HashMap[address, uint256])
allowance: public(HashMap[address, HashMap[address, uint256]])
@external
def transfer(_to: address, _value: uint256) -> bool:
return True
@external
def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
return True
@external
def approve(_spender: address, _value: uint256) -> bool:
return True
@external
def mint(to: address, amount: uint256) -> bool:
# Silently fail without minting
return False
@external
def burn_from(from_: address, amount: uint256) -> bool:
# Silently fail without burning
return False

Tools Used

  • Manual Code Review

  • AI (Chat GPT, Claude)

Recommendations

** Check Balances Before and After Operations:** Verify token balances before and after mint and burn operations to ensure proper execution.

Corrected Implementation Example:

@internal
def _mint_dsc(amount_dsc_to_mint: uint256):
assert amount_dsc_to_mint > 0, "DSCEngine__NeedsMoreThanZero"
self.user_to_dsc_minted[msg.sender] += amount_dsc_to_mint
self._revert_if_health_factor_is_broken(msg.sender)
# Get initial balance
initial_balance_response: Bytes[32] = raw_call(
DSC.address,
abi_encode(msg.sender, method_id=method_id("balanceOf(address)")),
max_outsize=32,
revert_on_failure=True
)
initial_balance: uint256 = convert(initial_balance_response, uint256)
# Attempt to mint tokens
extcall DSC.mint(msg.sender, amount_dsc_to_mint)
# Get final balance
final_balance_response: Bytes[32] = raw_call(
DSC.address,
abi_encode(msg.sender, method_id=method_id("balanceOf(address)")),
max_outsize=32,
revert_on_failure=True
)
final_balance: uint256 = convert(final_balance_response, uint256)
assert final_balance == initial_balance + amount_dsc_to_mint, "DSCEngine_MintingFailed"

This implementation ensures accurate balance validation before and after mint operations, minimizing the risk of state inconsistency.

Updates

Lead Judging Commences

bube Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.