codehawks-2023-08-sparkn-m03

[M-03] Malicious/Compromised organiser can reclaw all funds, stealing work from supporters

보고서

Summary

주최자를 무조건 신뢰하기 때문에 주최자가 자신의 계정으로 리워드를 전부 꺼내갈 수 있다. 주최자는 제안을 하고 관리자가 최종 확인을 해야 토큰을 이동할 수 있도록 하면 주최자에게 너무 많은 권한이 있는 것을 완화할 수 있다.

Keyword

lack of input validation, centralization

Vulnerability

컨테스트 리워드로 토큰을 예치하는 이는 후원자이며(주최자만의 자금이 아니라는 의미같다), 컨테스트에 펀딩을 하면 환불받을 방법은 없다고 문서에 명시했다. 하지만 이는 사실이 아니며, 주최자가 모든 컨테스트 자금을 환불할 수 있다.

리워드를 분배하는 _distribute 함수에서 리워드를 받는 winners 배열에 대한 입력값 검사가 없다. 따라서 주최자는 자신이 관리하는 지갑으로 토큰을 보낼 수 있다. 따라서 주최자의 계정이 탈취되거나 주최자가 악의적이라면 상금을 전부 꺼내갈 수 있다. 관리자는 EXPIRATION_TIME(7일)이 지나야 개입이 가능하기 때문에 이를 막을 수 없다.

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

Impact

악의적인 주최자가 리워드를 자신의 계정으로 전부 가져갈 수 있다.

Mitigation

리워드를 분배할 때 두 단계를 이용한다.

  1. 주최자가 리워드를 받을 주소와 비율을 지정하여 변수에 저장해둔다.
  2. 신뢰할 수 있는 프로토콜 관리자가 모든 자금을 주최자가 가져가진 않는지 확인하고 이상이 없는 경우 토큰을 이동한다.

이렇게 하면 주최자를 신뢰해야 하는 위험이 제거되어 전반적으로 완화된다.

Memo

컨테스트 전반에 걸쳐 악의적인 관리자는 Low 또는 Invalid라고 했는데, 이 취약점만은 Medium으로 인정해줬다. 주최자가 악성 유저인 경우는 인정하는가? 주최자는 외부인이 될 수 있으니 신뢰할 수 없다고 여기는 건가?(그런듯)


tags: bughunting, sparkn, smart contract, solidity, lack-of-input-validation-vul, rug pull, centralization, severity medium