ラック・セキュリティごった煮ブログ

セキュリティエンジニアがエンジニアの方に向けて、 セキュリティやIT技術に関する情報を発信していくアカウントです。

【お知らせ】2021年5月10日~リニューアルオープン!今後はこちらで新しい記事を公開します。

株式会社ラックのセキュリティエンジニアが、 エンジニアの方向けにセキュリティやIT技術に関する情報を発信するブログです。
(編集:株式会社ラック・デジタルペンテストサービス部)

次世代C2フレームワーク、Covenantをはじめよう

f:id:lac_devblog:20210728091716p:plain
※こちらの記事は2020年8月30日公開note版「ラック・セキュリティごった煮ブログ」と同じ内容です

デジタルペンテストサービス部のしゅーとです。

今回は、統合的な Red Teaming (Red Team演習) を実現するためのオープンソースのC2フレームワークである「Covenant」の紹介です。

Covenant はユーザ管理、画一的なオペレーションを可能とする Task 機能、各種実行ログ機能を備えており、単純なペネトレーションテストだけではなく、Red Teaming を目的としたセキュリティテストも可能です。
さらに Covenant は他のC2フレームワークと比較してコードの見通しが良く拡張性が高いです。そのため自力開発も辞さず、状況にあったペンテスト手法を選択したい、ツールの基本能力に縛られたくないプロのペネトレーションテスターに最適です。

f:id:lac_devblog:20210726162347p:plain
IME の変換履歴を表示する Task を作成しエージェントに実行させた様子

ただフレームワークとしてはまだ成長途中の段階ではあるので、細かいバグや不足点、惜しいところがいくつかあるのも事実であり、Covenantフォロワーとしては利用者を増やしてどんどんクオリティを高めていきたいと思っています。

この記事では Covenant の布教を目的として、誰でも簡単に Covenant を始めるためのチュートリアルを記載しています。
また、チュートリアルの前に Red Teaming を取り巻くC2フレームワークの現状と、なぜ Covenant なのか?についても説明していきます。

※検証したCovenantのバージョンは 0.6 です。
注意:本投稿で記述した手法を用いてトラブルなどが発生した場合、当社は一切の責任を負いかねます。本情報の悪用はしないでください。

C2フレームワークについて

PowerShell Empire などの Post-Exploitation ツールは Red Teaming の第一線から退き、今やC2フレームワークと呼ばれる統合的なアーキテクチャが台頭してきました。
(中でも OSS ではない商用C2フレームワークCobalt Strike が有名です)
C2フレームワーク、特にOSS C2フレームワークの定義は明確に定まっていませんが、INSIDER THERAT SECURITY BLOGの言葉を借りるなら、以下の特徴を持っています。

・セットアップと使用が非常に簡単(数分)
・スケーラブルなフレームワーク
クロスプラットフォームの対応
・オペレータのためのWebベースのユーザフレンドリーなインターフェース
・"Plug And Play architecture" - 新しいエージェント、データソース、
インテグレーションを素早く簡単に追加できる
Github上でオープンソースになっている
・軽量で信頼性が高いデータベースソフトウェアの活用
・認証によるマルチユーザ/プロファイル
・得られたデータ/クレデンシャルを簡単に抽出するためのUI
・活気のあるオープンなコミュニティ

この中では、"Plug And Play architecture" がキモとなると考えます。

日本のペネトレーションテストシーンでは未だに有効な手法である Living off the land (LOTL)
一方海外の企業では EDR 製品の導入が進み、Microsoft や 他 EDR 製品の根気強い Powershell 検知技術向上への取り組みや、certutil や bitsadmin などWindows標準ツールへの検知手法が進んだことにより、Red Team は単純な LOTL からの方針の転換を迫られました。

最近は Bring your own land (BYOL) と呼ばれる、Red Team が自分たちで全て用意するという一見前時代的な傾向に回帰しています。

この用語は2018年6月にFireEyeが提唱しました。
https://www.fireeye.com/blog/threat-research/2018/06/bring-your-own-land-novel-red-teaming-technique.html

BYOL を支えるのは検知回避手法です。AMSIバイパスやLOLBins(LOLBAS)を使ったものがありますが、ここでは割愛します。

この背景から、C2エージェントは柔軟に検知回避手法を取り入れながら必要なモジュールを必要なタイミングでC2サーバから取得し利用できることが
求められます。
そのために必要なのが、"Plug And Play architecture" なのです。

