Ethernaut 题库闯关 #17 — Recovery

Ethernaut题库闯关连载的第17篇题解。

今天这篇是Ethernaut 题库闯关连载的第17篇,难度等级: 有点难。

欢迎大家订阅专栏:Ethernaut 题库闯关,坚持挑战下去,你的 Solidity代码能力肯定大有提高。

挑战 #17: Recovery

合约创建者构建了一个非常简单的代币工厂合约。任何人都可以轻松地创建新的代币。部署第一个代币合约后,创建者发送0.001 来获得更多代币。但是创建者犯了一个错误,他们忘记保存创建代币合约地址。

本次的挑战的目标是检索出代币工厂创建的第一个token的合约地址,并掉用destroy清空已发送给它的0.001 ETH,则此关完成。

合约源码如下:

/ SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import '@openzeppelin/contracts/math/SafeMath.sol';

contract Recovery {

  //generate tokens
  function generateToken(string memory _name, uint256 _initialSupply) public {
    new SimpleToken(_name, msg.sender, _initialSupply);

  }
}

contract SimpleToken {

  using SafeMath for uint256;
  // public variables
  string public name;
  mapping (address => uint) public balances;

  // constructor
  constructor(string memory _name, address _creator, uint256 _initialSupply) public {
    name = _name;
    balances[_creator] = _initialSupply;
  }

  // collect ether in return for tokens
  receive() external payable {
    balances[msg.sender] = msg.value.mul(10);
  }

  // allow transfers of tokens
  function transfer(address _to, uint _amount) public { 
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] = balances[msg.sender].sub(_amount);
    balances[_to] = _amount;
  }

  // clean up after ourselves
  function destroy(address payable _to) public {
    selfdestruct(_to);
  }
}

在查看解题思路之前,可以先自己想一想,自己会怎么做?

这一关的关键是了解合约的地址是如何生成的。

研究合约

合约本身很容易理解,但解决方案的难点并不在于如何利用它。让我们回顾一下代码并理解我们需要做什么。

Recovery合约是一个允许msg.sender创建代币的工厂合约。发送者通过每次调用generateToken 来部署一个新的SimpleToken合约。

一旦我们找到检索已部署的SimpleToken地址的方法,我们就可以调用destroy函数,该函数将执行一个selfdestruct(_to),将所有合约余额发送到_to地址。

SimpleToken合约至少有两个不同的问题:

transfer函数总是重置_to余额

// allow transfers of tokens
function transfer(address _to, uint256 _amount) public {
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] = balances[msg.sender].sub(_amount);
    balances[_to] = _amount;
}

这个函数msg.sender的余额被正确更新,_to的余额将被重置为amount。恶意行为者只需调用transfer(受害地址,0)就可以将受害者余额完全重置为0

destroy函数没有权限限制

// clean up after ourselves
function destroy(address payable _to) public {
    selfdestruct(_to);
}

合约的destroy功能执行了自毁操作码。该操作码销毁合约...

剩余50%的内容订阅专栏后可查看

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

2 条评论

请先 登录 后评论
Ethernaut CTF
Ethernaut CTF
信奉 CODE IS LAW.