
このハックパックでは、ボットネットの基本的な特徴を実装するための基礎的なフレームワークを順を追って説明します。C言語を扱った経験があると役立つかもしれません。このチュートリアルが気に入ったら、このリポジトリにスターを付けてください!
注意: ここで学んだことを悪意のある目的に使わないでください。このハックパックは教育目的のためのボットネットのケーススタディに過ぎません。このハックパックで学ぶ概念は、幅広いユースケース(基本的にはネットワーキングに関わるすべてのこと)に応用できます。最も重要なこととして、このハックパックはローカルでテストし、展開することを意図しています(このハックパックに関連して作成したものを他のハッカーと共有しないでください)。プライバシーは重要なので、それを尊重してください。
ボットネットを構築する前に、ボットネットとは何かを理解することが重要です。ボットネットとは、リモートでコマンドを受信し、それをローカルで展開できるコンピュータのネットワークです。必要に応じて、ネットワーク内の他のノードに情報を中継することもできます。ボットネットは、分散型サービス拒否(DDoS)攻撃からスパイウェアの広範囲な展開まで、あらゆることに使用されてきました。これまでに多くのボットネットを聞いたことがあるかもしれません。最も有名なのは、それぞれ380万台と360万台のIoTデバイスを制御したMiraiとGameover Zeusでしょう。ボットネットが特定のタスクを実装する方法には多くの違いがあります。しかし、ボットネットを成功裏に構築するためには、動作するネットワーク内で以下の機能を保証する必要があります。
私たちのボットネットは、次のことを行うべきです:
この構造は、Command & Control(C&C)ボットネットとして知られるものの特徴です。これらのボットネットには、1つのマスターサーバーと多数のスレーブサーバーがあります。しかし、このスタイルのボットネットは時代遅れで、マスタードメインへのアクセスを遮断することで簡単に破壊できます。より最近の洗練されたボットネットはピアツーピア(P2P)アーキテクチャに従っており、管理者権限はネットワーク内のすべてのノードまたは一部のノードに分散されています。これらのボットネットは、中央の制御点がなく、数百万のノードに成長する可能性があるため、セキュリティの専門家にとって大きな頭痛の種です。そのようなボットネットを壊すこと自体が興味深い読み物です。しかし、このハックパックの目的のためには、物事を単純に保ちましょう。C&Cボットネット用のシンプルなスレーブノードを実装します。

