Ethernaut的writeup
作者:互联网
Ethernaut记录
Hello Ethernaut
出题目的
初级题目,用于测试通过控制台与合约交互
合约代码
pragma solidity ^0.4.18;
contract Instance {
string public password;
uint8 public infoNum = 42;
string public theMethodName = 'The method name is method7123949.';
bool private cleared = false;
// constructor
function Instance(string _password) public {
password = _password;
}
function info() public pure returns (string) {
return 'You will find what you need in info1().';
}
function info1() public pure returns (string) {
return 'Try info2(), but with "hello" as a parameter.';
}
function info2(string param) public pure returns (string) {
if(keccak256(param) == keccak256('hello')) {
return 'The property infoNum holds the number of the next info method to call.';
}
return 'Wrong parameter.';
}
function info42() public pure returns (string) {
return 'theMethodName is the name of the next method.';
}
function method7123949() public pure returns (string) {
return 'If you know the password, submit it to authenticate().';
}
function authenticate(string passkey) public {
if(keccak256(passkey) == keccak256(password)) {
cleared = true;
}
}
function getCleared() public view returns (bool) {
return cleared;
}
}
思路解析
用于测试metamask和控制台交互,按提示做就可以。F12打开控制台,在控制台依次输入以下js代码
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()
42
await contract.info42()
"theMethodName is the name of the next method."
await contract.theMethodName()
"The method name is method7123949."
await contract.method7123949()
"If you know the password, submit it to authenticate()."
await contract.password()
"ethernaut0"
await contract.authenticate("ethernaut0")
完成后点击提交实例即可。
知识点回顾
metamask是一个嵌入到浏览器的钱包,一旦浏览的网站支持web3,就会弹出metamask询问你是否连接到该网站。在有交易时,也会主动弹出来询问是否确认放行该交易。
Fallback
出题目的
成为合约的owner并将余额减少为0;题目目旨在考察智能合约的回调函数
合约代码
pragma solidity ^0.4.18;
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
//合约Fallback继承自Ownable
contract Fallback is Ownable {
using SafeMath for uint256;
mapping(address => uint) public contributions;
//通过构造函数初始化贡献者的值为1000ETH
function Fallback() public {
contributions[msg.sender] = 1000 * (1 ether);
}
// 将合约所属者移交给贡献最高的人,这也意味着你必须要贡献1000ETH以上才有可能成为合约的owner
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] = contributions[msg.sender].add(msg.value);
if(contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
//获取请求者的贡献值
function getContribution() public view returns (uint) {
return contributions[msg.sender];
}
//取款函数,且使用onlyOwner修饰,只能被合约的owner调用
function withdraw() public onlyOwner {
owner.transfer(this.balance);
}
//fallback函数,用于接收用户向合约发送的代币
function() payable public {
require(msg.value > 0 && contributions[msg.sender] > 0);// 判断了一下转入的钱和贡献者在合约中贡献的钱是否大于0
owner = msg.sender;
}
}
思路解析
分析代码可以看到,有两种方法使自己成为拥有者,1调用contribute()向合约转超过1000ETH,2调用匿名的回调函数使自己成为owner。当然第一种方法是不可能的,所以需要试着调用回调函数。
回调函数是一个合约中可以有且只能有一个没名字的方法,同时也不能有参数,也不能有返回的值。执行回调函数可以有两个方法:1调用合约中一个没有匹配的函数,2向合约中发送一个不带有任何信息的、纯转账的交易。
那么我们就可以直接向合约转一笔纯转账的交易。可以通过控制台与合约交互,await contract.sendTransaction({value:1})
或是使用metamask直接向合约地址转账,这样就能出发回退函数,把合约owner改成我们。最后为了满足题目的条件,只需要执行withdraw函数即可。
总结一下攻击过程
contract.contribute({value: 1}) //首先使贡献值大于0
contract.sendTransaction({value: 1}) //触发fallback函数
contract.withdraw() //将合约的balance清零
知识点回顾
在 Solidity 中,回退函数是没有名称、参数或返回值的外部函数。它在以下情况之一执行:
- 如果函数标识符与智能合约中的任何可用函数都不匹配;
- 如果函数调用没有提供数据。
只能将一个这样的未命名函数分配给合约。
回退函数的属性:
- 它们是未命名的函数。
- 他们不能接受参数。
- 他们不能返回任何东西。
- 智能合约中只能有一个回退函数。
- 必须在外部调用它。
- 它应该被标记为payable。否则,如果合约收到没有任何数据的以太币,它将抛出异常。
- 如果被其他函数调用,则限制为 2300 gas。
Fallout
出题目的
合约代码
思路解析
知识点回顾
CoinFlip
出题目的
合约代码
思路解析
知识点回顾
Telephone
出题目的
合约代码
思路解析
知识点回顾
Token
出题目的
合约代码
思路解析
知识点回顾
标签:function,函数,writeup,contract,msg,Ethernaut,合约,public 来源: https://www.cnblogs.com/unknown404/p/16385347.html