Nervos Fans

智能合约溢出攻击、下溢攻击

2018-07-13  本文已影响11人  526ba0512193

看个图:

这是个计算汽车行驶距离的里程表。 范围是000000 到999999。说明啥,车开了100万公里后,里程表直接回到000000。

细思极恐。

回看下2000年的千年虫(Y2K)。 Y2K是一类计算机漏洞,堪称假想中的大浩劫。

千禧危机、千年虫、千年问题 。千年问题可以追溯到二十世纪六十年代。当时计算器内存非常宝贵,故而编程人员一直借助使用 MM/DD/YY 或 DD/MM/YY 即月月/日日/年年或日日/月月/年年的方式来显示年份,但是当年序来到公元2000年的1月1日,系统却无法自动辨识00/01/01究竟代表1900年的1月1日,还是2000年的1月1日,所有的软硬件都可能因为日期的混淆而产生资料流失、系统死机、程序紊乱、控制失灵等问题,如此所造成的损失以及灾难是无法估计想像的。

上面的两个例子叫‘整数溢出’,属于一类错误。今天,我们就来探讨下溢出和下溢攻击对智能合约造成的影响。

溢出错误

数字增长超过其最大值时发生溢出。 好比声明一个uint8变量(8位的无符号变量)。 意思是,变量的数值范围0到28-1(255)。

看下边:

uint 8 a = 255;

a++;

发生溢出,因为a的最大值是255。

Solidity最大能处理256位数字,最大值为2256-1,加1会导致归 0,发生溢出:

0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+ 0x000000000000000000000000000000000001

—————————————-

= 0x000000000000000000000000000000000000

用简单的token转账合约看看溢出错误(代码取自GitHub):

mapping (address => uint256) public balanceOf;

// INSECURE

function transfer(address _to, uint256_value) {

    /* Check if sender has balance */

    require(balanceOf[msg.sender] >= _value);

    /* Add and subtract new balances */

    balanceOf[msg.sender] -= _value;

    balanceOf[_to] += _value;

}

// SECURE

function transfer(address _to, uint256_value) {

    /* Check if sender has balance and for overflows */

    require(balanceOf[msg.sender] >= _value && balanceOf[_to] +_value >= balanceOf[_to]);

    /* Add and subtract new balances */

    balanceOf[msg.sender] -= _value;

    balanceOf[_to] += _value;

}

什么意思?

上面程序中有两个“转账”函数(安全、不安全)。 一个有检查溢出,另一个没有。 安全转账函数有检查余额是否到达最大值。

注意一点,特别在上面的例子中,安全函数不一定必须被包含。 就是说作为开发者,对余额达到上限的可能性自己得有个判断,免得花无谓的gas。

下溢错误

好了,看看另个极端,也就是下溢错误。Over跟under是反义词。这俩错误也是反着来的。

uint8只能取0到255之间的值,还记得吧? 那么,考虑以下代码。

unint8 a = 0;

a–;

这就是下溢,后果是a的最大可能值是255。

出现在Solidity智能合约中,就是这:

0x000000000000000000000000000000000000

– 0x000000000000000000000000000000000001

—————————————-

= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

意思是,造成严重的数据错误表示。

还是用合约代码解释下,下溢的严重后果(代码取自GitHub):

mapping (address => uint256) public balanceOf;

// INSECURE

function transfer(address _to, uint256_value) {

    /* Check if sender has balance */

    require(balanceOf[msg.sender] >= _value);

    /* Add and subtract new balances */

    balanceOf[msg.sender] -= _value;

    balanceOf[_to] += _value;

}

// SECURE

function transfer(address _to, uint256_value) {

    /* Check if sender has balance and for overflows */

    require(balanceOf[msg.sender] >= _value && balanceOf[_to] +_value >= balanceOf[_to]);

    /* Add and subtract new balances */

    balanceOf[msg.sender] -= _value;

    balanceOf[_to] += _value;

}

这个例子中,因为动态数组是以顺序方式存储的,黑客可以利用manipulateMe,然后这么做:

调用popBonusCode产生下溢

计算manipulateMe的存储位置

使用modifyBonusCode修改并更新manipulateMe的值

当然了,单独函数中能一眼找出错误。 试想,有个超复杂的智能合约,几千行代码,代码检查也能一眼找出错误么?

溢出攻击、下溢攻击的危害

下溢错误发生的几率高于溢出错误,因为某人为引发溢出获取所需数量token(最大值就是)的可能性相当低。

假设Bob手里有X个token,但是不妨碍他想花掉X+1个。

若程序不检查一下Bob的余额,就有可能什么呢,Bob最后还真花掉了X+1个token。

看看下面的例子(取自nethemba):

pragma solidity ^0.4.22;

contract Token {

mapping(address => uint) balances;

function transfer(address _to, uint _value)public {

  require(balances[msg.sender] – _value >=0);

  balances[msg.sender] -= _value;

  balances[_to] += _value;

}

}

代码中,“转账”函数中的require条件乍一看好像没问题,注意交易双方之间的操作产生的是单位值。

意思是,不管条件为何,balances[msg.sender]– _value >= 0的值永远为真。

这种情形下,黑客可以最大化自己的余额。

比方说, 黑客有100个token,但是他想要101个。最后会得到100 - 101个token,因为下溢,一共得来2 256-1个token。

注释:

关于无符号数减去无符号数的用法错误:

if ( i - j >=0) 假如i,j为无符号数,这样写可能会引发错误,即当i小于j的时候,这个式子仍然成立,因为无符号数始终是大于等于零的。例: if ( strlen( a ) >= 10) 与 if (strlen ( b ) -10 >= 0) 这两条语句是不相等的 ,因为strlen函数返回的是无符号数类型。

意思是:

无符号数相减,如果被减数小于减数,那么结果会是一个非常大的无符号数,而不是一个想象中的有符号数。

所以对于无符号数相减之前需要进行判断,最好做比较的时候使用 if ( strlen( a ) >=

10) 这种方式,而不要使用if (strlen ( b ) -10 >= 0) 这种方式。因为无符号数进行计算的结果还是无符号数;另外无符号数和有符号数计算时,有符号数会被强制转提升无符号数。

再然后,系统可能就瘫了。

解决方法是总是检查代码中的下溢或上溢。这里有安全库协助检查,例如SafeMath或者OpenZeepelin。

现实生活中的下溢问题

有个准庞氏币叫POWH(Proof of Weak Hands Coin)。然后买的人还不少,筹了100多万刀。

但是,POWH开发者对操作的安全性不太上心,无法妥善应溢出或下溢攻击。然后,有个黑客瞄准这个弱点,直接卷了2000个ETH,差不多230万刀的样子。

这里想说的是,作为开发者,真是要加强对这种攻击是防御;作为赏金猎人的话,也是不应该对这种攻击放松警惕的。

Overflow and Underflow Attacks on Smart Contracts (Blockgeeks Guide)​blockgeeks.com

上一篇下一篇

猜你喜欢

热点阅读