0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

动不动就出事,智能合约攻击该怎么办

电子设计 来源:电子设计 作者:电子设计 2020-12-25 19:00 次阅读

如果你在数字货币世界待过足够时间,也许你听说过1或2个智能合约攻击时间,这些攻击导致了几千万美元的盗窃损失。最著名的攻击是DAO事件,这是数字货币世界最受期待的项目之一,同时也是智能合约的改革。虽然很多人听说过这些攻击,但是很少人知道到底发生了什么,是怎么发生的,以及如何避免这些错误。

智能合约是动态的,复杂的以及难以置信地强大。虽然他们的潜力是很难想象,但是也不可能一夜之间就成为了攻击的对象。也就是说,对于往后的数字货币,我们可以从之前的错误中学到经验,然后一起成长。虽然DAO是已经发生的事情,但是这对于开发者,投资者,以及社区成员对于智能合约攻击来说,都是一个很好的例子。

今天,我想和大家聊聊从DAO事件中,我们学到的3件事。

攻击#1:重入攻击

当攻击者通过对目标调用提款操作的时候,重入攻击就会发生,就好像DAO事件一样。当合约不能在发出资金之前更新状态(用户余额),攻击者就可以连续进行提取函数调用,来获得合约中的资金。任何时候攻击者获得以太币,他的合约都会自动地调用反馈函数,function (),这就再次调用了提现合约。这时候,攻击就会进入递归回路,这时候这个合约中的资金就会转入攻击者。因为目标合约都在不停地调用攻击者的函数,这个合约也不会更新攻击者的余额。当前的合约不会发现有任何问题,更清楚地说,合约函数中包含反馈函数,当合约收到以太币和零数据的时候,合约函数就会自动执行。

攻击流程

1.攻击者将以太币存入目标函数

2.目标函数就会根据存入的以太币而更新攻击者的约

3.攻击者请求拿回资金

4.资金就会退回

5.攻击者的反馈函数生效,然后调用提现功能

6.智能合约的逻辑就会更新攻击者的余额,因为提现又被成功调用

7.资金发送到攻击者

8.第5-7步重复使用

9.一旦攻击结束,攻击者就会把资金从他们自己的合约发送到个人地址

1*UeDgMZo2n0skHzgkl352zQ

重入攻击的递归回路

很不幸地是,一旦这个攻击开始,无法停下。攻击者的提现功能会被一次次地调用,直到合约中的燃料跑完,或者被害者的以太币余额被消耗光。

代码

下面就是DAO合约的简单版本,其中会包括一些介绍来为这些不熟悉代码/ solidity语言更好地理解合约。

