見出し画像

Lisk の Collision Attack について

はじめに

Lisk公式Twitterでアナウンスされている Collision Attack について書きます。


Lisk における Collision Attack ってなんぞや

ざっくり言うと、複数のパスフレーズから同一アドレスが生成されることを利用した、未初期化アカウントを狙った攻撃です。


Collision Attack を受けるとどうなるんや

全LSKが盗まれる可能性があります


そもそもなんでそんなことになんねん

Lisk はユーザビリティを考慮した結果、約20桁の数値に末尾Lという非常に短いアドレスとなっています。
「短いアドレスの方が分かりやすいやん!」
その通りなんですが、それが落とし穴だったわけですね。

Liskのパスフレーズは2048ワードから12ワードを使用して作成されています。

BIP39 english wordlist
github bitcoin/bips

単純に考えれば 2048の12乗の組み合わせになりますが、実際には128bitのエントロピーから生成されるので、2の128乗の組み合わせになります。

128bitエントロピー
16進数表記だと以下の範囲
00000000000000000000000000000000 ~ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

2048の12乗は約5444500000000000000000000000000000000000
2の128乗は約340280000000000000000000000000000000000
なので1桁ちがいますね!

このことからLiskのアドレスはパスフレーズの組み合わせ数から比べるとだいぶ少ないのがわかります。
そして、Liskのアドレスをパスフレーズから生成する際に行っている処理によって、複数のパスフレーズで同じアドレスが生成されてしまうという問題が発生します。

※ちなみに、パスフレーズからアドレスがどうやって生成されるかは以下の記事参照


どうやったら盗まれへんのん?

アカウントの初期化を行う必要があります。
とはいっても、もっているLSKが0になるということはありません。
受信以外の処理(送信、投票、2ndパスフレーズ登録など)を行うことで、公開鍵が登録され、パスフレーズとアドレスを1:1に紐づける作業を初期化と言っています。

パスフレーズからアドレスを生成する際に秘密鍵と公開鍵が作成されます。
この秘密鍵と公開鍵はパスフレーズと紐づいている為、同一アドレスとなるパスフレーズであっても値は別になります。

もっとも簡単な初期化方法は、自分自身にLSKを送金することです。
「入金のみしかしてないかも...」という方は必ず確認して、初期化がされていない場合は必ず初期化してください。

ただし、必ずしもこれをしたからといって盗まれない保障はありません。
パスフレーズの管理方法や、Collision Attackを行っている際にたまたま完全一致するパスフレーズを引き当てられる可能性もあります。
心配な場合は2ndパスフレーズの登録を行ったり、リスク分散としてアドレスを分割するなどを考えると良いかと思います。


初期化されてるかどうかどうやって確認するん?

Lisk Explorerで自身のアドレスを入力し、Public Key (公開鍵) の表記があるかどうかで確認できます。

画像1

初期化されていない場合

画像2

初期化されている場合

画像3


Collision Attackってそんな簡単に成功するもんなん?

しません。
上記でも記載の通り、パスフレーズは約340280000000000000000000000000000000000通り。
アドレスが20桁だとしてもかなり確率が低いことが分かると思います。

とはいっても、実際に起こっているのはなぜかというと、年単位で数多くのPCを使って実行しているからだと思います。

2021/02/18の朝9時頃の時点
アカウント数:約282330件
1LSK以上保持している未初期化アカウント数:12415件
※万博おじ調べ

2日前
アカウント数:約281700件
1LSK以上保持している未初期化アカウント数:12460件
※万博おじ調べ

となっており、未初期化アカウント数が減少していることがわかります。
(盗まれたのか、初期化したのか、1LSK未満になったのかはわかりませんが)

なお、盗難被害にあっているアカウントの多くは2018年以前に購入し放置されているものです。
(だからと言って新しく作ったから安心と言うわけではありません)

この理由として万博おじの予想は

2018年頃に未初期化アドレスの一覧を取得。
以降、パスフレーズからアドレスを生成し、未初期化アドレスの一覧を突き合わせる処理を延々と行っている。


からだと思っています。

