抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Recovery

1. 题目要求

  • 1.1 合约创建者构建了一个非常简单的代币工厂合约。 任何人都可以轻松创建新代币。 在部署了一个代币合约后,创建者发送了 0.001 以太币以获得更多代币。 后边他们丢失了合约地址。

    如果您能从丢失的的合约地址中找回(或移除),则顺利通过此关。

  • 1.2 题目代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Recovery {

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

}
}

contract SimpleToken {

string public name;
mapping (address => uint) public balances;

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

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

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

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

2. 分析

tips: 参考博客

  • 2.1合约地址是确定性的,可以从合约的部署者地址和来自部署者的部署交易的随机数中得出。

    在这种情况下,我们从一开始就有这些信息:

    1. 合同部署者地址(我们的例子,在我的例子中0xc03f501C5987CAaC9e4470849f13eEA338b76E9f
    2. 部署第一个 SimpleToken 的随机数(1 如练习所述)

    因此,我们可以轻松计算出第一个 SimpleToken 部署的合约地址,结果为0xa26D4caf289D657F24f8d2D26f0DFe99a0B312db.

    从技术上讲,这里很容易作弊,因为通过检查实例合约的内部交易,很容易在区块浏览器上看到我们要排空的合约的合约地址。然而,练习的目的是我们自己推导出地址。

    有了这些信息,我们现在要做的就是调用destroy()SimpleToken 合约中的函数,并将其中的资金定向到任何地址,以便将练习标记为已完成。

  • 2.2 做法:

  • 一旦我们有了这两个细节(部署地址,随机数),就像我在解决方案中描述的那样,我们就可以在 python 中(或直接在 solidity 中)编写一个函数来获取地址。作为一个足智多谋的开发人员,即使我知道如何计算它,我仍然决定去 StackExchange 中找到一个现成的解决方案以跳过这个:

  • # compute address of a given contract to be deployed from
    # the deployer address + nonce, as stated in the Section 7 
    # of the Ethereum yellowpaper for contracts created using CREATE
    def mk_contract_address(sender: str, nonce: int) -> str:
        """Create a contract address using eth-utils.
        # Modified from Mikko Ohtamaa's original answer which was later
        # edited by Utgarda
        # Obtained from https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
        """
        sender_bytes = to_bytes(hexstr=sender)
        raw = rlp.encode([sender_bytes, nonce])
        h = keccak(raw)
        address_bytes = h[12:]
        return to_checksum_address(address_bytes)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    - 然后,我们可以插入这些值并找到第一个 SimpleToken 部署的地址:

    - > first_simpletoken_contract_address = mk_contract_address(recovery.address, 1)

    - 连接到这个合约并调用`destroy()`函数,将资金发送到我的地址。

    - > simpletoken.destroy(acc.address, _**from**)

    - 攻击合约来自 [参考视频](https://www.youtube.com/watch?v=K8AFyNiuTXs)

    ```solidity
    contract Dev {
    function recover(address sender) external pure returns (address) {
    address addr = address(uint160(uint256(
    keccak256(abi.encodePacked(
    bytes1(0xd6), bytes1(0x94), sender, bytes1(0x01)
    ))
    )));
    return addr;
    }
    }

3. 解题

  • 3.1 获取实例地址:0x7802095a90641cd76543bc7df683d24D4bdd4436
  • 3.2 部署Dev合约,调用合约中的recover() 函数,传入的形参为关卡实例
  • image-20230225143018522
  • 3.3 将调用recover() 函数 返回的地址带到区块链浏览器上查看交易
  • image-20230225143151808
  • 3.3 使用recover() 函数 返回的地址获取部署的SimpleToken合约,并将调用SimpleToken合约中的destroy() 函数,传入的形参为自己 的钱包地址
  • image-20230225143402593
  • 3.4 执行destroy() 函数之后,到区块链浏览器中再去查看地址的余额为0
  • image-20230225143518994
  • image-20230225143620477
  • 3.5 提交案例
  • image-20230225143721298
  • 3.6 成功!!!!

评论



政策 · 统计 | 本站使用 Volantis 主题设计