
レッドチーム向けDNSコバートチャネルインプラント。
WEASELは、依存関係のないPython 3を使用した小さなインメモリインプラントです。ビーコンクライアントは、ホストに関する少量の識別情報を、あなたが管理するDNSゾーンに送信します。WEASELサーバーは、プリベイクされたコマンドや任意のコマンドを実行するようクライアントにタスクを割り当てることができます。
WEASELはステージ1ペイロードであり、検出が難しく、ノイズの多いフル機能のステージが検出されたときにアクセスを回復するのに役立ちます。
ステータス
具体的な使用方法については、クライアントのREADMEとサーバーのREADMEを参照してください。
サーバーまたはクライアントを起動するには、スクリプトを直接実行するか、Pythonインタープリターに渡します。
C2ドメインにはそれぞれ、server.pyを実行しているホストのIPアドレスを指すNSレコードが必要です。
WEASELはPython 3.6以上が必要です。
クライアントは自己完結型で、標準ライブラリのみを使用するため、macOS、Linuxなどで実行できます。
サーバーにはpipからの依存関係がいくつかあり、サーバーのrequirements.txtに含まれています。サーバーはLinuxで実行することを推奨しますが、macOSや他の*nixで実行することも可能です。
この場合、ビーコンを難読化・ミニファイする必要はありません。print文は保持されます。上記の使用方法と同様に、beacon.pyのserversにあるドメインのNSレコードが、server.pyのIPアドレスを指していることを確認してください。
サーバーホストで:
sudo python3 server.py
被害者ホスト(サーバーと同じでも可)で:
python3 beacon.py
WEASELを使用するために、これを理解する必要はありません。
ビーコンは、AAAAクエリと応答を使用してDNS経由で通信します。TXTレコードはDNSマルウェアやトンネルで使用されることが知られているため、使用しません。ブルーチームは、大きなTXTクエリにアラートを出すDNSトンネリング検出をしばしば持っています。
クライアント側は操作にrootを必要とせず、rawソケットを使用せず、不正なDNSパケットを作成しません。通常のシステムおよび言語提供のインターフェースを使用してDNSリクエストを行います。情報はレコード自体にエンコード+暗号化されています。
このビーコンは、低帯域幅で低速を意図しています。どのホストにいるかを知らせ、必要に応じてさらにステージを起動する手段を提供するだけで、それ以上はありません。任意のコマンドサポートはありますが、通常のインタラクティブシェルや通信チャネルとして使用することを意図していません。
WEASELは動作させ続けるステージ1であり、フル機能(したがってノイズが多い)のステージが検出された後もアクセスを維持します。
WEASELは当初、信頼できる足掛かり/エクスプロイトベクターがある高稼働サーバーを対象としていました。フォレンジックの回避が優先事項でした。その結果、ネイティブの永続化機能はありません。
お気に入りの永続化テクニックにその実行を追加することで永続化できます。これは読者の課題とします :)
**リクエスト(クライアントから)**は、次のようにフォーマットされた名前に対する単一のAAAAクエリです:
<preamble><data>.<stream>.<session>.domain.tld
プリアンブルは2バイトです。Preamble[0]はそのパケットのシーケンス番号です。Preamble[1]はそのストリームのパケット総数です。
データは50バイトに制限され(設定可能)、ペイロードを含みます。ペイロードはカスタムアルファベットでbase32エンコードされています。
まず、すべての 'w' 文字が '-' に置き換えられます。
次に、パディング文字が '=' から 'w' に置き換えられ、DNS文字セット[a-z0-9]と[-]に準拠します。
'=' を直接 '-' に置き換えないのは、パディングは常に文字列の末尾にあり、ホスト名が '---' で終わるのは疑わしく、DNS RFCにも反するからです。この方法により、パディングがある場合、文字列は 'www' で終わり、疑わしさが減り、RFCに準拠します。
**応答(サーバーから)**は、1つ以上のAAAA応答で構成されます。
各AAAA応答は、socket.inet_ntopを使用してIPv6アドレスとして表された16バイトの暗号化ペイロードです。DNS応答内の応答は転送中に順序が維持されないため、クライアントリクエストと同様にシーケンスされ再構成されます。
トランスポートペイロードは、^ 文字で区切られたデータ要素の文字列です。
リクエストと応答は次の形式に従います:
<type>|<data>
セッションは長期間存続します:クライアントはビーコンが最初に実行されたときにセッションを開始し、そのセッションはそのクライアント上でビーコンがアクティブである間続くはずです。ビーコンはインメモリで永続的ではないため、セッションデータはそのPythonプロセスのメモリに保存されます。ビーコンの新しい呼び出しはすべて新しいセッションを開始します。
セッションの開始には、クライアントが一意に識別できる非データプリアンブル(これが新しいセッションであることをサーバーに通知するため)を含むメッセージを作成します:32バイトのDiffie-Hellman公開鍵と16バイトのランダムAES IVの連結。
サーバーはこれを受信し、自身の32バイトの公開鍵で応答します。この時点で、クライアントとサーバーは共有セッション鍵を確立し、このセッションの存続期間中、AES-128 CTRモードを使用してデータペイロードを暗号化するために使用されます。Diffie-Hellman Ephemeral交換により、各クライアント-サーバー接続は前方秘匿性を持つ一意のセッション鍵を使用します。
暗号は意図的に脆弱になっています。理由はいくつかあります:
以下は暗号スキームの既知の問題です:
p はRFC 3526のGroup 5を最初の32バイトに切り詰めたものです。これにより公開鍵と秘密鍵が32バイトに制限されるだけでなく、Group 5はすでに非推奨であり推奨されていません。この悪い決定を「Group 1」と呼んでいます。a にCSPRNGではなくrandom.randint()を使用しています。クライアントとサーバー間で送信される各メッセージは、一般的なDNS covertチャネル検出の52バイト制限を下回るために、最大50バイトのパケットにパケット化する必要があります。特定のメッセージのすべてのパケットは同じストリームの一部です。メッセージ == ストリーム。
ストリームは2バイトのランダムな16進数で識別されます。クライアントリクエストの形式は次のとおりです:
<preamble><data>.<stream>.<session>.domain.tld
ストリーム内の各パケットの2バイトのプリアンブルには、シーケンス番号とそのストリームのパケット総数が含まれています。これにより、サーバーはすべてが到着したタイミングを知ることができます。
これはDNSであるため、UDP上で行われ、データグラムの到着順序について保証はありません。そのため、WEASELはシーケンス、再構成、多くのビーコンからの複数ストリームの追跡を考慮する必要があります。
各ストリームは、ペイロードの暗号化/復号化のために、グローバルに共有されるAES-128-CTR暗号を再初期化します。
ペイロードは、ストリームが完了した(すべてのパケットが到着した)場合にのみ復号化できます。base32がなければ、パケットが欠落していてもメッセージの一部を復号化できますが(AES-CTRはストリーム暗号のため)、部分的なストリームをbase32復号化することはできません。まあいいでしょう。DNSクライアントの性質上、応答を受信するまでリクエストは複数回(通常2回または4回)行われるため、各パケットは少なくとも2回送信されるはずなので、ストリーム内のすべてのパケットを受信できる可能性は高いです。パケットやストリームをドロップしても大した問題ではなく、ビーコンは後で再度チェックインし、その時はうまくいくでしょう。
貢献方法については、CONTRIBUTINGファイルを参照してください。
WEASELはMITライセンスです。詳細はLICENSEファイルを参照してください。
| タイプ | 意味(送信者) | 別名 | データ |
|---|
| 0 | 確認済み | ACK | ランダムな16進数 |
| 1 | チェックイン(クライアント) | PING | ランダムな16進数 |
| 2 | 自分自身を終了(サーバー)、自分自身を終了(クライアント) | FIN | |
| 3 | 初期化メッセージ(クライアント) | SYN | `version |
| 4 | 再接続(サーバー) | RST | |
| 5 | コールバック間隔の設定(サーバー) | 秒数 | |
| 6 | ネットワークインターフェースデータの取得 | eth0 1.2.3.4/24\neth1 fe80:::/64\n... | |
| 8 | 任意のPython3コードを666バイトまで評価(サーバー)、出力の最初の400バイトを返す(クライアント) | EVAL | python3 ワンライナースクリプト |
| 9 | 任意のコマンドを666バイトまで実行(サーバー)、出力の最初の400バイトを返す(クライアント) | EXEC | bashコマンド |