• 简体版 | 繁體版
  • 联系我们
  • 加入我们
  • 关于我们
  •  
  • 首页
  • 快讯
  • 价值号
  • 视频
  • 专题
  • 滚动
  • 入驻价值号
  • 碳链APP
    微信公众号

    扫码下载App

  • 登录
  • 微信公众号

    微信公众号

导航
  • 首页
  • 快讯
  • 区块链+
  • 价值号
  • 视频
  • 专题
  • DeFi优选
碳链价值APP
专注服务于金融科技和区块链
立即打开

深入理解「拒绝服务」漏洞

创宇区块链安全实验室创宇区块链安全实验室  •  2021-09-16
拒绝服务攻击愈发猖獗,影响严重,合约开发者应该如何针对漏洞进行修改?如何更好的杜绝这类攻击?

1. 前言

拒绝服务 (DoS):DoS 是 Denial of Service 的简称,即拒绝服务,任何对服务的干涉,使得其可用性降低或者失去可用性均称为拒绝服务。简单的理解就是,用户所需要的正常服务请求无法被系统处理。例如一个计算机系统崩溃或其带宽耗尽或其硬盘被填满,导致其不能提供正常的服务,就构成拒绝服务。

拒绝服务攻击:造成 DoS 的攻击行为被称为 DoS 攻击,其目的是使计算机或网络无法提供正常的服务。

在互联网中,拒绝服务攻击大致可以分为三类:利用软件实现上的缺陷;利用协议上的漏洞;利用资源压制。而在区块链中,拒绝服务攻击扰乱、中止、冻结正常合约的执行,甚至合约本身的逻辑无法正常运行。

2. 漏洞概述

在 Solidity 里,拒绝服务漏洞可以简单的理解为「不可恢复的恶意操作或者可控制的无限资源消耗」,也就是对以太坊合约进行 DoS 攻击,这就可能导致 Ether 和 Gas 的大量消耗,更严重的是让原本的合约代码逻辑无法正常运行。

举个例子,超市有三个收银点,正常来说人们排队在收银点进行扫码支付,但是有一天网络出现了问题,所有收银点的顾客扫码支付都失败了,而后面的人也不能进行支付买单,就导致了收银点的堵塞,超市不能正常运营。又或者,在支付时有顾客故意闹事,使得后面的顾客也不能去支付,这同样也会导致超市不能运营。我们可以看到有来自内部的,还有来自外部的,都是可能会造成拒绝服务攻击。

在智能合约中也是一样的,攻击者通过消耗合约的资源,让用户短暂地退出不可以操作的合约,严重时甚至能永久地退出,从而把以太币锁在被攻击的合约中。

3. 漏洞分析

智能合约中的拒绝服务攻击一般有三种:

  • 在外部操纵映射或数组循环。
  • 所有者操作。
  • 基于外部调用的进展状态。

3.1 在外部操纵映射或者数组循环

这种情况一般是由于映射或者数组循环在外部能被其他人操纵,由于映射或者数组循环的长度没有被限制,从而导致大量消耗 Ether 和 Gas,最后使得智能合约暂时或永久不可操作。在智能合约中通常出现在合约的 owner 和其投资者之间在分配 token 时出现,如下面合约中的 distribute() 函数中。

