[Solidity] Denial of Service

임형석·2023년 10월 15일
0

Solidity


Denial of Service

Denial of Service 는 서비스의 작동을 망가뜨리는 것을 말한다.

간단하게 아래의 코드로 예시를 들어보면,

contract EtherGame {
    address public owner;
    uint public balance;

    function getOwner() external payable {
        require(msg.value > balance, "Not enough balances.");

        payable(owner).transfer(msg.value);
        balance += msg.value;
        owner = msg.sender;
    }
}

EtherGame 컨트랙트는 owner 에게 현재 balance 이상의 이더를 지불하고 본인이 새로운 owner 가 될 수 있다.

이 컨트랙트를 망가뜨릴 수 있는 방법이 있다.

contract Attack {
    function attack(EtherGame _EtherGame) public payable {
        _EtherGame.getOwner{value : msg.value}();
    }
}

Attack 컨트랙트에서 fallback 또는 receive 함수를 선언해두지 않는 것이다.

컨트랙트가 이더를 받기 위해서는 위 두 함수가 필요하고, 그렇지 않다면 모든 트랜잭션은 revert 될 것이다.


위의 코드를 직접 확인해보았다.

1이더를 사용해 한 사용자가 새로운 owner 가 되었다.

그리고 2이더를 지불하고 Attack 컨트랙트가 새로운 오너가 되었다.

그리고 새로운 사용자가 4이더를 지불하고 owner 가 되기 위해 gerOwner 함수를 실행했으나, 트랜잭션이 revert 된다.


Pull over Push

위의 공격은 Pull over Push 패턴 이라고 불리는 방법으로 처리한다.

입금과 출금으로 두개의 함수로 나누어 처리하는 것이다.

현재는 함수를 실행하면, 사용자의 주소에서 owner 의 주소로 곧바로 이더가 전송되는 구조이다.

이런 경우에는 컨트랙트에서 fallback 또는 receive 를 선언하지 않는 것을 미리 예측하거나 막을 수 있는 방법이 없다.

아래의 예시 코드처럼 수정하면 문제 없이 잘 작동할 것이다.

contract EtherGame_Defense {
    address public owner;
    uint public balance;
    mapping(address => uint) ownerBalance;

    receive() external payable { }

    function deposit() external payable {
        require(msg.value > balance, "Not enough balances.");

        payable(address(this)).transfer(msg.value);
        balance += msg.value;
        ownerBalance[owner] += msg.value;
        owner = msg.sender;
    }

    function withdraw() external {
        require(msg.sender != owner,"Not allowed for current owner.");

        uint amount = ownerBalance[msg.sender];
        ownerBalance[msg.sender] = 0;
        payable(msg.sender).transfer(amount);
    }
}

컨트랙트가 owner 거래에 대한 계약의 중개자로써 활용되는 것이다.
deposit 으로 컨트랙트에 이더를 입금. withdraw 는 이전에 owner 였던 특정 주소만이 이더를 가져갈 수 있다.

공격에는 안전해지겠지만 사용자들은 입금, 출금 두번으로 나누어 실행해야하기에 유저 경험적인 부분이나 가스비 소비에서 손해를 보게 된다.


0개의 댓글