codehawks-2023-07-dsc-m05

[M-05] Anyone can burn DecentralizedStableCoin tokens with burnFrom function

보고서

Summary

burn 함수에는 권한 설정을 했지만 burnFrom 함수에는 설정하지 않았다. 따라서 권한을 우회하여 토큰을 소각할 수 있다.

Keyword

access control, burnable, openzeppelin, erc20

Vulnerability

DSC 토큰은 Openzeppelin 의 ERC20Burnable 컨트랙트를 상속한다. 이 컨트랙트에는 기본적인 burn 함수와 burnFrom 함수가 구현되어 있다.

abstract contract ERC20Burnable is Context, ERC20 {
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }
 
    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        _spendAllowance(account, _msgSender(), amount);
        _burn(account, amount);
    }
}

DSC 토큰은 burn 함수를 오버라이드하여 onlyOwner modifier를 붙였다. 따라서 관리자인 DSCEngine 컨트랙트에 의해서만 토큰의 물량이 조절되도록 제한한다.

    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);
    }

하지만 burnFrom 함수는 오버라이드 하지 않았다. 따라서 누구나 자신 또는 approve 받은 DSC 토큰을 소각할 수 있다.

Impact

onlyOwner를 우회하여 DSC 토큰을 소각할 수 있다.

Mitigation

burnFrom를 오버라이드하여 아무나 호출할 수 없도록 한다. 또는 함수를 사용할 수 없도록 한다.

@@ -40,6 +40,7 @@ contract DecentralizedStableCoin is ERC20Burnable, Ownable {
     error DecentralizedStableCoin__MustBeMoreThanZero();
     error DecentralizedStableCoin__BurnAmountExceedsBalance();
     error DecentralizedStableCoin__NotZeroAddress();
+    error DecentralizedStableCoin__BlockFunction();
 
     constructor() ERC20("DecentralizedStableCoin", "DSC") {}
 
@@ -54,6 +55,10 @@ contract DecentralizedStableCoin is ERC20Burnable, Ownable {
         super.burn(_amount);
     }
 
+    function burnFrom(address, uint256) public pure override {
+        revert DecentralizedStableCoin__BlockFunction();
+    }
+
     function mint(address _to, uint256 _amount) external onlyOwner returns (bool) {
         if (_to == address(0)) {
             revert DecentralizedStableCoin__NotZeroAddress();

tags: bughunting, codehawks, smart contract, solidity, access control vulnerability, openzeppelin, erc20, severity medium