15,000 USDC
View results
Submission Details
Severity: medium
Valid

Users who is not owner can burn DecentralizedStableCoin without going through DSCEngine

Summary

By using the DecentralizedStableCoin.burnFrom function, user can burn tokens without going through DSCEngine.

Vulnerability Details

DecentralizedStableCoin inherits ERC20Burnable and includes Burn feature.

contract DecentralizedStableCoin is ERC20Burnable, Ownable {

https://github.com/Cyfrin/2023-07-foundry-defi-stablecoin/blob/d1c5501aa79320ca0aeaa73f47f0dbc88c7b77e2/src/DecentralizedStableCoin.sol#L39

DecentralizedStableCoin overrides the burn function to limit the authority to burn with the onlyOwner modifier. DSCEngine contract will be set the owner of DecentralizedStableCoin contract, so to burn a token, the user should request to the DSCEngine contract.

function burn(uint256 _amount) public override onlyOwner {
uint256 balance = balanceOf(msg.sender);
if (_amount <= 0) {
revert DecentralizedStableCoin__MustBeMoreThanZero();
}
if (balance < _amount) {
revert DecentralizedStableCoin__BurnAmountExceedsBalance();
}
super.burn(_amount);
}

https://github.com/Cyfrin/2023-07-foundry-defi-stablecoin/blob/d1c5501aa79320ca0aeaa73f47f0dbc88c7b77e2/src/DecentralizedStableCoin.sol#L46-L55

But the ERC20Burnable contract also has the burnFrom function. Since this function is not overridden at DecentralizedStableCoin, a user who is not owner can directly burn the token.

function burnFrom(address account, uint256 amount) public virtual {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0a25c1940ca220686588c4af3ec526f725fe2582/contracts/token/ERC20/extensions/ERC20Burnable.sol#L35-L38

PoC can be done by adding the following test code to DecentralizedStablecoinTest.

function testPoCAnybodyCanBurn() public {
address user1 = address(0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF);
// mint token first
vm.startPrank(dsc.owner());
dsc.mint(user1, 100);
vm.stopPrank();
// approve to user1
vm.startPrank(user1);
dsc.approve(user1, type(uint256).max);
dsc.burnFrom(user1, 100);
vm.stopPrank();
}

Impact

The user can directly burn the token. If user burn the token without going through DSCEngine, the state variable of DSCEngine is not updated and the logic will not work normally.

Tools Used

vscode

Recommendations

Override burnFrom and add the onlyOwner modifier.

Support

FAQs

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