一、前言

在前文中,我们简单提及了“智能合约”的概念以及其安全属性。之后针对一些严重的事件展开合约安全的深入分析。在本文中,我们详细的介绍一下智能合约的概念,并且为大家讲述一些关于智能合约方面的攻击事例。并针对相关事例列举防御手段。

而我们知道智能合约的漏洞常存在于以太坊的Solidity中,所以本文中会有大量的合约代码分析。我也会为大家讲述相关合约分析流程。

本文为原创稿件,如有疑问大家可以在下方留言。

二、智能合约详述

1 智能合约概念

智能合约是20世纪90年代被提出的一个概念。由于缺少可信的执行环境,所以智能合约并没有被应用到实际的产业中。自比特币诞生以后,人们逐渐认识到区块链是天生的可以为智能合约提供可信执行环境的平台。简单地说,智能合约是代码与数据的集合。其被部署在区块链的某个具体地址中。智能合约类似于区块链平台中的一个自动化机器人代理,它有自己的账户并可以在某些时间与事件的驱使下自动执行一些事情。例如可以传递信息、修改区块链状态等。

“智能合约”的程序不仅仅是一个可以自动执行的计算机程序,它可以对接收到的信息进行回应。作为一个系统必不可少的参与者,其可以接收和存储价值并向外部发送信息。以太坊的智能合约是以太坊特定的字节码,我们称为EVM字节码。

2 智能合约编程语言

不同的区块链项目使用不同的程序语言作为其智能合约的编程语言。R3的Corda使用的是Java;HyperLedger Fabric使用Java、Go开发其ChainCode链码。而根据大量的文献参考,我发现现在合约的问题多自以太坊的Solidity,所以我们在这里多讲述一下相关内容。

以太坊智能合约默认的编程语言是Solidity,智能合约的运行环境是以太坊虚拟机(EVM)。在报智能合约部署到以太坊网络之前需要先将智能合约编译成字节码,然后以交易的形式将智能合约的字节码发送到以太坊网络中,以太坊会为每一个智能合约创建一个合约账户并保存于此。

3 DApp概念

DApp全程Decentralized APP,是以太坊中基于智能合约应用的去中心化的应用程序。DApp的目标是让智能合约有一个友好的界面。DApp可以在以太以太坊节点交互的中心化服务器上运行,也可以在任意一个以太坊平等节点上运行。

然而DApp不能在普通的服务器上运行,其需要提交交易到区块链并且从区块链中提取重要数据。对于用户登录系统,用户很有可能被表示成一个“钱包”地址,而其他用户数据被保存到本地。

下面交代一下其流程:

  • 1用Solidity编写智能合约。

  • 2用solc编译器将合约编译为EVM字节码。

  • 3编译好的字节码发送到DApp前端。

  • 4前端将合约部署到区块链中。

  • 5区块链返回智能合约地址。

  • 6前端通过地址+ABI+nonce调用只能合约。

  • 7智能合约开始处理。

三、EVM虚拟机硬性限制安全

以太坊虚拟机(EVM)对于合约能够做的事情存在许多硬性的限制。为了平台安全的考虑,许多函数都有其潜在的内容,如果不了解其用法就盲目的使用有可能会带来十分严重的安全隐患。

1 变量类型出错

例如在以太坊的Solidity中有unit8变量,而uint8的存储范围为0 ~ 255,而int8的范围为-127 ~ 127。而我们下面看一组代码:

