/images/avatar.png

#DASP# Short Addresses (9)

0x00 Info 短地址攻击是DASP TOP10中详细描述的最后一类漏洞。这个漏洞其实可以归结于过去EVM的缺陷。 0x01 基础知识 Quote The Contract Application Binary Interface (ABI) is the standard way to interact with contracts in the Ethereum ecosystem, both from outside the blockchain and for contract-to-contract interaction. 在EVM虚拟机中合约是通过ABI接口进行交互,数据和方法都根据特定标准进行编解码。具体参见Contract ABI Specification。 如何通过ABI调用合约方法?方法和数据如何编码?我们举例(经典Token合约)来说明: contract MyToken { mapping (address => uint) balances; event Transfer(address indexed _from, address indexed _to, uint256 _value); function MyToken() { balances[tx.origin] = 10000; } function sendCoin(address to, uint amount) returns(bool sufficient) { if (balances[msg.

#DASP# Time Manipulation (8)

0x00 Info 如果攻击者拥有矿工角色,在交易打包成块时,矿工在一定范围是可以操作块的时间戳的,而恰恰合约利用时间戳生成逻辑(特别是涉及到资金交易)就会存在Time manipulation风险。 0x01 Rules 一个合约需要用到“当前时间”,通常是通过block.timestamp或者now来实现,而这个值来源于矿工!矿工是可以在几秒内调整这个时间戳的,从而为自己的利益改变合同的输出。 例如一个合约使用时间戳来生成随机数(在example中有实例),矿工可以在区块被验证后30s内发布时间戳,从而利用这30s的时间增加自己获利概率。 30-second Rule 矿工可以改变区块的时间戳 仅限于被验证后的30s 当前块的时间戳不能小于前一块的时间戳 但是一个合约具备以下特性,是可以安全使用时间戳的: Quote If the smart contract function can tolerate a 30-second time period, it’s safe to use timestamp; If the scale of a time-dependent event can vary by 30 seconds and maintain integrity, it’s also safe to use a block timestamp. 0x02 Examples 前两个实例合约主要利用block.timestamp来生成随机数,而在#DASP# Bad Randomness一文中我们详细介绍了这种随机数生成的缺陷。 theRun Source Code

#DASP# Front Running (7)

0x00 Info 本篇介绍DASP第七种漏洞类型–前置执行。 可以理解为因为区块链是公开,其他用户或者合约的交易信息均透明可查,恶意用户可以利用这些已知信息制造有利于自己的交易,并通过提高手续费的方式抢先执行获利。 当然,在front-running-griefing-and-the-perils-of-virtual-settlement中描述,如果这个恶意用户本身又是矿工,可以任意安排交易并审查其他人交易,使自己利益最大化。 0x01 实例 LastIsMe Contract Source Code LastIsMe是一个lottery游戏合约,在一个游戏回合(一定的区块数内)参与者购买一张票认领最后一个座位,回合结束时最后一个就坐的玩家会获得头奖。 攻击者可以在回合快结束时观察其他玩家的交易池,通过提高gas来挤掉其他玩家的交易从而获得奖金。 ICO Contract Source Code ICO本身是一道CTF题,它包含一个DAPP网站和与其交互的两个合约(HaCoin & ICO),部署在rinkeby测试链上。 HaCoin是一个Token合约,比赛的目标也是从该合约中获取超过31337枚HackCoin,而这个合约本身并没有问题,需要配合Web网站存在的XSS漏洞来提权,才能进行转币操作。很有意思,具体writeup参见ZeroNights ICO Hacking Contest Writeup。 而另一个合约ICO是一个lottery合约,游戏规则也很简单,5个块为一回合,回合内参与者猜一个数字,回合结束时机器人会随机抛出一个数字,如果谁猜中谁就获胜。而该合约存在前置执行漏洞。 function spinLottery(uint number) public { if (msg.sender != robotAddress) { playerNumber[msg.sender] = number; players.push(msg.sender); NewLotteryBet(msg.sender); } else { require(block.number - lotteryBlock > 5); lotteryBlock = block.number; for (uint i = 0; i < players.length; i++) { if (playerNumber[players[i]] == number) { desires[players[i]].active = true; desires[players[i]].

#DASP# Bad Randomness (6)

0x00 Info 绝对的随机在Ethereum中很难实现,因为所有参数都可以在透明的链上查询,因此想要利用随机特性生成逻辑很容易出现bug,本篇就介绍DASP中第六类漏洞–不安全的随机性。 0x01 错误实践 Solidity本身提供一些获取“随机值”的方法和变量,你可以在官方文档中查看。但是这些“随机值”(在链上公开)如果不当的作为生成随机数的因子或种子,那随机数就可以被预测。 根据DASP提供的实例我们看看有哪些坑。 第一种 uint256 private seed; function play() public payable { require(msg.value >= 1 ether); iteration++; uint randomNumber = uint(keccak256(seed + iteration)); if (randomNumber % 2 == 0) { msg.sender.transfer(this.balance); } } 利用私有变量seed和iteration通过keccak256 hash计算得到随机数,虽然seed属性为private,但是它也要在某个时间点设置,可以通过链上相关tx来获取其值,因此随机性可预测。 第二种 function play() public payable { require(msg.value >= 1 ether); if (block.blockhash(blockNumber) % 2 == 0) { msg.sender.transfer(this.balance); } } 这里使用block.blockhash(blockNumber)来计算随机数,这里必须强调的是,在solidity中,block.blockhash(uint blockNumber) returns (bytes32)只计算就近的256个块hash,如果blockNumber为当前块(block.number)或者超过256更久远的块,计算结果都是0. Quote block.blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent blocks excluding current

#DASP# Denial of Services (5)

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!

#DASP# Unchecked Return Valuse (4)

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!