contract babyDAO {

/* assign key/value pair so we can look up

credit integers with an ETH address */

mapping (address => uint256) public credit;

/* a function for funds to be added to the contract,

sender will be credited amount sent */

function donate(address to) payable {

credit[msg.sender] += msg.value;

/*show ether credited to address*/

function assignedCredit(address) returns (uint) {

return credit[msg.sender];

/*withdrawal ether from contract*/

function withdraw(uint amount) {

if (credit[msg.sender] >= amount) {

msg.sender.call.value(amount)();

credit[msg.sender] -= amount;

如果我们看下函数withdraw(),我们可以看到DAO合约使用address.call.value()来发送资金到msg.sender。不仅如此,在资金发出后,合约会更新credit[msg.sender]的状态。攻击者在发现了合约代码中的问题,就能够使用类似下面的ThisIsAHodlUp {}来将资金转入contract babyDAO{}合约。

import ‘browser/babyDAO.sol’;

contract ThisIsAHodlUp {

/* assign babyDAO contract as "dao" */

babyDAO public dao = babyDAO(0x2ae...);

address owner;

/*assign contract creator as owner*/

constructor(ThisIsAHodlUp) public {

owner = msg.sender;

/*fallback function, withdraws funds from babyDAO*/

function() public {

dao.withdraw(dao.assignedCredit(this));

/*send drained funds to attacker’s address*/

function drainFunds() payable public{

owner.transfer(address(this).balance);

需要注意地是,这个后退函数,function(),会调用DAO或者babyDAO{}的提现函数,来从合约中盗取资金。从另个方面来说,当攻击者想要把所有偷窃来的资金赚到他们的地址,drainFunds()功能会被调用。

解决方案

现在,我们应该清楚重放攻击会利用两个特别的智能合约漏洞。第一个是当合约的状态在资金发出之后,而不是之前进行更新。由于在发出资金前无法更新合约状态,函数就会在中间计算的时候被打断,合约也认为资金其实还没有发出。第二个漏洞就当合约错误地使用address.call.value()来发出资金,而不是address.transfer() 或者 address.send()。这两个都受限于2300gas,只记录一个事件而不是多个外部调用。

contract babyDAO{

....

function withdraw(uint amount) {

if (credit[msg.sender] >= amount) {

credit[msg.sender] -= amount; /* updates balance first */

msg.sender.send(amount)(); /* send funds properly */

攻击2:下溢攻击

虽然DAO合约不会让受害者掉入下溢攻击,我们能够通过现有的babyDAO contract{}来更好地理解这些攻击为什么会发生。

首先,我们需要理解什么是256单位制。一个256单位制是由256个字节组成。以太坊的虚拟机是使用256字节来完成的。因为以太坊虚拟机受限于256字节的大小,所以数字的范围是0到4,294,967,295 (22??)。如果我们超过这个范围,那么数字就会重置到范围的最底部(22?? + 1 = 0)。如果我们低于这个范围,这个数字就会重置到这个范围的顶端(0–1= 22??)。

当我们从零中减去大于零的数,就会发生下溢攻击,导致一个新的22??数集。现在,如果攻击者的余额发生了下溢,那么这部分余额就会更新,从而导致整个资金被盗。

攻击流程

攻击者通过发出1Wei到目标合约,来启动攻击。

合约认证发出资金的人

随后调用1Wei的提现函数

合约会从发送者的账户扣除的1Wei,现在账户余额又是零

因为目标合约将以太币发给攻击者,攻击者的退回函数被处罚,所以提现函数又被调用。

提现1Wei的事件被记录

攻击者合约的余额就会更新两次,第一次是到零,第二次是到-1。

攻击者的余额回置到22??

攻击者通过提现目标合约的所有资金,从而完成整个攻击

代码

/*donate 1 wei, withdraw 1 wei*/

function attack() {

dao.donate.value(1)(this);

dao.withdraw(1);

/*fallback function, results in 0–1 = 2**256 */

function() {

if (performAttack) {

performAttack = false;

dao.withdraw(1);

/*extract balance from smart contract*/

function getJackpot() {

dao.withdraw(dao.balance);

owner.send(this.balance);

解决方案

为了防止受害人陷入下溢攻击,最好的方法是看更新的状态是否在字节范围内。我们可以添加参数来检查我们的代码,作为最后一层保护。函数withdraw()的首行代码是为了检查是否有足够的资金,第二行是为了检查超溢,第三个是检查下溢。

contract babysDAO{

....

/*withdrawal ether from contract*/

function withdraw(uint amount) {

if (credit[msg.sender] >= amount

&& credit[msg.sender] + amount >= credit[msg.sender]

&& credit[msg.sender] - amount <= credit[msg.sender]) {

credit[msg.sender] -= amount;

msg.sender.send(amount)();

需要注意,就像我们之前讨论,我们上面的代码是在发出资金之前更新用户的余额。

审核编辑:符乾江
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4556

    浏览量

    66814
  • 智能计算
    +关注

    关注

    0

    文章

    147

    浏览量

    16403
收藏 人收藏

    评论

    相关推荐

    UCB states切为CONFIRMATION状态后,UCB区域就不能被擦写了怎么办

    芯片手册中UCB states有四个状态UNLOCKED, CONFIRMATION, ERASED, ERRORED。我发现切为CONFIRMATION状态后,UCB区域就不能被擦写了,这怎么办
    发表于 02-01 08:15

    keil编译没有axf文件怎么办啊?

    keil编译没有axf文件怎么办
    发表于 01-15 07:28

    TPYBoard里的sht20.py的文件找不到了怎么办

    TPYBoard里的sht20.py的文件找不到了,怎么办
    发表于 11-08 06:06

    esp32指示灯不亮,PC也无法检测到怎么办呢?

    esp32这个板子 指示灯不亮,PC也无法检测到怎么办呢?
    发表于 11-02 06:40

    树莓派烧录后死机怎么办

    树莓派烧录后死机怎么办
    发表于 10-24 08:06

    51单片机不能识别红外循迹模块发出的的高电平怎么办

    51单片机不能识别红外循迹模块发出的的高电平怎么办
    发表于 10-23 07:02

    51单片机不能识别红外循迹模块发出的的高电平怎么办

    51单片机不能识别红外循迹模块发出的的高电平怎么办
    发表于 10-18 06:09

    STM8在休眠模式下独立看门狗还老是复位怎么办

    STM8在休眠模式下独立看门狗还老是复位怎么办
    发表于 10-15 06:42

    RT-Thread Studio没有支持的芯片怎么办

    RT-Thread Studio没有支持的芯片怎么办
    发表于 09-20 06:32

    模型转换失败怎么办

    模型转换失败怎么办
    发表于 09-18 08:41

    GPU发起的Rowhammer攻击常见问题

    任意虚拟内存位置的指针。 方法使用来自远程JavaScript的侧通道和所谓的“Rowhammer”攻击。 所谓的“Rowhammer”攻击已经存在了几年,但最近的研究表明,这种攻击
    发表于 08-25 06:41

    求助,ChipWatcher找不到信号怎么办

    ChipWatcher找不到信号怎么办
    发表于 08-11 10:01

    keil编译没有axf文件怎么办啊?

    keil编译没有axf文件怎么办
    发表于 06-27 08:35

    ​明源云 &GaussDB:盘活数据资产,驱动不动产数字化创新

    过去很长一段时间,不动产行业的数字化程度都是比较低的,特别在业务层面,存在大量碎片化和多主体的问题,导致在数据层面的标准化和数据结构统一化不足;而且在不动产行业全生命周期中,每个阶段都频繁涉及到
    的头像 发表于 06-05 16:39 275次阅读

    STM32F4 ADC采样速率远大于输入信号频率怎么办呢?

    STM32F4 ADC采样速率远大于输入信号频率怎么办呢?
    发表于 05-09 14:26