なぜCovenant?

C2フレームワークには Covenant の他にも様々あります。
著名なOSS C2フレームワーク(WebUI搭載)は以下です。

・Covenant
・FactionC2
・Merlin
・Apfell

より詳細を知りたい方は C2Matrix の一覧から調べてください。

今回の目的にうまく合致しなかったので除外しましたが、独自路線で大きく成功しているのはSILENTTRINITYです。
SILENTTRINITYは BYOI(Bring Your Own Interpreter)という設計思想の Powershell EmpireライクなC2フレームワークで、Covenantに勝るとも劣らない機能を持っています。
BYOLの欠点である柔軟性が低い.NETモジュール指向を PowerShell Empire 時代のスクリプト指向に回帰しようとしており、今後の動向に注目です。

基本的な機能は、率直にいうとどれも同じです。
その中で以下の点を意識して比較してみました。

・コードメンテナンスの頻度
- Githubの issue, PR, Contributor, コミットログから判断。
・コミュニティの活発さと話題性
- Slack, Discord, Googleの検索結果から判断。
・メンテナ
- 懐が深い著名な組織所属だとなおよい。
・拡張性
- 機能が疎結合化しているか。C2チャンネルを新規開発するために別モジュールに手を入れる必要があるソフトウェアは問題あり。

机上調査の結果、Covenant が用途に最もマッチすると判断しました。

Covenant は OSS C2 フレームワークとしては世界でも話題になっており、C2チャンネルを開発するために、モジュラブルな Listener 機能のほか、C2チャンネル実装の抽象性をより高めた BridgeListener(+C2Bridge) の機能を有しています。
f:id:lac_devblog:20210726162959p:plain

Covenant: Developing Custom C2 Communication Protocols
https://posts.specterops.io/covenant-developing-custom-c2-communication-protocols-895587e7f325

また、全てのモジュールが .NET で作成されており、ジョブ実行時に必要なコンポーネントをC2サーバから取得しメモリに読み込むことができるので、BYOL の要件も満たせそうです。

C3との連携

特筆すべき点は、C2チャンネル特化型であるF-Secure社のOSS C2フレームワーク「C3」が、Covenant に対応していることです。

f:id:lac_devblog:20210726163129p:plain

C3 Making Donuts Explode – Updates to the C3 Framework
https://labs.f-secure.com/blog/making-donuts-explode-updates-to-the-c3-framework/

C3は設計思想としてRAT機能は存在せず、C2チャンネルの実装のみを行っています。
f:id:lac_devblog:20210726163206j:plain

そしてRAT機能を使いたい場合は Cobalt Strike の ExternalC2 と連携する形となっていました。しかし2020年3月、C3 は新たに Covenant と連携できる機能を搭載しました。そのため Covenant は C3 と連携することで、使える C2 チャンネルを一気に増やすことができます。

C3で現在実装されているC2チャンネルは以下です。

クラウドサービス
  - Slack
  - OneDrive
  - Outlook
  - Asana
  - Dropbox
  - Github
P2P (内部の横展開用途)
  - MSSQL
  - UncShareFile

実際に私も C3 連携を試してみたところ、Slack をC2チャンネルとしてエージェントとやりとりさせることができました(少し不安定ですが)。

f:id:lac_devblog:20210726163345p:plain

C3連携についてはまた別に記事を掲載する予定です。

ーーー

Covenantのセットアップ

前置きはこのくらいにして、Covenant を触っていきましょう。

Covenant は C# + .NET Core 3.1 で作成されており、Windows 環境ではもちろん、Linux 上でもネイティブ起動することが可能です。

検証に必要なもの

2台のVMを用意することを想定しています。一応、1台のWindowsPCで検証することも可能です(面白味は少ないですが)。

・Windows10 VM (標的端末として)
Linux VM (C2サーバとして)
- Kali Linux 2020.2 で検証しました。Ubuntuでも動くと思います。

ダウンロードとインストール

あらかじめC2サーバ用VM .NET Core 3.1 をインストールしてください。
以下のコマンドで、Covenant をセットアップできます。

$ ~ > git clone --recurse-submodules https://github.com/cobbr/Covenant
$ ~ > cd Covenant/Covenant
$ ~/Covenant/Covenant > dotnet build
$ ~/Covenant/Covenant > dotnet run

