code4rena-2023-09-ondo-m04

[M-04] Admin can’t burn tokens from blocklisted addresses because of a check in _beforeTokenTransfer

보고서

Summary

악성 유저는 허용 리스트에 없고, 차단/제재 리스트에 있을 것이다. 허용/블락/제재 리스트 확인에서 걸려서 관리자 함수로 악성 유저의 토큰을 소각할 수 없다.

Keyword

bug, logic flaw, erc20

Vulnerability

burn 함수는 관리자가 악성 유저의 rUSDY를 소각하는 함수이다. 이들 악성 유저는 허용 리스트에 없고, 차단/제재 리스트에 있을 것이다.

function burn(
    address _account,
    uint256 _amount
  ) external onlyRole(BURNER_ROLE) {
    uint256 sharesAmount = getSharesByRUSDY(_amount);
 
@>  _burnShares(_account, sharesAmount);
 
    usdy.transfer(msg.sender, sharesAmount / BPS_DENOMINATOR);
 
    emit TokensBurnt(_account, _amount);
  }
 
  function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");
 
@>  _beforeTokenTransfer(_account, address(0), _sharesAmount);
 
    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");
 
    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);
 
    totalShares -= _sharesAmount;
 
    shares[_account] = accountShares - _sharesAmount;
 
    uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);
 
    return totalShares;
  }

토큰을 소각할 시 _beforeTokenTransfer(_account, address(0), _sharesAmount)를 호출한다. _account는 토큰을 빼앗길 악성 유저이다. 이 _account는 블락/제재 리스트에 있거나 허용 리스트에 없으므로 _beforeTokenTransfer의 리스트 확인에 걸려 소각할 수 없다.

function _beforeTokenTransfer(
    address from,
    address to,
    uint256
  ) internal view {
    // Check constraints when `transferFrom` is called to facliitate
    // a transfer between two parties that are not `from` or `to`.
    if (from != msg.sender && to != msg.sender) {
      require(!_isBlocked(msg.sender), "rUSDY: 'sender' address blocked");
      require(!_isSanctioned(msg.sender), "rUSDY: 'sender' address sanctioned");
      require(
        _isAllowed(msg.sender),
        "rUSDY: 'sender' address not on allowlist"
      );
    }
 
@>  if (from != address(0)) {
      // If not minting
@>    require(!_isBlocked(from), "rUSDY: 'from' address blocked");
@>    require(!_isSanctioned(from), "rUSDY: 'from' address sanctioned");
@>    require(_isAllowed(from), "rUSDY: 'from' address not on allowlist");
    }
 
    if (to != address(0)) {
      // If not burning
      require(!_isBlocked(to), "rUSDY: 'to' address blocked");
      require(!_isSanctioned(to), "rUSDY: 'to' address sanctioned");
      require(_isAllowed(to), "rUSDY: 'to' address not on allowlist");
    }
  }

Impact

악성 유저의 토큰을 소각할 수 없다.

Mitigation

_beforeTokenTransfer 에서 if (from != address(0)) 대신 if (from != address(0) && to != address(0)) 로 확인하여 토큰 소각시에는 리스트를 확인하지 않는다.


tags: bughunting, ondo finance, smart contract, solidity, logic flaw, erc20, severity medium