源码:
// 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``nonceRLP:是数据结构上的编码器,是序列化以太坊中对象的默认编码器。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。
- 打开浏览器,单击指向新合同的链接。
Internal Txns - 新合约地址现在应显示在左上角。
攻击合约
contract attack{
SimpleToken token =SimpleToken(0x78BDf4FE94FDC3eB49D67332d5d90cc752757673);
function att()public{
token.destroy(payable(msg.sender));
}
}