※最新の未初期化アドレスを狙うには、生成したアドレスをAPIを利用して初期化されていないかを確認する必要があり、それは非常に効率が悪い。

万博おじが興味本位で試してみた結果は以下の通り

以下のスペックのPCでCollision Attackを行う処理を5スレッド同時実行すると、延べ1000万件で50分かかった。
[スペック]
OS:Windows 10 (20H2)
メモリ:8GB
CPU:Core i5 2.4GHz

※もちろん1件もヒットしなかった。
※VPSで丸1日実行してもヒットしなかった。
※ヒットしたらHQへ連絡してみようと思ったけど、リソースの無駄だから早々にやめた。
※盗んだと思われて捕まりたくないし。


盗まれたんだけどどうしたらええのん?

どうしようもありません。
どうしても何かしたい場合は以下のことをしてください。
・警察に相談
・盗難者のアカウントが取引所へ送金している場合は、その取引所に相談
※日本の取引所はほぼ全てKYC(本人確認手続き)を行っているので何らかのアクションがあるかもしれません。

なお、警察や取引所も対応できない可能性が高く、基本的には泣き寝入りになると思っておいてください。
また、LiskHQも対応は出来ません。
これはLiskに限ったことではなく「盗まれた資産を戻す」、「盗難者のアカウントをBANする」為には直接データをいじる必要があり、それをするということはブロックチェーン自体の信頼・信用を失わせる行為だからです。


ちなみに...

未初期化アドレスのまま Core 3.0.0 がメインネットに適用されると、自動的に新しい形式のアドレスにならない為、Reclaim トランザクションという特別なトランザクションを行わない限りLSKの移動などが行えません。

Reclaim
Lisk Mainnet specifications


おわりに

非常に確率は低くても、全LSKが盗まれる可能性を少しでも下げるため、また、Core 3.0.0 適用後にめんどうな作業が必要になるので初期化は必ず行いましょう。


応援、文句などあればコメントください

Twitterしてます
Twitter - 万博おじ

Lisk関連のアプリとかツールっぽい何か作ってます
Github - lisknonanika

LSKくれると泣いて喜びます
Lisk Explorer - 5380827711560203827L


おまけ(Collision Attack 関連ソース)

1LSK以上を持つ未初期化アドレスをリストアップ

const { APIClient } = require("@liskhq/lisk-api-client");
const fs = require("fs");

(async () => {
 const outputFile1 = "./log/non_initialize_account.json";
 const outputFile2 = "./log/non_initialize_address.json";
 const client = APIClient.createMainnetAPIClient();

 fs.writeFileSync(outputFile1, "[\n", (err) => {if (err) throw err;});
 fs.writeFileSync(outputFile2, "[\n", (err) => {if (err) throw err;});

 let i = 243500;
 while(true) {
   try {
     const ret = await client.accounts.get({limit: 100, offset: i});
     for (let j = 0; j < ret.data.length; j++) {
       const data = ret.data[j];
       if (!data.publicKey && +data.balance >= 100000000) {
         fs.appendFileSync(outputFile1, `${JSON.stringify(data)},\n`, (err) => {if (err) throw err;});
         fs.appendFileSync(outputFile2, `"${data.address}",\n`, (err) => {if (err) throw err;});
       }
     }
     i += ret.data.length;
     if (ret.data.length !== 100) {
       fs.appendFileSync(outputFile1, `{"count": ${i}}\n]\n`, (err) => {if (err) throw err;});
       fs.appendFileSync(outputFile2, `"count: ${i}"\n]\n`, (err) => {if (err) throw err;});
       break;
     }
   } catch (err) {
     console.log(err)
   }
 }
})();

Collision Attack

悪用されると困るので公開しない。
ちょっと考えればコーディングが出来る人は思いつくと思うし。

興味本位でやるのは止めないが他人の資産を盗むのは犯罪です。

もし見つけたら盗むのではなく、HQへ連絡してみてね。
※それで何か変わるわけではないと思うが、しないよりマシか?

この記事が気に入ったらサポートをしてみませんか?