codehawks-2023-08-sparkn-l08

[L-08] DAI Tokens at Risk Due to Lack of address(0) Check in distribute

보고서

Summary

DAI는 address(0)로 토큰을 전송하는 것을 막지 않기 때문에, 수상자 주소에 address(0)가 포함되어 있다면 DAI 토큰이 영구적으로 손실될 수 있다.

Keyword

lack of input validation, transfer to address(0), asset lock

Vulnerability

USDC나 USDT의 경우 토큰 자체에서 address(0)로 보내는 것을 막는다. 하지만 DAI 토큰의 경우 address(0) 로 토큰을 전송할 수 있다.

DAI 토큰 소스코드를 보면, address(0)로 토큰을 전송하는 것을 막지 않음을 확인할 수 있다.

function transfer(address dst, uint wad) external returns (bool) {
    return transferFrom(msg.sender, dst, wad);
}
 
function transferFrom(address src, address dst, uint wad)
    public returns (bool)
{
    require(balanceOf[src] >= wad, "Dai/insufficient-balance");
    if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
        require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");
        allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
    }
    balanceOf[src] = sub(balanceOf[src], wad);
    balanceOf[dst] = add(balanceOf[dst], wad);
    emit Transfer(src, dst, wad);
    return true;
}

리워드를 분배하는 _distribute 함수에서는 수상자가 address(0)인지 아닌지를 확인하지 않는다. 따라서 수상자 주소에 address(0)가 포함되어 있다면 DAI 토큰이 영구적으로 손실될 수 있다.

    function _distribute(address token, address[] memory winners, uint256[] memory percentages, bytes memory data)
        internal
    {
        // token address input check
        if (token == address(0)) revert Distributor__NoZeroAddress();
        if (!_isWhiteListed(token)) {
            revert Distributor__InvalidTokenAddress();
        }
        // winners and percentages input check
        if (winners.length == 0 || winners.length != percentages.length) revert Distributor__MismatchedArrays();
        uint256 percentagesLength = percentages.length;
        uint256 totalPercentage;
        for (uint256 i; i < percentagesLength;) {
            totalPercentage += percentages[i];
            unchecked {
                ++i;
            }
        }
        // check if totalPercentage is correct
        if (totalPercentage != (10000 - COMMISSION_FEE)) {
            revert Distributor__MismatchedPercentages();
        }
        IERC20 erc20 = IERC20(token);
        uint256 totalAmount = erc20.balanceOf(address(this));
 
        // if there is no token to distribute, then revert
        if (totalAmount == 0) revert Distributor__NoTokenToDistribute();
 
        uint256 winnersLength = winners.length; // cache length
        for (uint256 i; i < winnersLength;) {
            uint256 amount = totalAmount * percentages[i] / BASIS_POINTS;
            erc20.safeTransfer(winners[i], amount);
            unchecked {
                ++i;
            }
        }
 
        // send commission fee as well as all the remaining tokens to STADIUM_ADDRESS to avoid dust remaining
        _commissionTransfer(erc20);
        emit Distributed(token, winners, percentages, data);
    }

DAI는 프로토콜 개발자가 이용한다고 예시로 든 토큰 중 하나이므로 특별히 주의를 기울일 필요가 있다.

Impact

수상자 주소에 address(0)가 포함되어 있다면 DAI 토큰이 영구적으로 손실될 수 있다.

Mitigation

수상자 중 address(0)가 있는지 확인한다.


tags: bughunting, sparkn, smart contract, solidity, lack-of-input-validation-vul, erc20, transfer to address(0), asset lock, severity low