起動後、https://localhost:7443/ でWebUIにアクセスできます。0.0.0.0 でリッスンしているため、別ホストからのアクセスも可能です。
はじめに管理ユーザの登録が必要です。好きなusername, passwordを入力してください。
f:id:lac_devblog:20210726163944p:plain
作成後、ダッシュボード画面に遷移します。
f:id:lac_devblog:20210726164031p:plain
デフォルトでは Classic Theme というホワイト基調の見やすいUIとなっています。なお Usersページからブラック基調のHeathen Mode も選択できます。

f:id:lac_devblog:20210726164057p:plain
私個人はClassic Themeのほうが好きなので、Classic Themeで解説していきます。

Listener の作成

デフォルトで用意されている Listener は2つです。
1つ目はメインとなる HTTPListener で、Grunt は HTTP(s) 通信でC2サーバとやりとりします。
もう1つは BridgeListener で、主に別の開発者が作成したC2チャンネルの
橋渡し役となります。
BridgeListener は今後 C3 との連携に使うので今はおいといて、
まずは HTTPListener を使いましょう。

Listeners のページから、Create を選択して作成画面に移行します。
f:id:lac_devblog:20210726164216p:plain

BindAddress/BindPort
HTTPListener をリッスンするときの情報です。
どこからでもアクセスできるようにしたいなら BindAddress は 0.0.0.0 にしましょう。BindPort は HTTP の場合は80に、UseSSL を True にする場合は 443にしておきましょう。

ConnectAddresses
わかりにくいのですが、のちに Launcher を作成するとき、 Launcher を実行する標的PC から見てどのホスト/IPアドレスにアクセスするか指定する場所です。今回 Covenant は 192.168.11.254 のホストで動いているので、192.168.11.254にしました。
+Add を選択することで、複数ホストを指定できます。
本番環境なら、Covenant にアクセスするためのドメイン名を直接指定するのではなく、クラウド上でリバースProxy を作成し、クラウドのホスト名を ConnectAddresses に指定して Covenant サーバを秘匿化するとよいでしょう。

UseSSL
C2通信を https にするかどうかです。Trueの場合は証明書を選択する必要があります。本番用途ならTrue 必須ですが、今回は検証なので False でいいです。

HttpProfile
C2通信をする際に、HTTPペイロードのどこにC2コマンドを埋め込むか定義されたプロファイルを設定する箇所です。CustomHttpProfile で問題ありません。

Launcherの作成

いよいよ Launcher です。ここで作成した Launcher を標的PCに実行させることで、標的PC上で Listener に通信が発生し、Stager を受信、実行し、最終的に Grunt というC2エージェントが立ち上がります。
Launchers 画面に移動します。
f:id:lac_devblog:20210726164403p:plain
標的で実行するために様々な Launcher が用意されており、状況に合わせて利用します。今回は簡略のために、Binary を選択して実行ファイルの形で生成しましょう。
Binary は PowerShell と違って explorer から実行するとコマンドプロンプトの画面が表示されるため動いていることがすぐわかりますし、例外エラー発生時は標準出力で表示されるので、検証用途にちょうどいいです。
f:id:lac_devblog:20210726164445p:plain

Listener
先ほど作成した Listener の名前を選択します。

ImplantTemplate
C2エージェントの構成タイプを選択します。今回はHTTPによるC2通信なので、GruntHTTP です。

DotNetVersion
3.5か4.0を選択します。Win7 .NET Framework 3.5 が、Win10は 4.0 がデフォルトでインストールされています。標的PCのOSバージョンで選びましょう。

KillDate
設定した日付以降は動作を停止します。デフォルトで1か月後になっています。

その他はよしなに設定してください。

設定後、Download を押すとダウンロードが開始されます。
なお、Windows Defenderはデフォルト構成のLauncherバイナリを検知します。検知回避手法はここでは割愛します。一旦 Windows Defenderを無効にするか、実行ファイルを例外に設定してください。

Launcher の実行と Grunt の登録

ダウンロードしたLauncher(デフォルト名: GruntHTTP.exe) を標的PCに移動し、実行します。
実行すると、Launcher は設定した Listener のポートに接続し、Stage0, Stage1, Stage2のペイロードを順次実行し、最終的に Grunt ペイロードを取得し読み込みます。HTTP通信はシステムのプロキシ設定に従って行われるので、プロキシ環境でも安心です。
Launcher プロセスにて Grunt の読み込みが完了すると、 WebUI 上で Grunt が登録されたという通知がされます。

