
Open Source C&C Specification
草案:请求讨论
本文档概述了 OST C&C 规范第 1 版。其目的是提供对消息及其字段的详细描述。
制定本规范的动机是提供一种 C&C 消息传递协议(包括任务下发、结构化输出和点对点路由),该协议可以被原样实现,也可以仅仅作为项目开发者的灵感来源。本文档不旨在描述什么是 C&C。假定读者理解它是什么以及它的用途。
关键词 "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"MAY" 和 "OPTIONAL" 应按照 [RFC2119] 中的描述进行解释。
本规范做出以下假设:
以下是本文档中使用的术语列表。
植入体元数据:植入体向团队服务器报告的关于自身的信息。
任务请求:交给植入体执行的任务。
任务响应:给定任务的状态和输出(如果有)。
会话密钥:植入体用于加密其消息的唯一加密密钥。
每个任务请求和响应消息必须具有以下 16 字节头。```text | Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | -------------------------------------------------------------| | 0 | Type | Code | Flags | Label | | -------------------------------------------------------------| | 1 | Identifier | Length | | -------------------------------------------------------------|
- **Type**: 1-byte integer. 此任务的 'type'。 参见 [任务类型与代码](https://github.com/rasta-mouse/ost-c2-spec?tab=readme-ov-file#task-types-and-codes).
- **Code**: 1-byte integer. 给定 Type 的 'sub code'。 参见 [任务类型与代码](https://github.com/rasta-mouse/ost-c2-spec?tab=readme-ov-file#task-types-and-codes).
- **Flags**: 2-byte integer. 一组按位标志,用于描述消息的状态。 参见 [任务标志](https://github.com/rasta-mouse/ost-c2-spec?tab=readme-ov-file#task-flags).
- **Label**: 4-byte integer. 用于关联同一任务多条消息的唯一标签。
- **Identifier**: 4-byte integer. 用于按正确顺序构造分片消息的序号标识符。
- **Length**: 4-byte integer. 任务数据的总长度。
## 任务类型与代码```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 |
|---------------------------------------------|
某些标志是互斥的,绝不能同时设置。 如果没有设置任何标志,则任务应被视为已成功完成,相关的输出(如果有)不会被分段。```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 |
| .. | |
| ------------------------------------ |
植入体必须先向团队服务器注册,然后才能接收或发送任何任务数据。
植入体生成一条 [IMPLANT-METADATA] 消息,使用团队服务器的 RSA 公钥对其进行加密,然后发送给团队服务器。
团队服务器使用其 RSA 私钥解密植入体的 [IMPLANT-METADATA],并且必须将其注册为新的会话/回调。
植入体必须与团队服务器进行“签到”,以接收自身或其子植入体的待处理任务数据。
签到方式取决于 C2 信道,不属于本规范的范畴。已注册的植入体可以仅发送其 ID 进行签到。但是,如果植入体后续更改了其会话密钥、睡眠或抖动配置,则还必须重新发送其元数据。
如果没有待处理任务,团队服务器可以返回无数据,或以一条或多条 [NOP] 消息的形式返回虚拟数据。否则,它必须返回一组使用植入体会话密钥进行 AES 加密的任务请求。
子植入体在与新的父植入体建立连接后,必须将其元数据写入 P2P 信道(例如命名管道或 TCP 套接字)。
父植入体必须读取此元数据,并通过 [LINK-REP] 消息将其发送回团队服务器。
团队服务器必须解密子植入体的元数据,并将其注册为新的会话/回调,或者在取消链接与重新链接的情况下更新现有的父子关系。
团队服务器必须向新的父植入体发送 [LINK-ACK] 消息,以确认子植入体的 ID。父植入体应使用消息的 Label 字段来关联此过程。
针对子植入体的任务被封装在一条或多条 [LINK-PASS-THRU] 消息中。这些消息将使用父植入体的会话密钥进行加密。收到后,父植入体必须解密该消息,并将封装的数据转发给 child-id 字段所指示的子植入体。
封装的数据可以是任务本身,也可以是另一条 LINK-PASS-THRU,如果该子植入体在链中更下一层的话。
所有 Timestamp 字段都以有符号 64 位整数(Int64)形式传输,表示 UNIX 纪元(自 1970 年 1 月 1 日以来经过的秒数)。
某些语言不区分省略的值与传输的零值。为保持一致,实现必须在可选字段前添加一个 1 或 0 字节(即 TRUE 或 FALSE),以指示该值是否存在。
在从二进制流读取数据时,并不总能知道一个字段何时结束、另一个字段何时开始。本规范要求在这些字段前添加长度值前缀,以便实现能够知道该字段包含多少字节或多少元素。以下数据类型必须使用长度前缀:
实现可以根据其独特的设计和功能,包含本规范中未定义的消息类型、控制代码和标志。但是,建议使用未保留值池中较高端的值,以减少在将来修订版本中被分配的可能性。实现不得将某个已定义的类型、代码或标志用于其预期用途以外的任何目的。
实现应优雅地处理收到包含无法识别的字段或标志的消息,并返回适当的错误消息。
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 字段,植入体应恢复其默认配置。
SET-BLOCKDLLS-REQ { blockdlls [1] Boolean OPTIONAL }
如果 `blockdlls` 字段 *未* 设置,则该植入体应恢复为其默认配置。
### SET-PPID-REQ```text
SET-PPID-REQ {
ppid [1] UInt32 OPTIONAL
}
如果未设置 ppid 字段,植入体应恢复为其默认配置。
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 字段未设置,植入体应将其工作目录更改为'默认'位置(例如用户的主目录)。
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` 字段 *未* 设置,则植入体应当列出其当前工作目录。
### 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] Sequence of [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 |