5.2 KiB
5.2 KiB
necropolis Protocol Specification
1. Peer Identity
1.1 Key Derivation
Operator Key: Ed25519 private key (operator.priv)
Operator PeerID: libp2p PeerID derived from operator.priv public key
Implant Key: Ephemeral Ed25519 (generated on first run)
Implant PeerID: libp2p PeerID from implant public key
1.2 Implant Certificate
On first execution, the implant generates:
implant.ed25519- ephemeral keypair- Registration envelope signed by operator key (embedded at build time)
2. Topic Structure (PubSub Fallback)
Topics are used as a fallback when direct streams are unavailable. Topic IDs use short opaque prefixes to reduce wire fingerprinting.
/b/<operator-peerid>/bx # Beacons — implants -> operator (pubsub fallback)
/c/<operator-peerid>/cx # Commands — operator -> implant (pubsub fallback)
/t/<operator-peerid>/tx/<id> # Per-implant task topics (pubsub fallback)
/necropolis/cmd/<id>/<nonce> # DHT dead-drop — operator -> implant command envelopes
Primary communication uses direct libp2p streams (see section 4). DHT dead-drop is the 4th transport rung — implants poll /necropolis/cmd/<id>/<nonce> in sequence every 30s for signed command envelopes published by the operator.
Topic Authorisation
beaconstopic: messages validated against implant's public key- Operator drops messages not signed by known implants on
beacons
3. Envelope Format
All messages use Protocol Buffers. Message types use opaque Z-series identifiers.
package apb;
message Envelope {
int64 ID = 1;
uint32 Type = 2;
bytes Data = 3;
bytes Signature = 4; // Signed by sender's key
bytes SenderKey = 5; // Public key of sender
bytes Token = 6; // Auth token (32 bytes, operator-specific)
}
The Token field carries a 32-byte random value generated per operator at first run. Implants embed it at build time and reject any Envelope with a mismatched token. This prevents unauthorized peers from sending commands even if the operator's Ed25519 public key is known.
// Z1 — Beacon register (async beacon mode)
message Z1 {
string ID = 1;
int64 Interval = 2;
int64 Jitter = 3;
Register Register = 4; // commonpb.Register
int64 NextCheckin = 5;
}
4. Protocol IDs
Direct libp2p streams use short protocol IDs:
| Protocol | ID | Purpose |
|---|---|---|
| Beacon | /bc/1.0.0 |
Persistent beacon stream (implant -> operator) |
| Command | /bc/1.0.0/cmd |
Command delivery (operator -> implant) |
| Shell | /x/sh/1.0.0 |
Interactive shell (Ctrl+] to exit) |
| Port forward | /x/pf/1.0.0 |
TCP port forwarding |
| SOCKS | /x/sk/1.0.0 |
SOCKS proxy tunnel |
WSS connections use standard /dns4/<host>/tcp/443/wss/p2p/<peer-id> multiaddrs — no custom protocol ID.
5. Transport Chain
Implants attempt connections in order:
- Direct libp2p peer (
-peer) - WSS transport (
-wss) - DHT rendezvous discovery (goroutine, 15s)
- DHT dead-drop polling (goroutine, 30s) — signed command envelopes at
/necropolis/cmd/<id>/<nonce>
6. Session Types
6.1 Beacon Mode (Persistent Stream)
- Implant opens persistent
/bc/1.0.0stream to operator (relay-aware,AllowLimitedConn) - Implant sends signed
Z1(beacon register) on the stream every 10-15s - A separate goroutine writes
MsgTypeCoverenvelopes every 5s to prevent relay idle timeout - Operator reads messages in a loop, updates
LastCheckin, dispatches by type - Results (
MsgTypeLs,MsgTypePs, etc.) are sent on the same persistent stream - If the stream dies, implant reconnects via DHT discovery +
openBeaconStream
6.2 Command Delivery (Direct Stream)
- Operator opens a
/bc/1.0.0/cmdstream to implant (relay-aware,AllowLimitedConn) - Operator signs the envelope and writes it length-prefixed
- Implant reads, verifies signature against embedded operator pubkey, dispatches
- Implant processes the command and sends the result on the persistent beacon stream
6.3 Interactive Session Mode (Stream)
- Operator initiates direct libp2p stream to implant
- Bidirectional encrypted stream for shell/portfwd/socks
- Uses libp2p stream multiplexing
- In shell, type
exitor press Ctrl+] to return to the operator prompt
7. Message Types
| Type | ID | Direction | Description |
|---|---|---|---|
| COVER | 127 | Implant -> Op | Cover traffic (silently dropped by operator) |
| REGISTER | 0 | Implant -> Op | Initial beacon/registration |
| TASK | 2 | Op -> Implant | Execute command |
| TASK_RESULT | 3 | Implant -> Op | Command output |
| SHELL | 4 | Bidirectional | Interactive shell |
| DOWNLOAD | 5 | Implant -> Op | File exfiltration |
| UPLOAD | 6 | Op -> Implant | File deployment |
| SOCKS | 7 | Bidirectional | SOCKS proxy tunnel |
| PORTFWD | 8 | Bidirectional | Port forwarding |
| SCREENSHOT | 9 | Implant -> Op | Screen capture |
| LS | 10 | Op -> Implant | List directory |
| CD | 11 | Op -> Implant | Change directory |
| PWD | 12 | Op -> Implant | Print working directory |
| EXECUTE | 13 | Op -> Implant | Run command |
| KILL | 14 | Op -> Implant | Self-terminate |
| PS | 15 | Implant -> Op | Process list result |
| DEADMAN | 16 | Op -> Implant | Dead man switch |
| DISCONNECT | 255 | Bidirectional | Clean close |