f:id:lac_devblog:20210726164618p:plain
Grunt ページで登録された Grunt を選択すると、Info 画面が表示されます。
f:id:lac_devblog:20210726164714p:plain
ここにはGruntを実行しているホスト・ユーザの概要が表示されています。
Integrity の情報は少し重要で、Medium は一般ユーザで、管理者ユーザは High です。SYSTEM 権限で動作している場合はSystemと表示されます。
このページで Grunt の名前を変更することができます。画面下部の「Name」欄に好きな名前を入力し、Edit ボタンを押します。

GruntでTaskを実行する

Taskについて
Gruntページでは、Task という単位で Grunt に対して処理をさせることができます。
f:id:lac_devblog:20210726164818p:plain
Taskそれぞれは Covenant のモジュールで好きなように実装することができ、デフォルトでたくさんの Task が定義・実装されています。
Task の定義は WebUI の Tasks ページに存在します。
f:id:lac_devblog:20210726164844p:plain
Task のオプションや、実行時に何が行われるか詳細を知りたいときは、
Tasksのページが参考になるでしょう。
Task の実態は C# であり、その多くは Covenant ファイル群の Data/ReferenceSourceLibraries にある dll をコールするように構成されています。
例として、一番単純な WhoAmI タスクの Code を記載します。

using System;
using System.Security.Principal;

public static class Task
{
   public static string Execute()
   {
       try
       {
           return WindowsIdentity.GetCurrent().Name;
       }
       catch (Exception e) { return e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace; }
   }
}

WhoAmI タスクはその名の通り、Grunt の実行ユーザを出力します。これは .NET の標準機能のみを使って実現しています。
対して Recon で利用する GetDomainUser タスクは、Covenant のメンテナであるcobbr氏が作成した、C#版PowerSploitともいえる SharpSploit の API を利用しています。
以下に GetDomainUser タスクのページにある Code を記載します。

using System;
using System.Text;
using System.Linq;
using System.Collections.Generic;

using SharpSploit.Enumeration;

public static class Task
{
   public static string Execute(string Identities = "")
   {
       try
       {
           List<Domain.DomainObject> domainUsers = new List<Domain.DomainObject>();
           if (Identities.Trim() != "")
           {
               List<string> identityList = Identities.Split(',').ToList();
               domainUsers = new Domain.DomainSearcher().GetDomainUsers(identityList);
           }
           else
           {
               domainUsers = new Domain.DomainSearcher().GetDomainUsers();
           }
           StringBuilder results = new StringBuilder();
           foreach (Domain.DomainObject domainUser in domainUsers)
           {
               results.Append(domainUser.ToString());
               results.AppendLine("------");
           }
           return results.ToString();
       }
       catch (Exception e) { return e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace; }
   }
}

このように Covenant は様々な外部dllを利用して、柔軟に Task を作成可能です。また、作成した Task は yaml 形式でインポート・エクスポート可能なので、Covenant ユーザ間でナイスな Task を共有したりもできます。

Taskを実行してみる

Task の確認方法がわかったところで、Task を実行してみましょう。
Grunt のページに戻り、Task タブから WhoAmI タスクを選択し、実行します。
そうすると自動的に Interact タブに移動し、しばらく待つとタスクの実行結果が返されます。

f:id:lac_devblog:20210726165135p:plain

タスクは以下のステータス遷移がされます。

・Uninitialized
 - Taskの開始がキックされた
・Tasked
 - GruntにTaskが登録された
・Progressed
 - GruntでTaskが実行されている
・Completed
 - GruntでTaskが完了した

Interact タブでは、Task で選択することがワンライナーで実行可能です。
例えば、cmd.exe を実行可能な ShellCmd タスクで、引数に"whoami"を設定するとき、Interact では

ShellCmd /shellcommand:"whoami"

と入力し、Enter キーを押すことで Task が実行可能です。また、エイリアスを使った簡単な記述も可能です。

shellcmd whoami

GruntsのInteractタブ

