0x00 Info 在智能合约中,当 超过gas限制,异常抛出,非预期的终止合约,访问控制被破坏 均会产生合约拒绝服务问题。 本篇就介绍DASP第五大漏洞–Denial of Service
0x01 Real World Impact GovernMental 起因:GovernMental’s 1100 ETH jackpot payout is stuck because it uses too much gas
官网:GovernMental
源码:etherscan code
GovernMental本身是个嘲讽政府庞氏骗局的游戏合约,其规则大概为投资者捐最少1ETH给政府,如果政府在最近12小时内没有收到任何捐赠,则将奖金发给最后一名投资人,而如果收到奖励,则按一定比例分别将钱分给资金池、政府及投资人。
其中在大于12小时未收到赞助的逻辑分支中,将奖金分给最后一名投资人后会重置所有投资人:
if (lastTimeOfNewCredit + TWELVE_HOURS < block.timestamp) { // Return money to sender msg.sender.send(amount); // Sends all contract money to the last creditor creditorAddresses[creditorAddresses.length - 1].send(profitFromCrash); corruptElite.send(this.balance); // Reset contract state lastCreditorPayedOut = 0; lastTimeOfNewCredit = block.timestamp; profitFromCrash = 0; creditorAddresses = new address[](0); // out of gas!
0x00 Info Unchecked Return Values For Low Level Calls漏洞简单理解就是没有检查一些不安全调用函数的返回值导致。
0x01 CALL -> Contract 在reentrancy漏洞介绍中提到几种账户间转币函数,这里我们回顾并深入了解下:
函数 特性 address.transfer() 1. 失败抛出异常且回滚 2. 提供2300gas,防止reentrancy address.send() 1. 失败返回false 2. 提供2300gass,防止reentrancy address.call.value().gas()() 1. 失败返回false 2. 发送所有可用gas 在这些交易函数中需要注意的是:
addr.transfer()和addr.send()能够防止重入漏洞。但是这些方法会触发fallback函数执行,被调合约仅被提供2300gas做一些日志事件。 x.transfer(y)等同于require(x.send(y)),transfer在发送失败时会自动revert(内置失败处理)。 addr.call.value(y)()也会触发代码执行,但是会用所有提供的gas执行代码,当然这种方式不能防止reentrancy漏洞。 通过了解以上我们会提出一些疑问:
2300gas由谁来提供?为何是2300gas? 2300gas能做什么? 哪些行为会导致发送失败? 要回答这些问题,首先要知道一个知识点,transfer、send、call在EVM虚拟机执行时会将这些solidity编译成 CALL 指令,而在以太坊wiki中定义了CALL的gas消耗:
Quote CALL has a multi-part gas cost: 700 base 9000 additional if the value is nonzero 25000 additional if the destination account does not yet exist (note: there is a difference between zero-balance and nonexistent!
0x00 Info Arithmetic Issues为DASP TOP10的第三类漏洞。这类算数漏洞比较常见
also known as integer overflow and integer underflow
0x01 原理 整型溢出的原理很简单,以8位整型为例,借图说明上溢。
8位无符号整型范围[0, 255]:
8位有符号整型范围[-128, 127]:
对于下溢 (unit8)0-1=(uint8)255, (int8)(-128)-1=(int8)127.
0x02 场景 对于智能合约常见的整型溢出漏洞,会出现在如下场景:
经典场景,没有检查下溢 function withdraw(uint _amount) { require(balances[msg.sender] - _amount > 0); // 如果 _amount > msg.sender, underflow msg.sender.transfer(_amount); // 会transfer一个很大的值 balances[msg.sender] -= _amount; } off-by-one function popArrayOfThings() { require(arrayOfThings.length >= 0); arrayOfThings.length--; // length是uint,当length=0,length--会下溢翻转;同样++也需要注意 } 关键字var var会根据分配值更改为最小适配类型,下例中i=0,因此i被分配为uint8。因此i最大255,当somethingLarge>256时,i就会上溢。 for (var i = 0; i < somethingLarge; i ++) { // .
0x00 Info 本篇为DASP TOP10的第二类漏洞Access Control。通过Parity及Rubixi合约分析理解此类型漏洞,改写OpenZeppelin中题目辅助理解。
0x01 Access Control 概念:攻击者通过合约不安全的可见性设置时可以直接访问合约的私有变量和函数,这其中需要可能需要绕过一些访问控制。
在使用 Solidity 编写合约代码时,有几种默认的变量或函数访问域关键字:private, public, external 和 internal,对合约实例方法来讲,默认可见状态为 public,而合约实例变量的默认可见状态为 private。 具体讲解参见solidity develop visibility 这里需要重点关注下external和internal,external函数为合约接口,只能被其他合约调用(在自身需通过this.f()调用)。而internal函数只能被自身调用。
此类漏洞经常会发生在以下场景:
合约使用已经遗弃的tx.origin验证调用者 通过很长的require处理大量的认证逻辑 通过delegatecall调用代理库或者代理合约 通俗讲就是一般的智能合约会通过初始化指定合约的拥有者,来实现类似赋予特权后收回合约资金的功能。而初始化函数如果能被任何人调用的话,攻击者就会将自己成为合约拥有者进行提币或恶意操作。
这里最著名的漏洞就是Parity Wallet Hack。但在介绍该漏洞前需要理解一些solidity知识。
0x02 tx.origin Solidity: Tx Origin Attacks
在solidity官方文档中已经声明禁止使用tx.origin做认证。
Quote If your wallet had checked msg.sender for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking tx.origin, it gets the original address that kicked off the transaction, which is still the owner address.
0x00 Info 从本篇开始学习智能合约漏洞,依据DASP TOP10。
0x01 DAO History:
The History of the DAO WhitePaper:
WhitePaper 白皮书 OpenSource:
Github etherscan 概念理解 众筹合约,通过资金ETH换取DAO token,从而获得投票和发起议案的权利,按一定规则回馈给投资人投资项目的收益。 因此,The DAO特点:
本质是个VC,通过以太坊筹集的资金(ETH)锁定在智能合约中,通过代码主导! 出资ETH获取对应DAO代币,具有审查项目、投票表决和提出投资项目议案的权利 投资议案由全体代币持有人投票表决,一币一票,票数通过,投资项目可获得相应投资额。利用“众智”+出资额权重决定投资策略(取代传统行业投资经理)。 投资项目的收益按规则回馈股东。 The DAO is attacked the-dao-the-hack-the-soft-fork-and-the-hard-fork
security-alert-dos-vulnerability-in-the-soft-fork
0x02 Reentrancy solidity基础知识 合约调用
message call bytes4 funcIdentifier = bytes4(keccak256("FuncName(paramType)")); this.call(funcIdentifier, paramValue); contract object Contract1 c = Contract1(AddressOfContract1); c.foo(); 限制
例如send花费2300gas 递归调用栈最大1024层 转币
正所谓不积跬步无以至千里,作为区块链安全学习的第一步,本篇汇聚区块链理论与实践的各种资源,通过知行来理解这个新领域,为安全研究做好伏笔。
BlockChain 区块链技术指南 中文资料阅读站 算法演进 挖矿演进 共识机制演进 自写demo 矿池 PHP-MPOS node-open-mining-portal Powerpool BitCoin 简介:how-bitcoin-works-under-hood whitepaper:bitcoin 中文注解:比特币白皮书 个人翻译+注解 Ethereum 原理 whitepaper: White-Paper 中文:以太坊白皮书 协议架构 https://github.com/ethereum/wiki/wiki/R&D https://ethresear.ch 智能合约 Dapp state wiki:ÐApp Development 入门指南: The Hitchhiker’s Guide to Smart Contracts in Ethereum 以太坊开发入门,完整入门篇 Getting Started with Ethereum and Solidity learning-solidity-part-1-deploy-a-contract 开发事例: Create a Hello World Contract in ethereum Create a Token Contract Create a Crowdsale Contract How to create a private Ethereum network Comments Feed 客户端开发环境 Ethereum Clients IDE: remix truffle 模拟环境:testrpc 客户端: 全节点 geth 轻节点 parity cli:https://ethereum.