contract DistributeTokens { address public owner; // 合约所有者 address[] investors; // 投资者数组 uint[] investorTokens; // 每个投资者获得的代币数量 // ... 省略相关功能,包括 transfertoken() function invest() public payable { // 投资investors.push(msg.sender);investorTokens.push(msg.value * 5); // 5 倍 value} function distribute() public { // 分配 require(msg.sender == owner); // 只有合约所有者可以操作 for(uint i = 0; i < investors.length; i++) { // 这里 transferToken(to,amount) 将 "amount" 的代币转移到地址 "to" transferToken(investors[i], investorTokens[i]);}}

在上面的代码片段中我们可以看到,distribute() 函数中会去遍历投资者数组,但是合约的循环遍历数组是可以被外部的人进行人为扩充,如果有攻击者要攻击这个合约,那么他可以创建多个账户加入投资者的数组,让 investors 的数据变得很大,大到让循环遍历数组所需的 gas 数量超过区块 gas 数量的上限,此时 distribute() 函数将无法正常操作,这样就会造成该合约的拒绝服务攻击。

针对以上情况,合约不应该对可以被外部用户人为操纵的映射或循环数组进行批量操作,这里更建议使用取回模式而不是发送模式,即每个投资者可以通过使用 withdrawFunds() 取回自己应得的代币。如果合约必须需要通过遍历一个变长数组来进行转账,那么最好是估计完成它们大概需要多少个区块以及多少笔交易,从而限制数组长度,此外还必须能够追踪得到当前进行到哪以便当操作失败时从那里开始进行恢复。如下面的代码所示,必须确保在下一次执行 payOut() 之前另一些正在执行的交易不会发生任何错误。

struct Payee { address addr; uint256 value;}Payee payees[];uint256 nextPayeeIndex;function payOut() {uint256 i = nextPayeeIndex; while (i < payees.length && msg.gas > 200000) { payees[i].addr.send(payees[i].value); i++; } nextPayeeIndex = i;}

3.2 所有者操作

在代币合约中,通常都有一个 owner 账户,也就是合约所有者账户,其拥有开启/暂停交易的权限,如果 owner 地址丢失,从而使得整个代币合约无法被操作,导致非主观的拒绝服务攻击。

bool public isFinalized = false;address public owner; // 合约所有者function finalize() public { require(msg.sender == owner); isFinalized == true;}// ... 额外的一些 ICO 功能// 重写 transfer 函数,先检查 isFinalizedfunction transfer(address _to, uint _value) returns (bool) { require(isFinalized); super.transfer(_to,_value)}

在上面的合约中代币系统的全部运作都只取决于一个地址,那就是 owner 地址,在 ICO 结束后,如果特权用户丢失,其私钥可能会变为非活动状态,此时,无法调用 finalize() 函数开启交易,那么用户就一直不能发送代币,合约也就不能进行正常操作了。

针对以上情况,合约不应该将整个代币系统都只取决于一个 owner 地址,可以设置多个权限用户地址,也可以设置暂停交易的时间,超过时间或满足某个条件时开启交易,这样整个代币系统就不会被拒绝服务攻击了。下面的代码可以作为参考,来防止所有者操作而造成的拒绝服务攻击。

require(msg.sender == owner || now > unlockTime)

3.3 基于外部调用的进展状态

如果智能合约的状态改变依赖于外部函数执行的结果,但又未对执行一直失败的情况做出防护,此时如果外部调用失败或者由于外部原因而被拒绝时,就可能会造成拒绝服务攻击。比如用户创建一个不接受以太币的合约(非 payable 属性),如果正常的合约需要发送以太币到不接受以太币的合约中才能进入到一个新的状态,那么合约就会被拒绝而达不到新的状态。

pragma solidity ^0.4.22;contract Auction { address public currentLeader; // 当前竞拍者 uint256 public highestBid; // 最高竞拍价 function bid() public payable { require(msg.value > highestBid); // 交易携带的以太币大于当前的 highestBid require(currentLeader.send(highestBid)); // 将当前的 highestBid 退还给当前的竞拍者 currentLeader currentLeader = msg.sender; // 设置新的竞拍者为消息调用者 msg.sender highestBid = msg.value; // 设置新的最高竞拍价 为 msg.value }}

上面的合约就是一个简单的竞拍合约,说下大致的流程,用户执行 bid() 函数时如果携带的以太币大于当前的 highestBid,那么 highestBid 所对应的以太币就会退还给当前的竞拍者 currentLeader,然后设置新的当前竞拍者为调用的用户,highestBid 也设置为用户发起交易时携带的以太币。看着合约代码好像没有什么问题,但是当恶意攻击者部署如下攻击合约时,通过合约来竞拍将会出现问题。

pragma solidity ^0.4.22;interface Auction{ // 设置原合约接口,方便调用函数 function bid() external payable;}contract POC { address owner; Auction auInstance; constructor() public { owner = msg.sender; } modifier onlyOwner() { require(owner==msg.sender); _; } function setInstance(address addr) public onlyOwner { // 指向原合约地址 auInstance = Auction(addr); } function attack() public onlyOwner { auInstance.bid.value(msg.value)(); } function() external payable{ revert(); }}

攻击者先通过攻击合约调用 bid() 函数向竞拍合约转账成为新的竞拍者 currentLeader,然后新的 bid() 函数被执行进行竞标的时候,当执行到 require(currentLeader.send(highestBid)) 退还以太币操作时,会因为攻击合约的 fallback() 回退函数执行 revert() 而无法接收以太币,导致一直为 false,其他竞拍者竞拍都会失败,最后攻击合约以较低的以太币赢得竞拍。

针对以上情况,如果需要对外部函数调用的结果进行处理后才能进入新的状态,那么一定要考虑外部调用可能一直失败的情况,也可以添加基于时间的操作,防止外部函数调用一直失败无法满足 require 判断。

4. 相关案例

4.1 演示案例

接下来会对拒绝服务攻击做出详细的演示讲解,以及会附上一个实例进行说明。

下面的合约代码是根据漏洞分析中第三点基于外部调用的进展状态讲的合约改的,正常的操作逻辑是任何出价高于当前合约 price 的都能成为新的 president,合约中存款也会通过 transfer() 函数转账以太币退还给上一个 president,这么看的话是没有任何问题的,但是以太坊是有两种账户类型,外部账户和合约账户,如果发起 becomePresident() 调用的是外部账户那就是正常的操作,但如果发起 becomePresident() 调用的是合约账户,并且在合约账户的 fallback() 函数中恶意的使用 revert() 等报错的函数,那么其他用户在发起 becomePresident() 时退还以太币给合约账户时会触发 fallback() 函数而导致报错,无法再正常进行 becomePresident() 中的逻辑成为新的 president 了。

那么我们先来看下存在问题的合约代码,这里我们将合约代码设置为 PresidentOfCountry.sol:

pragma solidity ^0.4.19;contract PresidentOfCountry { address public president; // 总统地址 uint256 price; // 出价 function PresidentOfCountry(uint256 _price) { // 构造函数,设置初始的价格 require(_price > 0); price = _price; // 设置初始的价格 } function becomePresident() payable { // 竞争总统 require(msg.value > price); // 支付的以太币必须大于当前总统的竞争费 president.transfer(price); // 退还以太币给上一任总统 president = msg.sender; // 设置新的总统为竞争成功用户 price = price; // 设置最新的竞争价格 }}

在编写攻击合约之前,我们先来介绍下智能合约的两种账户类型以及 fallback 函数。

以太坊中有两种账户类型:

  • 外部账户(externally owned accounts),也就是用户账户,由私钥控制。
  • 合约账户(contract accounts),可执行代码和私有状态,由合约代码控制。

回退函数 (fallback function):回退函数是每个合约中有且仅有一个没有名字的函数,并且该函数无参数,无返回值,如下所示:

function() public payable{...}

回退函数在以下几种情况中被执行:

  • 调用合约时没有匹配到任何一个函数;
  • 没有传数据;
  • 智能合约收到以太币(为了接受以太币,fallback 函数必被标记为 payable)。

下面就来编写攻击合约,主要有两个重点,一个是外部调用 becomePresident,二个就是在回退函数中使用 revert。

pragma solidity ^0.4.19;import "./PresidentOfCountry.sol";contract Attack { function Attack(address _target) payable { // 构造函数,设置目标合约地址,用 call 进行外部调用 becomePresident _target.call.value(msg.value)(bytes4(keccak256("becomePresident()"))); } function () payable { // 回退函数,使用 revert 报错 revert(); }}

在 Remix 中进行调试查看结果,首先使用账户 (0x5B38Da6a701c568545dCfcB03FcB875f56beddC4) 设置初始竞争价格并部署漏洞合约代码 PresidentOfCountry.sol。

部署好后合约的地址为 0xd9145CCE52D386f254917e481eB44e9943F39138,后面在部署攻击合约时需要用到。

点击 president 可以查看当前竞争者的地址。

使用账户 (0x5B38Da6a701c568545dCfcB03FcB875f56beddC4) 调用 becomePresident 并携带 1 eth,执行成功后再点击 president 查看,发现新的总统地址已经变成了 0X5B 的账户。

此时有一个攻击者 (0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2) 编写了一个攻击者合约 Attack.sol,携带 2 eth(因为成为新的总统必须要大于当前总统的竞争价格,当前为 1 eth)并设置 _target 为 PresidentOfCountry 合约地址 (0xd9145CCE52D386f254917e481eB44e9943F39138) 进行部署。

部署好后的攻击合约地址为 0xa131AD247055FD2e2aA8b156A11bdEc81b9eAD95,此时再点击 president 进行查看新总统的地址,发现已经是攻击合约的地址了。

之后如果还有其他用户想来竞争总统位置,就需要大于 2 eth 的价格去调用 becomePresident 函数,这里有个用户 (0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c) 想去竞争总统,携带 3 eth 去调用 becomePresident,结果发现报错并回退,点击 president 发现总统地址还是攻击合约没,此时不管是谁使用多少的以太币去调用 becomePresident,结果都是失败,该合约已经不能进行正常的操作,这就说明合约受到了拒绝服务攻击。

4.2 真实案例

下列代码是实际合约中存在拒绝服务攻击的案例,只写了关键的代码并做了相关的改动。

可以看到合约的关键代码是用作提款操作,但是在提款中有一个判断要提款的金额和用户在该合约中存款的数量是否相等,而并不是大于等于,那么就有可能发生当用户要提出 amount 数量的代币时,由于各种原因(他人转账、奖励分配等使得用户在合约中的存款代币余额发生变化)导致 balances[msg.sender] 变动,甚至是用户不想全部提款,从而使得判断条件 require(balances[msg.sender] == amount); 不成立,这时就会造成短暂的拒绝服务攻击。

...function withdraw(uint256 amount) public { // 提款 amount 数量require(balances[msg.sender] == amount); // 检查要提款的金额是否等于该用户在合约中的存款 balances[msg.sender] -= amount; // 修改合约中存款的状态变量 msg.sender.transfer(amount); // 转账到用户账户}

而修改的办法就是将判断条件 require(balances[msg.sender] == amount); 修改为 require(balances[msg.sender] >= amount); 就可以了。

4.3 历史案例

在历史上,2016 年 2 月 6 日至 8 日,在游戏 KotET(King og the Ether Throne) 的「Turbulent Age」期间,就遭受到了拒绝服务攻击,导致部分角色的补偿和未接收款项无法退回玩家的钱包中。

同年 6 月,GovernMental 合约也遭受到了拒绝服务攻击,当时 1100 个以太币通过使用 250 万个 gas 交易而获得,这笔交易超出了合约能负荷的 gas 上限,从而导致交易暂停。

相关的还有 Fomo 3D 等的拒绝服务攻击。

5. 解决办法

通过上面的讲解,我们可以发现拒绝服务攻击在智能合约中的影响也是非常严重的,所以针对拒绝服务攻击,合约开发者应该针对上面漏洞分析时讲到的三种情况进行相应的代码修改。

比如对于外部操作的映射或者数组循环,需要对长度进行限制等;而对于所有者操作需要考虑合约的非唯一性,不要使得合约因为某个权限账户而导致整个业务瘫痪;基于外部调用的进展状态需要对函数的调用进行异常处理,一般来说内部函数的调用不会造成危害。

如果调用失败也只是会进行回退,而外部调用具有不确定性,我们不知道外部调用者想干什么,如果被攻击者攻击,就可能会造成严重的后果,具体表现为恶意返回执行错误,造成正常代码无法执行,从而造成拒绝服务攻击,那么针对这种开发者就应该加入函数执行异常的处理机制。

总的来说,合约开发者需要考虑合约代码的代码逻辑全面性和缜密性等,这样才能更好的杜绝拒绝服务攻击。

6. 参考文献

  • 拒绝服务攻击(黑客的攻击手段之一)_百度百科 (baidu.com)
  • 以太坊智能合约安全入门了解一下(下) (rickgray.me)
  • 《智能合约安全分析和审计指南》
展开全文
打开碳链价值APP  查看更多精彩资讯
声明:本文内容为作者独立观点,不代表碳链价值立场,且不构成任何投资理财建议。
0 0
攻击链圈子 海盗号

扫一扫,分享到微信

相关推荐

邹传伟:如何理解元宇宙的经济学 滚动

邹传伟:如何理解元宇宙的经济学

万向区块链 2021-11-04 滚动
元宇宙海盗号
经济系统是元宇宙的核心要素之一
微软CEO:元宇宙将把现实世界带入任何数字空间 深度

微软CEO:元宇宙将把现实世界带入任何数字空间

白泽研究院 2021-11-03 深度
微软元宇宙海盗号
继Facebook更名Meta高调进军元宇宙后,微软将通过两项举措发展元宇宙。
一文读懂芝商所的微型以太坊期货MET 滚动

一文读懂芝商所的微型以太坊期货MET

星球日报 2021-11-03 滚动
以太坊海盗号
芝商所推出“微型”加密货币衍生品产品取得了不错的成绩

碳链快讯更多 ›

2023-02-07

OKX宣布将向土耳其地震灾区捐款100万里拉

2023-02-07

SPACE ID 完成 1000 万美元战略轮融资,Polychain Capital 和 dao5 领投

2023-02-07

国际清算银行正开发稳定币监控系统,以确保发行方保持足够储备资产

2023-02-07

ENS DAO 社区已通过“出售 1 万枚 ETH 来支付运营成本”的链上提案

2023-02-07

OpenAI跻身全球TOP50网站,1月访问量突破6.72亿

2023-02-07

北京市监局提醒警惕元宇宙、NFT 炒作

2023-02-07

360计划推出类ChatGPT产品

2023-02-07

蓝色光标:公司正在积极试用ChatGPT技术

2023-02-07

加密矿企Layer1 CEO指控两名董事会成员利用多数股权攫取公司收益

2023-02-07

软银愿景基金第三财季亏损约 49.92 亿美元,投资亏损约 55.26 亿美元

2023-02-07

韩国国民议会将加快制定《数字资产基本法》,最早本月举行公开听证会

2023-02-07

联合国报告:朝鲜黑客2022年盗取的加密货币为历年最多

2023-02-07

DCG 已半价出售约四分之一的灰度以太坊信托基金股份,筹款达 2200 万美元

2023-02-07

Aave创始人:V3升级完全是为了管理风险

2023-02-07

欧易Web3钱包与Avalanche生态借贷协议Benqi达成官方合作

2023-02-07

a16z:已将约 4000 万枚 UNI 投票权无条件委托给外部团体

2023-02-07

加密储蓄公司 Donut:Genesis 的重组计划债权回收率或达 80% 以上

2023-02-07

香港监管机构正扩招负责虚拟资产的职位

2023-02-07

Yuga Labs 在 Bored Ape NFT 商标诉讼中达成和解

2023-02-07

区块链数据分析公司 Elementus 获 1000 万美元 A-2 轮融资,ParaFi Capital 领投

2023-02-07

SBF 获电子通信保释,将被允许使用 FaceTime、Zoom、电子邮件等消息应用程序

2023-02-07

英国央行和财政部邀请公众参与制定数字英镑计划

2023-02-07

Genesis 已就重组计划和母公司 DCG 、债权人 Gemini 达成初步协议

2023-02-07

FTX 已敦促南希·佩洛西等政客在 2 月 28 日前归还此前的政治捐款

2023-02-06

OpenSea 推出内测计划,现已开放申请

2023-02-06

彭博社:区块链金融公司 Figure 放弃 SPAC 上市,并寻求 1 亿美元融资

2023-02-06

Louis Vuitton将与草间弥生发行1万枚联名款NFT并上架OpenSea

2023-02-06

欧易Web3钱包与去中心化交易所BabyDogeSwap达成官方合作

2023-02-06

ARK Invest:未来十年加密货币和智能合约可分别创造 20 万亿和 5 万亿美元的市场价值

2023-02-06

区块链初创公司 Super Block 完成约 722 万美元 A 轮融资,SK、Netmarble 等参投

2023-02-06

徐明星:OKX 暂无推出稳定币的计划

2023-02-06

加密 KOL:Debt DAO 发行的 FTX 债券代币 FUD 可能违反证券法

2023-02-06

普华永道:全球共有 11 个支持加密货币合法的国家/地区

2023-02-06

Visa 正在测试以太坊上的 USDC 大额支付结算系统

2023-02-05

a16z 投票反对在 BNB Chain 上部署 Uniswap V3,希望部署采用 LayerZero 桥接

2023-02-05

集成ChatGPT-4的微软Bing于今日短暂上线

2023-02-05

多名投资人退出前 Moonbirds 首席运营官 Ryan Carson 创立的新项目「Flux」

2023-02-05

数据:1 月份 NFT 交易量达 9.46 亿美元,创 2022 年 6 月以来最高记录

2023-02-05

Yuga Labs 联创:Dookey Dash 得分审核与实时作弊检测分开,玩家得分在审核后或被减为零

2023-02-04

Pioneer宣布获香证监会虚拟资产管理牌照,可管理100%虚拟资产

2023-02-04

去中心化数据系统 Inery 完成新一轮融资,Truth Ventures 参投

2023-02-04

美国司法部机构 U.S. Trustee 代表任命成立 Genesis 破产案中代表无担保债权人的七人委员会

2023-02-04

Arthur Hayes:Zhu Su 新 CEX 项目“GTX”已完成融资

2023-02-04

Microstrategy 创始人:查理·芒格若处于第三世界,会比我更加看好比特币

2023-02-03

慢雾:Orion Protocol 被黑分析

2023-02-03

韩国金融服务委员会定于2月6日公布「证券型代币发行」相关制度指南

2023-02-03

MicroStrategy首席财务官:公司考虑利用比特币波动,进行更多加密货币交易

2023-02-03

以太坊核心开发者:计划2月7日在Zhejiang测试网上激活上海升级

2023-02-03

欧易 Web3 钱包上线币种行情功能

2023-02-03

Damus:正通过比特币闪电网络随机向用户发放小额比特币

推荐文章

  • 深度研究:如何设计代币经济学框架?

    2022-05-31

  • 万事达卡CEO:SWIFT系统将在五年内被央行数字货币取代

    2022-05-31

  • 达沃斯Crypto手记:加密幽灵,在欧洲游荡

    2022-05-30

  • 以太坊合并:如何影响显卡和区块链行业?

    2022-05-29

  • V神:「灵魂绑定」币将成为你们的区块链护照

    2022-05-28

价值号更多 ›

吉时通信
吉时通信
文章: 133
  • 以太坊合并:如何影响显卡和区块链行业?
  • 以太坊合并的底层观察:区块结构和MEV
  • 宏观视角解析LUNA和UST崩盘:稳定币的路在何方?
链集市ChainMarket
链集市ChainMarket
文章: 177
  • 区块链产业周刊丨全球区块链商业委员会与全球数字金融合并;最高人民法院发布区块链司法应用相关意见;蚂蚁链区块链应用专利获国家授权
  • 区块链将如何帮助我们解决气候问题?
  • 区块链产业周刊丨国家级数据云平台“人民云”正式上线;徐工机械成立包含区块链技术的国重实验室;欧盟推进数字欧元发展阶段
Unitimes
Unitimes
文章: 380
  • 深度研究:如何设计代币经济学框架?
  • 创作者经济:正统性危机之下的变革之路
  • 哈佛商业评论:Web3的机遇和挑战
换一批

热门标签

新基建 比特币 以太坊 矿业 DeFi 共识对话 区块链+ 研报 美联储 央行数字货币 无限QE 加密衍生品 AI 云计算 大数据 5G 政策 交易所 稳定币 电子支付 Libra 算力产业 联盟链 公链 区块链 加密货币 Nervos Cosmos EOS STO

邮件订阅

及时、全面、专业、准确的资讯与数据,致力于为区块链爱好者以及数字货币投资者提供最好的服务。

App内打开

邮件订阅

及时、全面、专业、准确的资讯与数据,致力于为区块链爱好者以及数字货币投资者提供最好的服务。

Moshou

碳链价值是集资讯、行情、数据于一身的区块链信息服务平台,我们追求及时、全面、专业、精确的资讯与数据,致力于为区块链创新者和数字货币投资者提供优质的服务。

关于我们 加入我们 联系我们 隐私条款
微信公众号

扫一扫关注微信公众号

Copyright © 2018-2020 碳链价值 京ICP备18046423号
下载碳链App

下载碳链App

微信公众号

微信公众号

微信公众号

微信公众号

打赏文章作者

支付宝打赏二维码 支付宝扫一扫打赏
微信打赏二维码 微信扫一扫打赏

# 热门搜索 #

CBDC 比特币 DeFi 以太坊 区块链