Grunt 詳細画面の他に、Grunts 一覧上で Grunt の左にあるアイコンをクリックすることで、Interact の画面をGrunt ごとにタブ形式で表示することが可能です。
f:id:lac_devblog:20210726165343p:plain
複数 Grunt の状態を簡単に切り替えたい場合はこれを利用するとよいでしょう。

Tasking

Grunt 内の Tasking タブは、その Grunt で実行した Task の詳細情報を見ることができます。
f:id:lac_devblog:20210726165647p:plain
RedTeam オペレーションには証跡が必須なので、これは頼もしいです。
全ての Task には Name が割り振ってあり、Name をクリックすることで、そのタスクの実行ユーザや実行結果まで全てのログを確認することができます。
下記画像は、GetDomainUser タスクの結果です。
f:id:lac_devblog:20210726165711p:plain
データベースとして記録されるので、誤って結果を削除することがないのが助かります。
ちなみに WebUI の Tasking ページは、全Gruntのタスク実行履歴を見ることができます。

他のWebUI機能

Graph
おまけみたいなもので、Listener に接続している Grunt を表示できます。

Data
認証情報、インジケータ、ダウンロードファイル、スクリーンショットの情報を表示できます。
認証情報については Grunt の結果からいい感じに表示されるわけではなく、自分で手入力で作成していく必要があります。備忘録としてメモを取れって感じでしょうか。
ダウンロードファイルについては、Grunt の Download タスクが成功するとファイル情報がこのタブに表示され、ダウンロードできるようになります。

f:id:lac_devblog:20210726165751p:plain

ScreenShots タブは、Grunt の ScreentShot タスクの実行が成功するとこのページにも表示されるようになります。
f:id:lac_devblog:20210726165833p:plain

Advanced: SMBを使ってP2P接続

侵害先で横展開を行い侵害ホストを拡大していくとき、侵害先ホストがインターネットに接続できなかったり、インターネットへの通信が BlueTeam によって検知される可能性が高い場合があります。そういったときはホスト間での P2P を使うことで対処します。
Implant Template に GruntSMB を用いることで、Listener に接続されたノードと別のホストを P2P 接続できます。
少しわかりにくいですが、Graphをどうぞ。
f:id:lac_devblog:20210728091413p:plain
赤が Listener、青が GruntHTTP、緑が GruntSMB です。
緑アイコンが1つの青アイコンに矢印が向いていることがおわかりでしょうか。これは緑の GruntSMB が、青の GruntHTTP を中継して間接的に Listener とやりとりしていることを示しています。
仕組みを説明すると、横展開時 GruntSMB の Launcher を配置し実行する
ことで、そのホストで予め Launcher 画面で指定した名前の NamedPipe が
立ち上がります。
Listener に接続している Grunt は、WebUI の Connect タスクを用いて
当該マシンの NamedPipe に接続を行うことで、図のようにチェーンが
繋がるようになります。
やり方は以下のページを参照してください。
https://posts.specterops.io/designing-peer-to-peer-command-and-control-ad2c61740456

安定性について

Grunt
1週間ほど連続で試しても、Grunt のビーコン、および Task の実行には大きな問題はありませんでした。

Task
大きな問題はありませんが、Task の内容によっては時々 Task 初期化時に
エラーが発生します。不慮の事故で Grunt に Task を送信できなかったとき、Task ステータスは Uninitialized で止まります。
少し待ってもステータスが Uninitialized のままなら Task は実行されていないので、改めて Task を実行してください。

まとめ

Covenant について書きました。日本語で初めての Covenant の記事です!
Cobalt Strike などの商用 C2 フレームワークにはまだ及ばないところもありますが、OSS の C2 フレームワークとしては十分なレベルになっています。
また UI と拡張性については Cobalt Strike よりも一歩先を行っていると思います。

次世代のOSS C2フレームワークは Covenant が覇権をとると信じています!
ペネトレーションテスターのみなさんも Covenant を使い、コントリビュートしていきませんか?

リファレンス

・Covenant
https://github.com/cobbr/Covenant
・Covenant: Developing Custom C2 Communication Protocols
https://cobbr.io/Covenant-Developing-Custom-C2-Protocols.html
・C3
https://labs.f-secure.com/tools/c3/
・NEXT-GEN OPEN SOURCE C2 FRAMEWORKS IN A POST PSEMPIRE WORLD: COVENANT
https://blog.stealthbits.com/next-gen-open-source-c2-frameworks/