このハックパックは主にクライアントマルウェアの実装を扱います。マスターサーバーには、オープンソースのTCPサーバーであるNetcatを使用できます。Netcatはボットネットとは何の関係もありません。これは、クライアントとの間でテキストパケットを送受信するために再利用できる、便利で確立されたツールにすぎません(マスターとは実際にはそのようなものです)。Netcatサーバーを少し調整して、"master"というバイナリにコンパイルしました。ここでこれ以上の作業は必要ありません!マスターはすぐに使用できます。
より興味深い部分に移りましょう:リモートコマンドの受信と実行です(マルウェアの偽装については後で心配します)。ここでの目標は、スレーブノードを可能な限りシンプルにし、上記の要件に準拠することです。多くの定数がlib/macros.hで定義されているので、自由に使用してください。実装されたすべての関数シグネチャは、lib/connect.hまたはlib/utils.hにあります。
bot.cファイルを開いてください。サーバーで新しいノードを開始するとき、マスターがどのクライアントにコマンドを展開するかを知ることができるように、ノードに名前を付けるべきでしょう。使用できる命名規則は多数あります。IPアドレスを使用することはおそらく最良です。なぜなら、それは各クライアントの一意の識別子だからです。しかし、一般の人間にとって読みやすくするために、コンピュータのユーザー名を使用しましょう。C関数のgetenv()を引数"USER"とともに使用すると、コンピュータがUSER環境変数に格納しているものが返されます。これはユーザーのユーザー名が格納されている場所の1つなので、それを使用しましょう。また、スレーブが実行されているので、マスターを見つけましょう。これを行うには、マスターのIPアドレスを知っている必要があります。すべてのネットワークデバイスにはIPアドレスがあります。それは、他のノードを識別し、位置アドレス指定を行う役割を担っています。さらに、マスターは異なるポートで多くのサーバーを実行できます。したがって、マスターに接続するだけでなく、正しいポートも指定する必要があります。このポートはマスターによって選択されますが、変更できます。このハックパックでは、ローカルでテストすることを望んでいます。そこで、コンピュータをネットワークとして使用します。すべてのコンピュータのローカルIPアドレス("localhost"も解決される)は"127.0.0.1"です。マスターでは、ポート9999で実行するように指定しました。これらの3つの要素(マスターIPアドレス、マスターポート、スレーブ名)により、サーバーとクライアント間の通信パイプライン(ソケットと呼ばれる)を開始できます。これら3つの引数を関数init_socket()に渡してソケットを作成します。init_function()はCの組み込みコマンドではありません。むしろ、それを実装する必要があります。次に、受信メッセージを保持するためにスタック上にいくらかのスペースを割り当てる必要があります。ここではかなり余裕を持たせて、約10KBのスタックスペースを使用しましょう。このスタックポインタをmsgと呼びます。最後に、すべてがうまくいっていることを示すprintfステートメントがあります。
char* name = //クライアントのユーザー名を取得してnameに格納します
int channel = //SERVER、PORT、nameを指定してチャネルを初期化します
//CMD_LENGTHサイズのスタックスペースを割り当てて、char型のデータを保持します。スタックポインタをmsgと呼びます
printf("%s joining the botnet\n", name);
次にlib/connect.cに切り替えます。init_channel()を実装しましょう。まず、長さCMD_LENGTHのスタックcharバッファmsgと、マスターへの接続に関する情報を保持する特別なCネットワーク構造体であるserverを定義しました。まず、渡されたIPアドレスを、人間が読める形式(数字とドットで構成)からネットワークバイトオーダーのバイナリ形式に変換します。これは、ソケットライブラリの特別なC関数inet_addr()を使用して行われます。これは単にIPアドレスを受け取り、ネットワークで使用可能なバイナリを出力します。Cでは、sockaddr_inという構造体のフィールドを埋めることでネットワークを簡単に指定できます。この構造体のインスタンスはserverと呼ばれます。この構造体の3つのフィールドを埋める必要があります:server.sin_addr.s_addr(マスターIPアドレス)、server.sin_family(通信ドメインを指定する1バイト値)、server.sin_port(マスター上で接続するポート)。sin_familyには、ソケットライブラリによって提供されるCマクロを指定できます。通常、この場合のように、このフィールドをAF_INETに設定します。これは、接続がネットワークノードをIPアドレスで識別することを意味し、それが私たちの望みです。ただし、PF_INETを使用することも可能で、これはAF_INETと似ていますが、ネットワークがプロトコル内の任意のものを使用して特定のノードを識別できることを指定します。また、両方が存在する歴史的な理由も数多くありますが、それは私がよく知らないし、あまり気にしないことです。ただAF_INETを使用してください。最後に、サーバーポートを設定するときは、portを特別な関数htons()(host to network short)に通す必要があります。これはデータをホストバイトオーダーからネットワークバイトオーダーに変換します。このバイトオーダーの問題は、エンディアンと呼ばれるものに関係しています。詳細はで読むことができます。
最後に、マスターとスレーブの間の実際の接続を定義する必要があります! これを行うには、データを送信できるネットワークソケットを定義します。マスターが多くの「電気ソケット」を持っていると考えてください。次に、スレーブ側にマスターの「壁コンセント」に合う「プラグ」を構築する必要があります。これはソケットライブラリのsocket()関数を使用して行えます。なんと便利でしょう! socket()は3つの引数を取ります:通信ドメイン、ソケットタイプ、プロトコルです。通信ドメインには、おそらくもうお分かりでしょう:AF_INETです。ソケットタイプには、データを双方向にストリーミングするだけのソケットが必要です。したがって、与えられたマクロSOCK_STREAMを使用します。ソケットプロトコルについては心配しないでください。これはかなり基本的なネットワークなので、デフォルトのプロトコルを示す値0を使用しましょう。この関数はソケットを表すintを返します。この値をchannelに格納します。次に、ソケットを起動します(スレーブをマスターの壁コンセントに差し込みます)。C関数connect()を呼び出します。これは3つの引数を取ります:チャネル、sockaddr構造体、構造体のバイト単位のサイズです。connect()が正の整数を返す場合、マスターとの接続は成功です! 新しく確立された接続をテストするために、マスターに挨拶メッセージを送信しましょう! メッセージバッファを埋めて、respond()(まだ実装されていません)を使用してmsgをchannelを通じてマスターに送信します。最後に、init_channel()関数がこの成功した接続を返すようにします。
int init_channel (char *ip, int port, char *name) {
char msg[CMD_LENGTH];
struct sockaddr_in server;
server.sin_addr.s_addr = //ipをネットワークバイトオーダーに変換します
server.sin_family = //サーバーの通信ドメインを設定します
server.sin_port = //portをネットワークバイトオーダーに変換します
int channel = //SOCK_STREAMソケットを定義します
if(channel < 0) {
perror ("socket:");
exit(1);
}
int connection_status = //定義したチャネルを使用してスレーブをマスターサーバーに接続します
if (connection_status < 0) {
perror ("connect:");
exit(1);
}
//文字列をmsgにロードして挨拶メッセージをマスターに送信します(ヒント:snprintfが役立ちます)
respond (channel, msg);
return channel;
}
スレーブがマスターに接続されると、常にメッセージを待機し、コマンドを受信したらすぐに行動する必要があります。そこで、無限whileループを使用して、これらのメッセージを受信および解析しましょう。bot.cのprintfステートメントの下に、recieve()とparse()の2つの関数をその順序で呼び出す無限whileループを追加します。両方の関数は、channelとmsgスタックバッファを引数として取ります。関数シグネチャはlib/utils.hにあります。これは次のようになります:
無限ループ {
recieve(...);
parse(...);
}
utils.cに移動して、recieve()とrespond()を実装します。recieve()はチャネルからメッセージを取得し、respond()はチャネルを通じてメッセージを送り返します。respond()のパラメータは、ソケットアドレスsとスタックバッファmsg_bufです。C関数write()を使用して、スタックバッファの内容をチャネルに書き込み、そのステータスを返します。write()には3つの引数が必要です:ソケットアドレス、メッセージバッファ、メッセージの長さです。
int respond(int s, char *msg_buf) {
//msg_bufの内容をソケットsに書き込み、ステータスを返します
}
recieve()も同様に単純なヘルパーです。msgバッファをリセットします(ヒント:memset()を使用)。次に、ソケットライブラリ関数read()を呼び出してmsgを読み取ります。read()は3つの引数を取ります:ソケットアドレス、メッセージバッファ、メッセージの最大期待長です。
int recieve(int s, char *msg) {
//msgバッファをリセットします
int read_status = //ソケットsの内容をmsgに読み込みます
if (read_status) {
perror("log:");
exit(1);
}
return 0;
}
もうすぐ完了です! 今のところ、ボットネットはかなり退屈です。ソケットを介してメッセージを受信および送信することしかできません。受信したものをターミナルで実際に実行させましょう。まず、関数parse()を実装します。これは名前が示すとおり、コマンドを解析します。メッセージが不正な形式かどうかを確認する簡単なエラーチェックを行うことができます。また、受信したが自分宛てではないメッセージは黙って無視したいです。メッセージはマスターから(ボットネットの名前):(実行するコマンド)の形式で送信されます。前者はすでにやっておきました。2つのチェックを通過したら、コマンドをexecute()関数に渡しましょう。
int parse (int s, char *msg, char* name) {
char *target = msg;
//メッセージがこのクライアント宛てかどうかを確認します。違う場合は、0を返してパケットを黙ってドロップします
char *cmd = strchr(msg, ':');
if (cmd == NULL) {
printf("Incorrect formatting. Reference: TARGET: command");
return -1;
}
//cmdポインタを実際のコマンドの先頭に調整します
//終端文字をコマンドの末尾に調整します
//受信したコマンドの内容をローカルに表示するステートメントを出力します
execute (s, cmd);
return 0;
}
次はクールな部分です。execute()は、受信したコマンドをターミナルにパイプし、出力をソケットを通じてマスターに書き込む必要があります。各行の入力を格納するスタックバッファを作成します。次に、C関数popen()を使用して入力を実行し、出力をファイルfに格納します(この時点では、多くの方法があります。マスター入力を使用してボットネットを本当にクールなことを行うようにカスタマイズしたり、ボットネット内の他のノードと自律的な連携/更新を実行したりできます。自由に創造性を発揮してください。今のところは、標準的な目標に固執します)。fを行ごとに解析し、すべてをソケットを通じて送信します。fを閉じて完了です!
int execute (int s, char *cmd) {
FILE *f = //popenを使用してコマンドをローカルで実行します
if (!f) return -1;
while (!feof (f)) {
//fを行ごとに解析し、出力をマスターに送り返します
}
fclose(f);
return 0;
}
新しいボットネットを次のターミナルコマンドでコンパイルします:
gcc -lcurl lib/connect.c lib/utils.c bot.c -o bin/slave
1つのターミナルウィンドウでbin/masterを実行し、他のウィンドウでbin/slaveを実行します。コマンドを(スレーブのユーザー名):(リモートターミナルコマンド)として入力します。おめでとうございます! ボットネットを構築しました!
マルウェアを偽装および展開するためにできる本当にクールなことがあります。実際、それはそれ自体が一つの分野です。できることの1つの例として、マルウェアを画像として偽装することがあります。パンダの画像を使用しましょう。utils.cに、パンダの画像をcurlで取得してプレビューで表示する簡単な関数を追加しました。これにより、ユーザーにはパンダの画像を開いているように見えますが、実際にはマルウェアを実行しています。これを追加するには、次のコード行をbot.cに含めます:
char* open_cmd = alias_img();
system(open_cmd);
free(open_cmd);
次に、任意の画像を右クリックして「情報を見る」を選択します。bin/slaveに対しても同じことを行います。画像のサムネイルをbin/slaveの実行可能ファイルのサムネイルにドラッグします。これにより、デスクトップ上での見た目が変わります。しかし、特徴的な.pngファイル拡張子がまだありません。実行可能ファイルを次の名前に変更します:
panda⒈png
これで、これはpngファイルのように見えます。しかし、これが依然としてUnix実行可能ファイルであることを隠すために、"."の代わりにUnicode文字"1."を使用しています。LEFT-TO-RIGHT OVERRIDE文字など、実行可能ファイル名を偽装するためにもっと多くの説得力のあるUnixトリックを使用できます。より極端なケースでは、画像内やファイルマクロ内にコードを埋め込んで、ホストが開かれたときに同時に実行させることができます(トロイの木馬のようなものです...)。しかし、TreeHacksにはサイバー犯罪者は誰もいないので、そのような技術をあまり気にする必要はありません ;)。
完全に動作するボットネットができたので、自分自身に挑戦できる多くの拡張があります。私たちのボットネットはまだかなりつまらないものです。ユーザーが毎回クリックしない限り、あまり何もできません。いくつかの提案を以下に示します:
ボットネットは、コンピュータがシャットダウンした場合でも、何とかしてコンピュータに残り続けることができれば、攻撃者にとって本当に信頼できる悪意のある活動の源になります。起動のたびにスレーブを再び開始する方法を試してみてください。これにより、ユーザーがマルウェアをクリックすると、それを掃除するまで自分のコンピュータを感染させてしまいます。これを実現するための1つの提案は、実行可能プロセスをデーモンに変換することです。次に、起動時に実行されるべきデーモンのリストに実行可能ファイルを追加する設定ファイルを生成します(クラウドストレージアプリケーション、チームメッセージングプラットフォームなどはすでにこれを行っています)。詳細はこちらをご覧ください。
ピアツーピアネットワークの実装は、ネットワーク設計を再配置するだけです。ただし、P2Pネットワークの鍵は、管理者/攻撃者がネットワーク上の任意のノードを介してマスター制御を達成できることです。したがって、攻撃者は、任意のノードのマスター制御を可能にする何らかのマスターキーと暗号化されたログオンを持つ必要があります。ピアツーピアネットワークの仕組みについて詳しくはこちらをお読みください。
実装したマスター-スレーブ構造はあまり安全ではありません。スレーブはマスターノードを殺すことで簡単に解放できます。理想的には、P2P設計に切り替えるべきです。ただし、マスターのコマンドを、ボットネットに展開される前に、攻撃者が制御する一連のボットをランダムに経由させることで、マスターのセキュリティをわずかに高めることもできます。これにより、専門家がコマンドの中心を見つけたり、攻撃者ノードとクライアントノード間のボットネット呼び出しを追跡したりすることが難しくなります。
おそらく、より重要なのは、ネットワーキングをもっと試してみることです。私たちのネットワークはこれ以上ないほど単純です。多くの点で、非常に弱く、決して堅牢ではありません。したがって、Internet Relay Chat(IRC)などの確立されたネットワークプロトコルを探索して、より適切なネットワークを構築したいかもしれません。時間はかかりますが、非常に教育的で、価値のある投資です。詳細はこちら:https://oramind.com/tutorial-how-to-make-an-irc-server-connection/。
このハックパックでは、マスターサーバーの代わりとして自由に利用できるオープンソースプロジェクトを使用しました。しかし、これには多くの欠点があります。まず、マスターサーバーをカスタマイズして、ネットワークを通じて自動化されたコマンドを送信することはできません。コマンドライン入力の使用に限定されます。次に、ボットネット上のすべてのスレーブがすべてのコマンドを受信することに気付いたかもしれません。実行の条件は、ターゲット名がスレーブ名と一致するかどうかをチェックすることです。偽の場合は、コマンドは黙ってドロップされます。これはブロードキャストネットワークとして知られています。より最適なのはマルチキャストネットワークかもしれません。ブロードキャストネットワークでは、ノードは接続されているすべてのノードにパケットを中継します。マルチキャストシステムでは、パケットを受信する特定のノードのサブセットを指定できます。さらに、マルチキャストネットワークを使用すると、コマンドの割り当てがクライアントから、それが属するマスターサーバーに移動します。独自のマスターを実装して、ボットネットをブロードキャストからマルチキャストに切り替えてください。
ネットワークプログラミングの総合ガイドについては、次を参照してください:http://beej.us/guide/bgnet/output/html/multipage/index.html。
楽しんでいただけたでしょうか!
MIT
HackPacksは、毎年2月にスタンフォードで開催されるハッカソンで、ハッカーが素晴らしいプロジェクトを構築するのを支援するためにTreeHacksチームによって構築されています。私たちは、あらゆるスキルレベルの誰もが素晴らしいものを作ることを学べると信じており、これはハッカー文化を促進するための1つの方法です。私たちはハックパック(内部テクノロジーとともに)をオープンソース化しているので、誰もがそれらから学び、使用できます! 自分のハッカソン、ワークショップ、その他構築を促進するあらゆるものに自由に使用してください :)
TreeHacksに参加することに興味がある場合は、応募期間中にウェブサイトから応募できます。
ここGitHubでフォローして、私たちが行うすべてのオープンソース作業を見ることができます(issue、貢献、あらゆる種類のフィードバックを歓迎します!)。また、Facebook、Twitter、InstagramでTreeHacksからの一般的な更新情報を見ることができます。