一、概述
智能合约(Smart Contract)是以太坊中最为重要的一个概念,即以计算机程序的方式来缔结和运行各种合约。最早在上世纪 90 年代,NickSzabo 等人就提出过类似的概念,但一直依赖因为缺乏可靠执行智能合约的环境,而被作为一种理论设计。区块链技术的出现,恰好补充了这一缺陷。
以太坊支持通过图灵完备的高级语言(包括 Solidity、Serpent、Viper)等来开发智能合约。智能合约作为运行在以太坊虚拟机(Ethereum Virual Machine,EVM)中的应用,可以接受来自外部的交易请求和事件,通过触发运行提前编写好的代码逻辑,进一步生成新的交易和事件,并且可以进一步调用其它智能合约。
随着区块链技术的兴起,以及智能合约应用越来越广泛,不过大部分还处于功能实现阶段,安全问题也接二连三地暴露出来。我们在进行开发的同时,也要时刻警惕可能出现的安全问题。感谢Zeppelin,为智能合约出了一套CTF题目 —— Ethernaut。通过做CTF,可以更好地理解漏洞的原理和熟悉漏洞利用方式,对开发者和审计人员都有极大的帮助。
二、Ethernaut
图2.1
Ethernaut是一个基于Web3和Solidity并运行在EVM上的战争游戏,灵感来源于 `overthewire.org`和漫画 `El Eternauta`,以攻克关卡的形式逐步升级。
地址:< https://ethernaut.zeppelin.solutions>
做这套题目,最让人高兴欢喜的莫过于每一关通关时候的completed画面啦。
图2.2
图2.3
三、准备工作
1、交互界面:使用chrome,插件 MetaMask 指向 `Ropsten test network`,然后打开F12,即可在console看到。
图3.1
2、查看账号与余额: `player` 查看自己的账号 `getBalance("")`查看账号余额
3、获取 Ether : 通过 < https://faucet.metamask.io/> 获取 Ether 来玩。
4、Remix-ide : < http://remix.ethereum.org> 以太坊官方编辑器,做题时会使用到。
四、关卡分析
0.Hello Ethernaut
关卡说明:
本关卡帮助你了解游戏的基本操作。
解决方案:
1.Fallback
关卡说明:
仔细观察下面的合约代码。你的目标是:
1. 获取合约所有权
2. 获取所有合约的余额
题目代码:
解决方案:
fallback 能够将 `msg.sender`变成owner,而条件是 `msg.value`和 `contributions[msg.sender]`都大于0。其中 `msg.value`在发起交易的时候amount大于0就行,而 `contributions[msg.sender]`要先调用 `contribute()`函数给合约充点钱。所以解题过程如下:
1、点击题目页面上的 `Get new instance` ,并在MetaMask点submit 部署合约
2、F12 console 中执行 `contract.contribution({value:1})` 先给dd合约打点钱。
图4.1.1
3、contract.address 查看合约地址,然后使用chrome插件MetaMask直接向合约打钱,可以调用fallback函数。这样owner就变成我们了。
图4.1.2
图4.1.3
4 、`contract.withdraw()` 获得合约的所有余额。
5、 点击题目页面上的 `Submit instance` 提交实例,过关。
2.Fallout
关卡说明:
目标是获取合约所有权
题目代码:
解决方案:
合约名称是Fallout,构造函数Fal1out(),对不上,所以Fal1out()变成了一个全局函数,可以被任何人调用。
1、在 console 窗口里使用 `contract.Fal1out()` 即可完成本关。
3.Coin Flip
关卡说明:
这是一个硬币翻转游戏,你需要通过猜测硬币翻转的结果来增加你的连胜纪录。要完成这个关卡,你需要连续10次猜测出正确的结果。
题目代码:
解决方案:
`block.blockhash(block.number - 1)` 表示负一高度的区块哈希,使用这种方式生成随机数,是可以极易被攻击利用的。
图4.3.1
如图,一个交易是被打包在一个区块里的,通过攻击合约去调用Lottery合约,那么他们的区块信息都是一样的。
由此本题的解决方案如下:
1、修改 `CoinFlipExploit.sol`脚本中的地址,并部署。
2、利用 `CoinFlipExploit`合约来发起交易。
3、在F12 console来与合约交互, `contract.consecutiveWins()` 查看已猜对次数。
4、当猜对次数超过10次时,即可提交并完成本关。
图4.3.2
4.Telephone
关卡说明:
目标是获取合约所有权
题目代码:
解决方案:
难点在于利用绕过if语句的判断。 `tx.origin`是系统的交易变量,为交易的原始调用者,可以利用另一个来源的调用从而改变它的值。
定义:tx.origin (address): sender of the transaction (full call chain)。
如果我们直接调用题目合约, `tx.origin` 就与 `msg.sender` 相同。因此我们用另一合约去调用此合约, `tx.origin` 就不会与 `msg.sender` 相同。
1 、修改上面内容,并部署。
2 、执行hack,即可完成本关。
图4.4.1
5.Token
关卡说明:
本关为攻击一个简单的token合约。初始合约时你有20个token,通过本关需要你增加你的token数量(有可能是一个非常大的值)。
题目代码:
解决方案:
因为 `balances` 为 `unit` 类型,无符号整数,不存在负数形式,所以 `balances[msg.sender] - _value >= 0` 永远为真。那么当我们 `_value` 大于 `balances[msg.sender]` 时, `balances[msg.sender]` 就会下溢,变成一个非常大的数。
1 `contract.transfer(0x01, 21)` 这里的0x01为你的账户地址。 `balances[msg.sender]` 将变成 `2**256 – 1`
后续详见下一篇文章 Part 2
热点文章:
网藤宣布正式开源漏洞检测框架Osprey(鱼鹰)
BTW
TCC team长期招聘,包含安全研究、机器学习、数据分析、大数据等职位。感兴趣不妨发简历联系我们。
Email: alex.xu@tophant.com。