contract Baduse
{address[] employees;function pay(){for(var i = 0 ; i < employees.length; i++){address employee = employees[i];uint bonus = calculatebonus(employee);employee.send(bonus);}}function calculateBonus(address employee) return (uint){funcs;//许多花费巨大的计算}
}

我们大致阅读下上述代码,我们发现代码含义为传入employees地址,并且放入到相关地址数组中,之后循环send相应的数值。

然而不只读者有没有发现,作为智能合约其中潜在了三个问题。这里介绍第一种。

这里存在一个陷阱,是一些编译器微妙的情况还没有告诉你。

首先我们需要知道,在for (var i = 0; i < arrayName.length; i++) { ... },  i的类型是uint8,因为这是存放值0最小的类型。如果数组元素超过255个,则循环将不会终止,知道Gas用尽。

所以上述代码中倘若地址数组中的数量超过了255,那么用户就会发现自己的合约莫名其妙的一直处于运行状态,到了最后Gas被耗尽。

而要解决上述的问题,我们需要将var变量修改为nint,因为uint8是保持值为0所需的最小类型。而我们需要将函数修改为如下:

contract Baduse
{address[] employees;function pay(){for(uint i = 0 ; i < employees.length; i++)//在此处修改uint{address employee = employees[i];uint bonus = calculatebonus(employee);employee.send(bonus);}}function calculateBonus(address employee) return (uint){funcs;//许多花费巨大的计算}
}

2、Gas限制

根据往期文章中所写的内容,我们讲述过在以太坊中为了防止恶意者的作恶攻击,我们特意设立了Gas机制。而上述的代码中同样存在了一些不完善的情况导致用户Gas用尽的情况出现。

我们在代码中可以关注下面的语句:

uint bonus = calculatebonus(employee);
employee.send(bonus);

calculatebonus()函数在一些复杂的基础计算中会对每一位员工所获得的奖金进行计算,然而就想计算项目的利润,这会花费用户大量的Gas。而用户的Gas是有限的,所以势必会有一天Gas被快速消耗。如果Gas达到消耗的限制,那么所有的变化将会被修复,但是Gas费用仍然无法退回。所以每每我们发现自己的代码中存在循环时,我们应该注意可变的Gas成本问题。

对于此类问题,我们可以通过将奖金计算从for循环中分离出来并进行优化。

contract Baduse
{address[] employees;mapping(address => uint) bonuses;function pay(){for(uint i = 0 ; i < employees.length; i++){address employee = employees[i];uint bonus = calculatebonus(employee);employee.send(bonus);//直接调用send函数,并没有进行检查验证}}function calculateBonus(address employee) return (uint){uint bonus = 0;//可以手动对此进行调控bonuses[employee] = bonus;//修改奖金的额度}
}

3 、调用堆深度限制

调用深度(call depth)被限制为 1024。EVM 中一个智能合约可以通过 message call 调用其它智能合约,被调用的智能合约可以继续通过 message call 再调用其它合约,甚至是再调用回来(recursive)。

这意味着如果嵌套调用的数量达到1024,合约将会失败。攻击者可以递归调用一个合约1023次,然后调用开发者合约函数,造成“send”因为这个限制而失败。

例如下面的代码:

function sendether() {address addr = 0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b;addr.send(20 ether);//用户会认为无论如何此函数都会被处理var thesendok = true;//用户会认为无论如何都会返回true...
}

之后我们也初始化fallback函数:

function fallback() {//fallback函数内容
}

至此用户会认为我们既然已经定义了fallback函数,那么应该就万无一失了。然而我们看下面的攻击代码:

function hack() {var count = 0;while (count < 1023) {this.hack();count++;}if (count == 1023) {thecallingaddr.call("sendether");}
}

此处攻击者利用了1024这个限度,当嵌套调用数量执行到1024个的时候,攻击者调用了sendether()函数,并使系统在执行到send的时候失败。

这也为我们写合约代码的时候提了一个醒。那我们如何更改相关内容呢?

正确的写法应该是在每次涉及到 call depth 增加的地方都检查调用返回是否正确,如下:

function sendether() {address addr = 0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b;if (!addr.send(20 ether)) {throw;//防止有人攻击,提前进行检查}var thesendok = true; ...
}

四、交易顺序依赖性

1 交易顺序依赖攻击模型

我们知道,一笔交易在被传播出去之前需要经过矿工的同意并将其记账在一个区块内。而我们知道倘若一个攻击者在监听到网络中对应合约的交易后发送一个新的合约交易来改变当前的合约状态,那么系统中会因为交易的先后问题而产生安全隐患。例如悬赏相关的合约,倘若两个悬赏合约放出,第二个合约减少了合约的回报,则定几率使这两笔交易包含在同一个区块下面,并且使修改过的那个合约由于某种原因先进行记账处理。这意味着,如果某个用户正在揭示拼图或其他有价值的秘密的解决方案,恶意用户可以窃取解决方案并以较高的费用复制其交易,以抢占原始解决方案。

对于此类问题,我们提出一种基本的攻击模型:

  • 1 攻击者可以提出一个有奖竞猜合约,而此合约的目的是让用户参与某个问题的求解,并承诺给找到解的用户发放丰厚的奖励。

  • 2 当合约提交后攻击者开始监听整个网络,并观察是否有人提及相关问题的解。

  • 3 当有人提交答案后,由于交易还未被记账(存在时间差)所以攻击者可以立刻发起一个新的交易以降低奖金的数额使之无限接近0。

  • 4 此时攻击者增大了第二次发送的交易的gas值,所以此交易会被优先处理。

-5 矿工将第二个交易优先处理后,答案提交者所获得的奖励将变得极低,攻击者就能几乎免费的获得正确答案。

2 ERC20标准

ERC-20 标准是在2015年11月份推出的,使用这种规则的代币,表现出一种通用的和可预测的方式。简单地说,任何 ERC-20 代币都能立即兼容以太坊钱包(几乎所有支持以太币的钱包,包括Jaxx、MEW、imToken等,也支持 erc-20的代币),由于交易所已经知道这些代币是如何操作的,它们可以很容易地整合这些代币。这就意味着,在很多情况下,这些代币都是可以立即进行交易的。

  ERC20 让以太坊区块链上的其他智能合约和去中心化应用之间无缝交互。一些具有部分但非所有ERC20标准功能的代币被认为是部分 ERC20兼容,这还要视其具体缺失的功能而定,但总体是它们仍然很容易与外部交互。

  因此ERC-20协议是目前数字货币交易体系中较为主流的一种协议体系。但是该协议也存在不完善的地方。正如同清扫房间,总会有没有看到的地方一样,智能合约的协议只能不断地根据漏洞来改进,却不能一劳永逸的解决所有漏洞。黑客们也正是利用这些漏洞来实现自己的目的。

详细概念请参考ERC-20

3 相关安全事件

我们在这里简单地给大家讲解一下某次ERC-20标准下的真实顺序交易攻击案例。

ERC20标准中定义,approve(address _spender,uint256 _value):允许_spender提取限额_value的代币

ERC-20代币合约内置了很多函数,允许用户查找账户的余额,以及通过各种各样的合约从一个用户转移到其他用户中。

approve()这个函数通过两个步骤实现从一个用户发送给其他用户token。第一步token拥有者给另一个地址(通常都是智能合约的地址)批准转出一个最大上限的token,也就是额限。token拥有者使用approve()函数提供这个信息。

然而这个漏洞是由于这个approve()功能而产生的。

假设我们现在存在两种用户A与B,而B是攻击者,A是受害者。

  • 1 A允许B通过调用approve()方法传输N个A的token值。

  • 2 过了一段时间,A发现他需要改变对B的传输token值的个数,从N到M。之后她再次调用approve()方法并在这个时间传递给B的地址和M作为方法参数。

  • 3 B发现此消息后快速发送A的第二笔交易调用方法转移N个A的token的事件(使第一次事件的执行时间比第二次提前)。

  • 4 如果B的交易将在A交易之前执行,那么B将会执行成功转移N个A的代币并获得转移另一次M个代币的能力。

  • 5 在A注意到出现问题之前,B调用了transferFrom方法,这次转移M个A的代币。

四、参考文献

  • 1 http://finance.sina.com.cn/blockchain/coin/2018-04-26/doc-ifztkpin0534100.shtml

  • 2 https://www.jianshu.com/p/a3a197ce9268

  • 3 https://blog.csdn.net/xuguangyuansh/article/details/81329671

  • 4 https://blog.csdn.net/xuguangyuansh/article/details/83416560

  • 5 https://blog.csdn.net/weixin_42451205/article/details/80966049

  • 6 http://wiki.jikexueyuan.com/project/solidity-zh/miscellaneous.html

区块链安全—详谈合约攻击(二)相关推荐

  1. 区块链安全—详谈共识攻击(三)

    一.前言 我们在前面进行了两讲共识机制,在前文的讲述中,我们讲解了部分共识的具体流程以及优缺点.本文中,我们补充剩下的共识理念并分析其对应的优缺点.除此之外,我们针对相关的共识漏洞进行分析,为安全爱好 ...

  2. 区块链安全—详谈共识攻击(四)

    一.前言 我们在前文讲述了许多区块链这几年发展演进过来的共识机制.在之前的内容中,我们讲述的共识多属于区块链1.o与2.0的知识.这次,我们着重讲述下区块链3.0时代的HyperLedger Fabr ...

  3. 波音公司用区块链打击GPS欺骗攻击

    点击上方 "蓝色字" 可关注我们! 暴走时评:波音公司申请新的专利,探索如何利用区块链保护航班GPS接收机.利用区块链保存并保护数据,一旦遇到伪造位置信息或者无法接收信息,可以从区 ...

  4. 区块链的简单介绍(二)

    区块链的简单介绍(二) 什么是PoW共识机制 所谓PoW机制全称是proof of work,也就是工作量证明.最开始这个技术是被用来解决垃圾邮件的问题.不过后来中本聪发现这个技术能有效解决拜占庭将军 ...

  5. 详解区块链,智能合约,去中心化应用

    文章目录 一.区块链是什么? 二.智能合约 三.去中心化应用 一.区块链是什么? 区块链本质上是一个去中心化的分布式账本数据库,目的是解决交易信任问题.广义来看,区块链技术是利用块链式数据结构验证与存 ...

  6. 从区块链中常见的攻击类型谈区块链的隐私与安全

    本文整理自Parity亚洲技术总监贾瑶琪先生在万向区块链蜂巢学院直播间进行的Web 3.0训练营公开课. 过去几年,从比特币到以太坊,区块链系统从最初的分布式账本功能,慢慢进化到现在类似于分布式计算机 ...

  7. 论文笔记:使用区块链和智能合约打击深度假冒视频

    摘要 随着人工智能(AI)和深度学习技术的兴起,近年来虚假数字内容激增.假镜头.假图像.假音频和假视频(被称为深度假冒,Deepfake)可能是一种危险的现象,有可能改变真相,并通过伪造事实来侵蚀信任 ...

  8. [区块链安全-Damn-Vulnerable-DeFi]区块链DeFi智能合约安全实战-连载中

    [区块链安全-Damn-Vulnerable-DeFi]区块链DeFi智能合约安全实战-连载中 前言 环境准备 1. unstoppable 任务分析 发起攻击 总结 2. Naive receive ...

  9. 安全研究公司Gauntlet称轻量级区块链协议Mina的攻击成本很高

    安全研究公司Gauntlet分析称,轻量级区块链协议Mina的攻击成本非常高.Mina使用一种称为OuroborosSamasika的权益证明(PoS)变体,可以在验证epoch之前选择区块生产者.如 ...

最新文章

  1. python语言标号_Python 编码为什么那么蛋疼?
  2. Oracle数据库之过滤和排序
  3. signature=a662b42175c342c2f67535627a2cf0a4,California and Nevada Railroad
  4. kali2020设置root用户登录
  5. const定义常量_go语言基本语法——常量constant
  6. 图片上传工具 java_图片上传工具类-fileUtil
  7. (01)C++之设计模式演变
  8. Android添加gdb symbols
  9. APUE读书笔记-18终端输入输出-09终端标识
  10. 基于RTMP协议的音视频传输----FLV格式
  11. 数据挖掘实验--非负矩阵分解
  12. 修改 jenkins 插件下载地址
  13. MFC + MDI文件拖拽功能
  14. Quartus18.1安装USB Blaster驱动蓝屏
  15. python 源代码 macd双底 高 低_MACD双底,三底背离公式指标
  16. iOS设备录制屏幕视频
  17. MySQL 的分页查询 SQL 语句
  18. 初一知识用计算机进行运算,【初一数学】必考的21个知识点!
  19. 谷歌AI发展史:“量子霸权”将人类推进计算的火箭时代!
  20. ORACLE忘记某个用户密码,但是密码已经过期了怎么办?

热门文章

  1. 货车运输题解 最大生成树+lca
  2. 如何系统的学习java_如何系统学习java
  3. 改进差分进化算法及其求解柔性作业车间调度问题(Python代码实现)
  4. 细菌或原核生物16S rRNA
  5. 进制转换—FY的蓝桥
  6. Golang sync.Map 原理(两个map实现 读写分离、适用读多写少场景)
  7. 很好用的电脑桌面远程控制软件 支持多平台 Win,Mac,Debian… 等操作系统 Anydesk...
  8. 电子琴21键存3首歌c语言,37健电子琴歌曲谱图片_37键电子琴键盘能弹什么歌
  9. C++的基类指针指向派生类对象,vector实现多态性
  10. BOOTCAMP 5 安装win7系统