
ドラフト: ディスカッション のリクエスト
この文書は、OST C&C 仕様書バージョン 1 の概要を提供します。 また、メッセージとそれらのメッセージ内のフィールドの詳細な説明を提供することを目的としています。
この仕様書の背後にある動機は、逐語的に実装できる、または単にプロジェクト開発者へのインスピレーションとして役立つ C&C メッセージングプロトコル(タスキング、構造化出力、ピアツーピアルーティングを含む)を提供することです。 この文書は C&C が何であるかを説明することを意図したものではありません。 読者は C&C が何であり、何に使用されるかを理解していることを前提としています。
キーワード "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"MAY"、"OPTIONAL" は、[RFC2119] に記載されているとおりに解釈されるものとします。
この仕様書は以下の前提を置きます。
以下は、この文書全体で使用される用語のリストです。
インプラントメタデータ: インプラントがチームサーバーに自身について報告する情報。
タスクリクエスト: インプラントに実行させるために与えられるタスク。
タスクレスポンス: 特定のタスクのステータスと出力(あれば)。
セッションキー: インプラントがメッセージを暗号化するために使用する一意の暗号化キー。
各タスクリクエストおよびレスポンスメッセージは、以下の 16 バイトのヘッダーを持たなければなりません(MUST)。```text | Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | -------------------------------------------------------------| | 0 | Type | Code | Flags | Label | | -------------------------------------------------------------| | 1 | Identifier | Length | | -------------------------------------------------------------|
- **Type**: 1バイト整数。このタスクの「タイプ」です。[[Task Types and Codes](https://github.com/rasta-mouse/ost-c2-spec?tab=readme-ov-file#task-types-and-codes)] を参照してください。
- **Code**: 1バイト整数。指定されたTypeに対する「サブコード」です。[[Task Types and Codes](https://github.com/rasta-mouse/ost-c2-spec?tab=readme-ov-file#task-types-and-codes)] を参照してください。
- **Flags**: 2バイト整数。メッセージの状態を表すビット単位のフラグのセットです。[[Task Flags](https://github.com/rasta-mouse/ost-c2-spec?tab=readme-ov-file#task-flags)] を参照してください。
- **Label**: 4バイト整数。同じタスクに関連する複数のメッセージを関連付けるための一意のラベルです。
- **Identifier**: 4バイト整数。断片化されたメッセージを正しい順序で構築するために使用される連続識別子です。
- **Length**: 4バイト整数。タスクデータの全長です。
## タスクタイプとコード```text
|------------------|--------------------------|
| Type | Code |
|------------------|--------------------------|
| 0 - NOP | 0 |
|------------------|--------------------------|
| 1 - Exit | 0 |
|------------------|--------------------------|
| 2 - Set | 0 - Sleep/Jitter |
| | 1 - SpawnTo |
| | 2 - BlockDLLs |
| | 3 - PPID |
|------------------|--------------------------|
| 3 - File | 0 - Copy |
| | 1 - Move |
| | 2 - Delete |
| | 3 - Upload |
| | 4 - Download |
|------------------|--------------------------|
| 4 - Directory | 0 - Print |
| | 1 - Change |
| | 2 - Create |
| | 3 - Copy |
| | 4 - Move |
| | 5 - List |
| | 6 - Delete |
|------------------|--------------------------|
| 5 - WhoAmI | 0 |
|------------------|--------------------------|
| 6 - Process | 0 - List |
| | 1 - Kill |
| | 2 - Inject Spawn |
| | 3 - Inject Explicit |
|------------------|--------------------------|
| 7 - Registry | 0 - Query |
| | 1 - Add |
| | 2 - Delete |
|------------------|--------------------------|
| 8 - RPortFwd | 0 - Start |
| | 1 - Data |
|------------------|--------------------------|
| 9 - Environment | 0 - Get |
| | 1 - Set |
|------------------|--------------------------|
| 10 - SOCKS | 0 - Connect |
| | 1 - Data |
| | 2 - Close |
|------------------|--------------------------|
| 11 - Tokens | 0 - List |
| | 1 - Make |
| | 2 - Steal |
| | 3 - Use |
| | 4 - Revert |
| | 5 - Delete |
| | 6 - Purge |
|------------------|--------------------------|
| 12 - Run | 0 |
|------------------|--------------------------|
| 13 - ItemStore | 0 - List |
| | 1 - Add |
| | 2 - Delete |
| | 3 - Purge |
|------------------|--------------------------|
| 14 - LocalExec | 0 - .NET |
| | 1 - BOF |
| | 2 - Managed PowerShell |
| | 3 - Unmanaged PowerShell |
|------------------|--------------------------|
| 15 - PrintScreen | 0 |
|------------------|--------------------------|
| 16 - RemoteExec | 0 - WinRM |
| | 1 - WMI |
| | 2 - PsExec |
| | 3 - SSH |
|------------------|--------------------------|
| 17 - Link | 1 - Link SMB |
| | 2 - Link TCP |
|------------------|--------------------------|
| 18 - Unlink | 0 |
|------------------|--------------------------|
| 19 - P2P | 0 - Acknowledge |
| | 1 - PassThru |
|------------------|--------------------------|
| 20 - Jobs | 0 - List |
| | 1 - Kill |
|---------------------------------------------|
一部のフラグは相互に排他的であり、一緒に設定してはなりません(MUST NOT)。 フラグが設定されていない場合、タスクは正常に完了したとみなされるべきであり(SHOULD)、関連する出力(ある場合)は断片化されません(NOT fragmented)。```text
| Value | Description |
|---|---|
| 0 | No flags |
| 1 | Task Error |
| 2 | Task Running (as job) |
| 4 | Message is fragmented, more to follow |
## タスクデータ
タスクデータはヘッダーに追加され、特定のタスクタイプとコードに応じたバイナリ構造で構成されます。 各タスク要求および応答メッセージタイプは、[[メッセージ定義](https://github.com/rasta-mouse/ost-c2-spec?tab=readme-ov-file#message-definitions)]で定義されています。
不要な場合、タスク要求または応答にデータを付与することは必須ではありません。
## 暗号化タスクメッセージ
送信前に、タスクヘッダーとタスクデータが結合され、インプラントのセッションキーでAES暗号化されます。```text
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| ------------------------------------ |
| 0 | Iv |
| 8 | |
| ------------------------------------ |
| 16 | Checksum |
| 24 | |
| 32 | |
| 40 | |
| ------------------------------------ |
| 48 | Data |
| .. | |
| ------------------------------------ |
インプラントは、タスクデータを受信または送信する前に、チームサーバーに自身を登録しなければなりません(MUST)。
インプラントは [IMPLANT-METADATA] メッセージを生成し、チームサーバーの公開RSA鍵で暗号化して、チームサーバーに送信します。
チームサーバーは自身の秘密RSA鍵を使用してインプラントの [IMPLANT-METADATA] を復号し、それを新しいセッション/コールバックとして登録しなければなりません(MUST)。
インプラントは、自分自身または子のための未処理のタスクデータを受信するために、チームサーバーに「チェックイン」しなければなりません(MUST)。
チェックインの方法はC2チャネルに固有であり、この仕様の対象外です。登録済みインプラントは、チェックインのためにIDのみを送信してもかまいません(MAY)。ただし、インプラントがその後セッションキー、スリープ、またはジッター設定を変更した場合は、メタデータも再送信しなければなりません(MUST)。
保留中のタスクがない場合、チームサーバーはデータなしで応答するか、1つ以上の [NOP] メッセージの形式のダミーデータで応答してもかまいません(MAY)。それ以外の場合は、インプラントのセッションキーでAES暗号化されたタスク要求の集合で応答しなければなりません(MUST)。
子インプラントは、新しい親との接続が確立されたら、自身のメタデータをP2Pチャネル(例:名前付きパイプまたはTCPソケット)に書き込まなければなりません(MUST)。
親はこのメタデータを読み取り、[LINK-REP] メッセージでチームサーバーに送り返さなければなりません(MUST)。
チームサーバーは子のメタデータを復号し、新しいセッション/コールバックとして登録するか、リンク解除&リンクの場合には既存の親子関係を更新しなければなりません(MUST)。
チームサーバーは、子のIDを確認するために、新しい親へ [LINK-ACK] メッセージを送り返さなければなりません(MUST)。親はこのプロセスを関連付けるためにメッセージのLabelを使用すべきです(SHOULD)。
子インプラントへのタスクは、1つ以上の [LINK-PASS-THRU] メッセージにラップされます。これらは親のセッションキーで暗号化されます。受信すると、親はメッセージを復号し、child-id フィールドで示された子インプラントにラップされたデータを転送しなければなりません(MUST)。
ラップされたデータは、タスク自体、または子がチェーンのさらに下のレベルにある場合は別の LINK-PASS-THRU の場合もあります。
すべての Timestamp フィールドは、UNIXエポック(1970年1月1日からの経過秒数)を表す符号付き64ビット整数(Int64)として送信されます。
一部の言語では、省略された値と送信されたゼロ値を区別しません。一貫性のため、実装はOPTIONALフィールドの先頭に 1 または 0 バイト(すなわち TRUE または FALSE)を付けて、値が存在するかどうかを示さなければなりません(MUST)。
バイナリストリームからデータを読み取る際、あるフィールドがどこで終わり別のフィールドがどこで始まるかを常に知ることはできません。この仕様は、これらのフィールドに長さ値をプレフィックスとして付けることを義務付けており、実装がそのフィールドに含まれるバイト数または要素数を把握できるようにします。次のデータ型は長さプレフィックスを付けなければなりません(MUST):
実装は、独自の設計と機能に応じて、この仕様で定義されていないメッセージタイプ、制御コード、およびフラグを含めてもかまいません(MAY)。ただし、将来のリビジョンで割り当てられる可能性を減らすために、未予約プールのより高い方の値を使用することが推奨されます(RECOMMENDED)。実装は、定義されたタイプ、コード、またはフラグをその意図された目的以外に使用してはなりません(MUST NOT)。
実装は、認識できないフィールドまたはフラグを含むメッセージを受信した場合に、適切に処理し、適切なエラーメッセージを返すべきです(SHOULD)。
IMPLANT-METADATA { id [1] UInt32 session-key [2] SEQUENCE of Byte (32) sleep [3] UInt32 OPTIONAL jitter [4] UInt32 OPTIONAL username [5] String OPTIONAL host-id [6] String OPTIONAL hostname [7] String OPTIONAL domain [8] String OPTIONAL ipv4-ips [9] SEQUENCE of IPV4-ADDRESS OPTIONAL ipv6-ips [10] SEQUENCE of IPV6-ADDRESS OPTIONAL process-name [11] String OPTIONAL process-id [12] UInt32 OPTIONAL architecture [13] [Architecture] OPTIONAL platform [14] [Platform] OPTIONAL os-description [15] String OPTIONAL integrity [16] [Integrity] OPTIONAL }
### IPV4-ADDRESS```text
IPV4-ADDRESS {
address [1] SEQUENCE of Byte (4)
}
IPV6-ADDRESS { address [1] SEQUENCE of Byte (16) }
IP アドレスはネットワークバイトオーダーで送信されなければなりません。
### プラットフォーム```text
Platform {
Linux = 0,
MacOS = 1,
Windows = 2
}
TASK-ERROR { error-code [1] UInt32 message [2] String OPTIONAL }
## NOP 定義
### NOP```text
NOP {
padding [1] SEQUENCE of Byte OPTIONAL
}
SET-SLEEP-REQ { interval [1] UInt32 jitter [2] Byte OPTIONAL }
### SET-SPAWNTO-REQ```text
SET-SPAWNTO-REQ {
spawnto [1] String OPTIONAL
}
spawnto フィールドが設定されていない場合、インプラントはデフォルト設定に戻るべきです(SHOULD)。
SET-BLOCKDLLS-REQ { blockdlls [1] Boolean OPTIONAL }
`blockdlls` フィールドが*設定されていない*場合、インプラントは既定の構成に戻るべきである。
### SET-PPID-REQ```text
SET-PPID-REQ {
ppid [1] UInt32 OPTIONAL
}
ppid フィールドが 設定されていない 場合、インプラントはデフォルト設定に戻るべきです(SHOULD)。
FILE-COPY-REQ { source [1] String destination [2] String force [3] Boolean OPTIONAL }
### FILE-MOVE-REQ```text
FILE-MOVE-REQ {
source [1] String
destination [2] String
}
FILE-DELETE-REQ { path [1] String }
### FILE-UPLOAD-REQ```text
FILE-UPLOAD-REQ {
destination [1] String
content [2] SEQUENCE of Byte
}
FILE-DOWNLOAD-REQ { path [1] String }
### FILE-DOWNLOAD-REP```text
FILE-DOWNLOAD-REP {
current-chuck [1] UInt16
total-chunks [2] UInt16
chunk-content [3] SEQUENCE of Byte
}
DIR-PRINT-REP { path [1] String }
### DIR-CHANGE-REQ```text
DIR-CHANGE-REQ {
path [1] String OPTIONAL
}
path フィールドが 設定されていない 場合、インプラントはその作業ディレクトリを「デフォルト」の場所(例:ユーザーのホームディレクトリ)に変更すべきです(SHOULD)。
DIR-CREATE-REQ { path [1] String }
### DIR-CREATE-REP```text
DIR-CREATE-REP {
entry [1] [FileSystemEntry]
}
DIR-COPY-REQ { source [1] String destination [2] String }
### DIR-MOVE-REQ```text
DIR-MOVE-REQ {
source [1] String
destination [2] String
}
DIR-LIST-REQ { path [1] String OPTIONAL access-control [2] Boolean OPTIONAL }
`path` フィールドが *設定されていない* 場合、インプラントは現在の作業ディレクトリを一覧表示すべきです(SHOULD)。
### DIR-LIST-REP```text
DIR-LIST-REP {
entries [1] SEQUENCE of [FileSystemEntry]
}
DIR-DELETE-REQ { path [1] String recurse [2] Boolean OPTIONAL }
### FileSystemEntry```text
FileSystemEntry {
path [1] String
length [2] UInt32 OPTIONAL
attributes [3] [FileAttributes] OPTIONAL
owner [4] String OPTIONAL
created [5] Timestamp OPTIONAL
last-accessed [6] Timestamp OPTIONAL
last-written [7] Timestamp OPTIONAL
access-control [8] SEQUENCE of [FileSecurity] OPTIONAL
}
ビット単位のフラグ。```text FileAttributes { Normal = 1, Archive = 2, Compressed = 4, ReadOnly = 8, Hidden = 16, Directory = 32, System = 64 }
### FileSecurity```text
FileSecurity {
identity [1] String
access-mask [2] Int32
inheritance [3] [Inheritance] OPTIONAL
propagation [4] [Propagation] OPTIONAL
}
ビットフラグ。```text Inheritance { None = 0, ContainerInherit = 1, ObjectInherit = 2, }
### 伝播
ビット単位のフラグ。```text
Propagation {
None = 0,
NoPropagateInherit = 1,
InheritOnly = 2,
}
WHOAMI-REP { primary [1] String impersonation [2] String OPTIONAL }
## プロセス定義
### PROC-LIST-REP```text
PROC-LIST-REP {
processes [1] SEQUENCE of [ProcessEntry]
}
PROC-KILL-REQ { process-id [1] UInt32 force [2] Boolean OPTIONAL }
### PROC-INJ-REQ```text
PROC-INJ-REQ {
shellcode [1] SEQUENCE of Byte
capability [2] SEQUENCE of Byte
process-id [3] UInt32 OPTIONAL
}
ProcessEntry { process-name [1] String process-id [2] UInt32 parent-process-id [3] UInt32 OPTIONAL session-id [4] Byte OPTIONAL owner [5] String OPTIONAL architecture [6] [Architecture] OPTIONAL integrity [7] [Integrity] OPTIONAL }
### アーキテクチャ```text
Architecture {
X86 = 0, // 32-bit Intel
X64 = 1, // 64-bit Intel
Arm = 2, // 32-bit ARM
Arm64 = 3, // 64-bit ARM
Wasm = 4 // WebAssembly
}
Integrity { Untrusted = 0, Low = 1, Medium = 2, // user High = 3, // sudoers System = 4 // root }
## レジストリ定義
### REG-QUERY-REQ```text
REG-QUERY-REQ {
hive [1] [RegistryHive]
key [2] String OPTIONAL
value [3] String OPTIONAL
access-control [4] Boolean OPTIONAL
}
REG-QUERY-REP { values [1] SEQUENCE of [RegistryValue] keys [2] SEQUENCE of [RegistryKey] }
### REG-ADD-REQ```text
REG-ADD-REQ {
hive [1] [RegistryHive]
key [2] String
name [3] String OPTIONAL
kind [4] [RegistryValueKind] OPTIONAL
value [5] SEQUENCE of Byte OPTIONAL
}
REG-DELETE-REQ { hive [1] [RegistryHive] key [2] String }
### RegistryHive```text
RegistryHive {
ClassesRoot = 0,
CurrentUser = 1,
LocalMachine = 2,
Users = 3,
CurrentConfig = 4
}
RegistryKey { name [1] String access-control [2] [RegistrySecurity] OPTIONAL }
### RegistryValue```text
RegistryValue {
name [1] String
type [2] [RegistryValueKind]
data [3] SEQUENCE of Byte
access-control [4] SEQUENCE of [RegistrySecurity] OPTIONAL
}
RegistryValueKind { None = 0, // REG_NONE String = 1, // REG_SZ ExpandString = 2, // REG_EXPAND_SZ Binary = 3, // REG_BINARY DWord = 4, // REG_DWORD MultiString = 5, // REG_MULTI_SZ Qword = 6 // REG_QWORD }
### RegistrySecurity```text
RegistrySecurity {
identity [1] String
access-mask [2] Int32
inheritance [3] [Inheritance] OPTIONAL
propagation [4] [Propagation] OPTIONAL
}
RPORTFWD-START { bind-port [1] UInt16 localhost-only [2] Boolean OPTIONAL forward-host [3] String forward-port [4] UInt16 }
### RPORTFWD-DATA```text
RPORTFWD-DATA {
data [1] SEQUENCE of Byte
}
ENV-GET-REQ { key [1] String }
### ENV-GET-REP```text
ENV-GET-REP {
value [1] String
}
ENV-SET-REQ { key [1] String value [2] String }
## SOCKS 定義
### SOCKS-CONNECT-REQ```text
SOCKS-CONNECT-REQ {
id [1] UInt32
target [2] SEQUENCE of Byte (4)
port [3] UInt16
}
SOCKS-DATA { id [1] UInt32 data [2] SEQUENCE of Byte }
### SOCKS-CLOSE-REQ```text
SOCKS-CLOSE-REQ {
id [1] UInt32
}
## Token Definitions
### TOKEN-LIST-REP
```text
TOKEN-LIST-REP {
tokens [1] SEQUENCE of [TokenEntry]
}```
### TOKEN-CREATE-REQ
```text
TOKEN-CREATE-REQ {
username [1] String
domain [2] String OPTIONAL
password [3] String OPTIONAL
}```
### TOKEN-STEAL-REQ
```text
TOKEN-STEAL-REQ {
process-id [1] UInt32
access-mask [2] UInt32 OPTIONAL
}```
### TOKEN-USE-REQ
```text
TOKEN-USE-REQ {
index [1] Byte
}```
### TOKEN-DELETE-REQ
```text
TOKEN-DELETE-REQ {
index [1] Byte
}```
### TokenEntry
```text
Token {
index [1] Byte
username [2] String
handle [3] String OPTIONAL
process-id [4] UInt32 OPTIONAL
}```
## Implant Store Definitions
### STORE-LIST-REP
```text
STORE-LIST-REP {
items [1] SEQUENCE of [StoreItem]
}```
### STORE-ADD-ITEM Definition
```text
STORE-ADD-ITEM-REQ {
item [1] SEQUENCE of Byte
name [2] String
type [3] [StoreItemType]
}```
### STORE-DELETE-ITEM Definition
```text
STORE-DELETE-ITEM-REQ {
index [1] Byte
}```
### StoreItem
```text
StoreItem {
index [1] Byte
name [2] String
type [3] [StoreItemType]
}```
### StoreItemType
```text
StoreItemType {
Assembly = 0,
BOF = 1,
Script = 2,
Generic = 3
}```
## Local Execution Definitions
### RUN-REQ
```text
RUN-REQ {
program [1] String
arguments [2] String OPTIONAL
token [3] Byte OPTIONAL
}```
### RUN-REP
```text
RUN-REP {
output [1] String
}```
### EXEC-ASM-REQ
Either store-index or assembly MUST be provided.
```text
EXEC-ASM-REQ {
store-index [1] Byte OPTIONAL
assembly [2] SEQUENCE of Byte OPTIONAL
arguments [3] SEQUENCE of String OPTIONAL
bypass-amsi [4] Boolean OPTIONAL
bypass-etw [5] Boolean OPTIONAL
}```
### EXEC-ASM-REP
```text
EXEC-ASM-REP {
output [1] String
}```
### EXEC-BOF-REQ
Either store-index or bof MUST be provided.
```text
EXEC-BOF-REQ {
store-index [1] Byte OPTIONAL
bof [2] SEQUENCE of Byte OPTIONAL
arguments [3] SEQUENCE of Byte OPTIONAL
bypass-amsi [4] Boolean OPTIONAL
bypass-etw [5] Boolean OPTIONAL
}```
### EXEC-BOF-REP
```text
EXEC-BOF-REP {
output [1] String
}```
### EXEC-POSH-REQ
Either store-index or script MUST be provided.
```text
EXEC-POSH-REQ {
cmdlet [1] String
store-index [2] Byte OPTIONAL
script [3] SEQUENCE of Byte OPTIONAL
bypass-amsi [3] Boolean OPTIONAL
bypass-etw [4] Boolean OPTIONAL
}```
### EXEC-POSH-REP
```text
EXEC-POSH-REP {
output [1] String
}```
## Screenshot Definitions
### SCRNSHOT-REP
```text
SCRNSHOT-REP {
data [1] SEQUENCE of Byte
}```
## Remote Execution Definitions
### WINRM-REQ
```text
WINRM-REQ {
target [1] String
program [2] String
arguments [3] String OPTIONAL
}```
### WMI-REQ
```text
WMI-REQ {
target [1] String
program [2] String
arguments [3] String OPTIONAL
}```
### PSEXEC-REQ
```text
PSEXEC-REQ {
target [1] String
service-name [2] String
service-description [3] String OPTIONAL
bin-path [4] String
}```
## Peer-to-Peer Definitions
### LINK-SMB-REQ
```text
LINK-SMB-REQ {
target [1] String
pipename [2] String
}```
### LINK-TCP-REQ
```text
LINK-TCP-REQ {
target [1] String
port [2] UInt32
}```
### LINK-REP
```text
LINK-SMB-REP {
child-metadata [1] SEQUENCE of Byte
}```
### LINK-ACK
```text
LINK-ACK {
child-id [1] UInt32
}```
### LINK-PASS-THRU
```text
LINK-PASS-THRU {
child-id [1] UInt32
message [2] SEQUENCE of Byte
}```
## JOB Definitions
### JOB-LIST-REP
```text
jobs [1] [JobEntry] のシーケンス```
### JOB-KILL-REQ
```text
index [1] UInt32```
### JobEntry
```text
index [1] UInt32
type [2] Byte
code [3] Byte```
| 8 | Message is fragmented, no more to follow |