$5000 from Raydium Bug Bounty Program ~バグハントがブルーオーシャンだと思った話~ part1/2
前書き
構成
Raydiumのバグバウンティプログラムに参加したら、$5000の報酬をもらえた話です。
この記事は二部構成になっています
パート1:バグハンティングに参加した私の体験レポート
パート2:バグハンティング界のリサーチ/体験を活かしてどう向き合うか
No Code No Vulnerability
まず初めに断っておきますが、この記事ではバグの詳細については最低限に留めます。コードも出ません。
理由についてはこの記事の後半でも他に述べますが、脆弱性の詳細をレポートした記事は検索すれば他にいくらでもあります。そしてそれらの記事はすべて、私よりはるかに高いスキルを持った人達によって書かれています。つまり、私が似た記事を書いてもレベルが違いすぎてその価値は薄いです
しかし私がひとつだけ、彼らに突出して勝っているものがあります。
それは……
私の持つプログラマーとしてのスキルの低さです(ドヤ)
逆説的に考えてみました。
おそらく、私よりレベルの低いホワイトハッカーなどまぁ存在しないと思います。バグバウンティに応募したこともはじめてです。
右も左もわからない状態でびくびくしながら応募してみたら、それがなんと$5000相当の報酬を頂けたのです。
なのでその立場でしか書けないものを書くべきです。
ここは強調しておきますが、私が認定を受けることができたのは、いくつもの幸運が重なり合って生じた結果です。バグハンティングの世界は同じDefiを舞台としていても、私たちが普段触っている感覚とは大きく異なり、落とし穴がたくさんありました。
それらの紹介も含めて、遥か高みにあると思っていた世界を運よくのぞき見ることができた私の体験をまとめてみます。
バグハンティングの世界って…
結論から言いますが、私の抱いた感想は「バグハンティングってブルーオーシャン(仮)じゃね?」です
当たり前ですがそれでも敷居は間違いなく高いです。ブルーオーシャンであることと敷居の低さはイコールにはなり得ません。その逆であり、敷居の高さこそがブルーオーシャンを満たす大きな要因であることは、心に留めておいてください。
さて、一般的にバグバウンティプログラムに参加しているホワイトハッカーたちはハッカーの中でもエリートの集まりです。こう言うとつけ入る余地がないように聞こえますが、私に舞い降りた幸運や失敗、実践とそれらの分析によってこのブルーオーシャン(仮)に参加するための条件や、攻略する糸口が見えることに期待します。
対象となる読者に向けて
この記事の目的は、その敷居の高さをなるべく下げることが主であり、私がそのハードルの下限となって指標となることです。
私をフォローしてくださっている方々で、既に高いスキル(プログラミングに限らずです)を持っている人は大勢います。どうぞ私を追い抜いていってください。
あなたはご存知ないかもしれませんが、あなたがこれまでに無償で投稿した記事や解説などの足跡を辿ることで、私はすでに多くの知識と利益を享受してきました。あなたのスキル・経験にはまだまだ及びませんが、ちょっと珍しい体験をしてきた私の話が、あなたにリターンできるかもしれない良い機会になることを願っています。そしてもし私の話に興味をそそられ、バグバウンティプログラムに参加することになれば、それはDefi全体にとってとても良いことです。
私を含むBotterという生き物は基本的に孤独であり、自己承認欲求に飢え、社会的意義がまったく無い(注:あくまで私個人の見解です)活動をしていますから、バグハントによってそういったモノを満たせるのも大きな利点です。
それ以外にも、プログラミングのスキルに自信があってもまだDefiに進出していない方、そもそもプログラミングに自信のない方にも、バグバウンティで収入を得るという日本では(おそらく世界でも)まだ未開拓なルートをひとつ示すことには、価値があると信じています。
条件はありますが、他の収入を得る方法よりも情報をオープンにすることが求められている、というのは大きな違いです。
学習のためのリソースが比較的豊富であり、そもそもターゲットとなるコードのほとんどがGithubに公開されており、(大抵の場合は)その範囲も限定されています。無論その分、より深度が求められますが。
これらはDappsやスマートコントラクトを学ぶ上で良いとっかかりとなりますし、冬の時代にはなおさらモチベーションの良い発生源となります。
プログラミング未習熟の方へ。指を咥えて眺める必要はないです。
ChatGPTの出現により、プログラミングを学ぶための学習コストはたったこの半年~1年の間で劇的に下がりました。学習を始めるにはよい時期かと思われます。そしてそういったつもりがない方にも、私の体験談を伝えることで、普段見えないDefiの裏側で琢磨している人たちにスポットを当て、「こういう面白そうな世界もあるんだな」とお見せできる読み物にできればと思います。
また前書きの最後に、私とは対照となるトップレベルのホワイトハッカーが書いたこちらの記事を紹介させていただきます。併せて読んでみてください
でははじめましょう
幸運、その一:Raydiumだったこと
時系列的には順序が異なりますが、これが一番大きかったので先に述べます。
私がバグの原因を特定できた時点において、Raydiumはバグバウンティをメールで受け付けていました。レポートにはPoC(概念実証)が求められるため、そういったものの書き方に無知な私は、調べてもよく分からなかったので「とにかくむちゃくちゃ詳しく書けばいいだろ」といった勢いでバグの報告を仕上げました。
この時、私にはタスクのキャパシティ的に、PoCや要件について深く掘り下げて調べる余裕がなかったというのもあります。
そしてIT分野以外のものも混在している&バグバウンティプログラム自体がまだ日本で浸透していない&そして大前提として職業エンジニアには遠く及ばない私の業界常識の欠如と相まって、とにかくバグバウンティへの様式に関しては知識不足でした。
当然ながら、私のPoC(自称)はまったく要件を満たせておりませんでした。
さてその後、2週間ほど前にproposalが可決されていたこともあってタイミングが重なり、バグが修正された後しばらしくして、ImmuneFiというバグバウンティプログラムのためのプラットフォームとの提携がスタートしました。
このImmuneFiというプラットフォームはホワイトハッカーとプロジェクトとのバグハンティングの橋渡しを担うサービスです。
こちらについては二部で主にフォーカスしていきますが、明確なガイドラインと学習リソース、そしてレポート作成及びPoCのテンプレートなどを提供しており、多岐に渡るDappsのバグハントを一元化する役割を果たしています。
さて、もし私がImmuneFiからアカウントを作成し、そこから自称PoCを送っていたらどうなっていたでしょうか?
はい、一発永久BANです😇
しかもたくさんタブーがあり、どれか一個でも触れていたら即OUTです😇
規則は厳格に定められており、ImmuneFiを介してバグハンティングプログラムに参加する場合においては必ず遵守しなければならないものですが、ImmuneFiを介す介さないに関わらず、ここは細部にまで目を通すことを強く推奨します。併せてFAQのリンクも貼っておきます。
https://immunefisupport.zendesk.com/hc/en-us/articles/7789428643217-Bug-Bounty-Program-and-Report-FAQs
ここは一番最初にふるいにかけられるステップであり、恐らく一番多くの人が勝手に自滅してくれる、一番おいしいスポットです。自由に好き勝手やっていいDefiの雰囲気をそのまま持ち込みがちなので、気を付けましょう。
さて、そんな偉そうなことを言っておきながら、私は自由に好き勝手書いたレポートを送り付けたわけです。それが受理され、認定、修正、支払いまで驚くほどスムーズに事が運んだわけですから、これほど幸運なことはないでしょう。
またプログラミングに関する査読、精査は非常に相手に負担を強いるものです。少し考えれば分かることですが、ユーザー側の目の届かないところで、プロジェクトには報奨金目当てのひやかしスパムが非常に多いだろうということは容易に想像がつきます。
その配慮が私のレポート(どれだけ詳細であっても)には圧倒的に欠けていました。
相手がRaydiumTeamだったからこそ、その寛大さに救われ、こうした貴重な体験を得ることができました。
この場で改めてお礼申し上げます
幸運、その二:存在証明
さて、それでは私がバグハントに取り掛かった時点に話をフォーカスしていきましょう。
私の報告したバグについて何も語らないには話が進められないので、比較的多くの人が目にするプラットフォームで観測された事象ならば問題は少ないと思います。
birdeyeから確認できる、Sol-Bonkのチャート4/30/2023現在のチャートです。ペアのプルダウンから、DEXのプールを指定することができます。
これを見ただけで違和感のある箇所を指摘できる人は、恐らくバグハントに関わらず仮想通貨界隈への適正が高い人かと思われます。
と言っても、私が違和感を覚えたのはこのようなチャートからではなく、普段自分が動かしているBotのログからでした。Defiでなくとも(CEXにおいても)こういったものを運用している人は、生じた異変に対する感度はクリプト界においても、おそらくかなり高い人種かと思われます。
そういった強みも私にプラスに働いた点ではありましたが、それよりももっと大きかったのは、事象、つまり不具合の存在がある程度担保された状態から、バグハンティングに取り掛かれたというのは無視できない点です。
通常のホワイトハッカー達の作業は、脆弱性があるかどうか分からない状態からバグを見つけ出すために多くの時間を割いています。その最も難しくストレスのかかるプロセスを省略できたというのは、私にとって大きなお膳立てをされた状態だったと言えます。
多くの熟練ホワイトハッカー達の報告は、主にまだ一度も発生していないバグを指摘するものです。
いかにこの点が私にとって大きな幸運だったかお判りいただけるかと思います。
とは言えそれでも、まだバグが存在するという証明にはなり得ません。birdeyeの不具合ということも考えられますし、要因は様々です。
そこで私がとった次のステップは、この違和感を覚えた箇所をクローズアップし、トランザクションを確認する作業でした。
具体的にはsolanaFMでブロックの番号を適当に入力して、該当する期間のブロックを割り出すという雑なやり方で、このプールへ送られたトランザクションを確認したり(正確にはこれはプールが正常化したあとに使いました)、そのあとDuneでクエリを書いて異常のあった期間の直前から直後までをすべてをリストにし、様々な条件(流動性を入れるor出す、売るor買うなど)でフィルター分けしたりといった作業です。
無論トランザクションが弾かれたProgram logも確認し、
それがJupiterのProgramがAssertしたもの(※これによる失敗TxはほぼすべてBotが恣意的に起こすもの)ではなく、
Raydium CLAMMのInvokeによるAssertであったこと、
Jupiterが導出したパスはどうなっているか、またそこからスワップしてトランザクションが実際に通るのかといったことを確認しました。
これらをあらかた行った上で、当初は自前のBotの保全のためにしていたことですが、「おそらくRaydiumCLMMに問題がある」と自信がないながらも得られた指針を基に、ここで私は初めて本格的にコードを読む作業に入りました。
ちなみにこの時点においても、私にはバグバウンティプログラムに参加するといった気概はおろか、その存在や実態についてもほぼ知らなかったという点については留意していただきたい点です。
ですから作業に入るためのモチベーションはほとんど無く、非常に腰の重い状態でした。(ちなみに私はそれまでの1か月、ほぼゲームしかしてないという体たらくの極みにいました。Pathfinder: Wrath of the Righteous というゲームは非常に人をダメにするので、キケンにつき触らないことをお勧めします。*最初のキャラクリエイトだけで諸々20時間以上費やすゲームとだけ、私からは申し上げておきます)
ちょうどゲームがクリア目前だったことも3つ目の大きな幸運かもしれませんが、これはきっとカウントしちゃいけないやつなのでノーカウントとします。ただしとても良い充電期間になりました!
またSolana同好の士であるk5さんからも、背中を押していただけるひと言を頂けたのも大きかったです。Solanaには少し寂しい雰囲気が漂っていますが、大きな可能性を感じるニュースも、雨後の筍のようにk5さんの発信により届いています。これからも応援しています。
幸運、その三:出題範囲
そういったわけで、ようやくコードを読む作業に入ったのですが、ここが一番しんどくて地味な上、そのくせお伝えできるような事柄の少ないパートです。
やったことはひたすらコードを読むだけで、手を動かすことなどあまりなかったですし、どこに原因があって何が影響しているかなどひも解くのにひたすら唸っていました。
しかし今まで自分がやってこなかったことと、今まで自分がやってきたこととの差が如実に現れた箇所となります。
準備ができていた部分としては、それはもうとにかく何度も繰り返しコードを書いてきた範囲だったので、そこがターゲットだったのはとにかく大きいです。
ストライクゾーンど真ん中だったわけで、具体的に挙げると集中流動性プールの実装、それもスワップの処理に関しては、WhirlPoolベースのコードで3回、RaydiumCLMMで2回、その他あまり知られていないプロトコルもそれぞれ1~2回と、各DappsのGithubにあるリポジトリをひとつもCloneやCargo add(インポート)することなく、手書きと自前スニペットからのコピペだけで複数回書き上げたという積み上げがありました。
参照とするものもすべてがRustで書かれているわけではなかったので、Javascript製のものを全部Rustに書き換えたり、重複する部分は多いけども細部が異なっていたりする各プロトコルの差異をできるだけ同じ関数で呼び出せるように改造したり、自分が使う用には必要のないものを極限まで削ってみたりと、かなり原型から離れてはいるけれども作りたい形で自由に流動性プールの実装を組み上げられるくらいにはできるようになっていたお題だったのです。
振り返ってみると非効率なことをやっていたと自分でも思いますが、この頃の私はRustの勉強が楽しくて相当熱を上げていた上に、早く仕上げることよりもRustという言語そのものへの理解を深めることに重きを置いていたので、楽をしようという発想はあまりなかったです。
それと集中流動性プールの実装はなかなかに難解で、UniswapV2型のプール(Constant Product Pool)の100倍はめんどくさいことを内部でやっており、平方根を使うけど浮動小数点は使えないから整数で無理やり表現する、といったことだとか、たくさんあるTick(CEXでいうオーダーブックの板一枚一枚みたいなもの)のうちどれが有効で無効かを効率的に探索するためにBitMapと呼ばれるかなりプリミティブなデータ構造を使っていたり、丸め処理や繰り上げ、オーバーフロー(桁溢れ)のチェック、ビット演算といった、誰もが一度はナニモワカランとなるであろう類のものがオールスターで揃っています。
言及したものが私の過去のツイートにも数多く残っており、時系列順に並べてみましたがこの数とその期間からも、その雰囲気を察していただけるかと思います。笑
以上を見返すと何言ってんだかと思うものもありますが、これだけやった範囲がバグハントでピンポイントに出題されたのですから、いくら雲の上と崇めるホワイトハッカー様といえど、この分野に関してだけは自分も同じ土俵に上がれる程度にはなっていたのかなと、今は思います。
とはいえ、原因を突き止めるのには本当に苦労しました。
「あった!」と思っても、「よく読んだら違う…」といったことを数回繰り返し、「ここに原因はない!」と結論付けて、「ここで見つからなかったらあとはもう知らん!考えられるとしたらそれしかない!」という気持ちで確認したところに、ありました。
原因としては、ひとつのトランザクションでは問題が顕在化せず、バグの存在を確認するにはふたつのトランザクションが必要となる類のものです。
いくつか脆弱性の記事や論文に目を通してみたところ、ステートメントの変遷を挙げる分類を見かけたので、このパターンに当てはまるのではないかと思います。
さて、私のやってきたことが無事原因の特定に繋がったことはお判りいただけたと思います。が、同時にプログラマーの方には、私がやってこなかったことを見抜かれているかもしれません。
それはずばりテストです。
そして私が行っていたのはデバッグでした。
両者は違ったものというわけではないのですが、重要なのはその尺度です。
デバッグはバグの発見、原因の特定、修正するすべてのプロセスを指すのに対し、テストはバグの発見にフォーカスしています。
つまり、あてずっぽうでバグを探そうとするのではなく、まずテストコードを書き、まずそれを走らせることでバグの存在を確認し、条件を変更したり走らせる本体コードの改変などで、少しずつどこがバグの要因になっているのかを絞り込んでいく作業をやりませんでした。
しかもどういった条件で再現できるかといった点については、予想ができていたので、テストコードを書くという発想さえあれば、原因の特定ももっとスムーズにできたはずです。
そしてこのテストコードを書く、という行為そのものが上述したPoCに他ならなかったのです。
ではなぜ私はそれをやらなかったのか?
はい、今までまったくテストなどしてこなかったからです。笑
「必要ないからいいや。めんどくさい」という理由で、やらずにいたツケがここで回ってきました。人にコードを見せることがないと高をくくっていたので、テストコードの書き方などの学習は私の優先順位の中でも最底辺に位置していたのです。
しかしどうでしょう?バグハンティングに参加するなら、テストスキルは最重要項目です。これで一気に優先順位トップに躍り出ました。
今はテストがしたくてしたくてたまりません。
最後に注意事項です。
テストやそのバグを特定するために、MainnetやTestnetにある対象プロトコルのコントラクトを使用してはいけません。
テストをする際は、その実装を自分でDevnetにデプロイするか、そこまでではなくとも、プロジェクト側がテスト用のstateデータを用意してくれています。少なくともRaydiumのGithubにはすべてまるっと用意されておりました。それを使用しましょう。
座して待つのみ
さてパート1の最終章ですが、非効率なデバッグでバグの要因も特定し、自称PoCも書き上げて送信したあとのことになります。
ですが、私がやることはもう何もなかったです。
慎重に調査するのでお待ちくださいといった返信と、
その結果Immunefi Vulnerability Severity Classification System V2.2.に基づくMedium:Block stuffing for profitの認定を受けたこと、
修正したテストコードを送るので確認してくださいと言われたことと、
自分のウォレットアドレスを教えたくらいです。
驚いたのは、私がバグレポートを送信してからのスピード感です。バグが修正されるまで半日程度でほとんどの工程が完了しました。
私がこれまで海外の何かのサポートにメールしたレスポンスの中で、最速なのは間違いないです。
そしてそのごく短い期間の中で、コントラクトのアップデートが入り、RaydiumのGithubレポジトリが更新されといったことが目まぐるしく行われ、あっという間にすべての工程が完了していました。
私の視点からはただ想像をすることしかできないのですが、プロジェクトの背後で尽力するチームワークとそのハードワークを、リアルタイムに、しかも私が起点となって垣間見れたことは、本当に形容しようのない感動を覚えました。
RaydiumTeamに、そしてSolanaのみならずDefiを支えるすべての人に敬意を表し、パート1を締めくくりたいと思います。
この記事が気に入ったらサポートをしてみませんか?