---
title: "Resilient Out-of-Band C2 Pipelines: Framing, AEAD Encryption, and Covert Channels in Go"
description: "Designing fault-tolerant, out-of-band command-and-control pipelines for authorized security testing: forward error correction via XOR parity, P-256 ECDHE session encryption, and 9 covert transport protocols in ghostlink."
date: 2026-11-09
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/ghostlink-out-of-band-c2-transport-architecture/
---

## The fragility of conventional C2 channels

Most command-and-control (C2) frameworks assume a clean, reliable, and persistent network connection. They wrap an interactive shell in a standard HTTP/S beacon or a raw TCP socket and send heartbeats every few seconds.

In hardened enterprise networks, this model breaks down immediately:

1. **TLS Inspection and Interception:** Corporate proxies terminate outbound TLS connections, inspect request bodies, and validate client JA3/JA4 fingerprints. Unfamiliar Go or custom TLS client handshakes are flagged or dropped at the perimeter.
2. **Behavioral Beaconing Detection:** Network detection systems (NDR) calculate periodicity heuristics across outbound HTTP streams. Consistent jitterless polling schedules trigger automated quarantine.
3. **Egress Filtering:** Strictly segmented subnets (such as SCADA networks, core banking enclaves, or air-gapped engineering environments) block outbound TCP ports 80 and 443 entirely. Only internal DNS resolvers or specific loopback relays are permitted egress.

When direct TCP/TLS egress is denied, operators turn to **out-of-band and covert communication channels**—transporting data over DNS, ICMP, DoH, WebSocket, or header steganography.

However, covert channels introduce a new operational failure mode: **fragility**. Subdomains in DNS queries have strict 63-character label limits and 255-character overall payload limits. UDP packets get dropped or arrive out of order. Network gateways rewrite header fields, and aggressive proxy timeouts truncate connections.

To study how to build resilient, fault-tolerant transport pipelines across degraded networks, we engineered [`ghostlink`](https://github.com/jankesec/ghostlink).

## The ghostlink pipeline architecture

The core architectural principle of `ghostlink` is the strict decoupling of **data preparation and session cryptography** from the **physical or network transport**.

The encoding pipeline treats the network as an untrusted, unreliable bit-pipe. Before a single packet is dispatched, the payload passes through five deterministic stages:

<figure class="diagram">
<svg viewBox="0 0 760 220" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="ghostlink 5-stage encoding pipeline and modular transport architecture.">
<defs><marker id="gl-arrow" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-crit" /></marker></defs>
<text x="0" y="16" class="dg-muted">FAULT-TOLERANT OOB PIPELINE · 5-STAGE ENCODING & TRANSPORT</text>
<rect x="0" y="40" width="175" height="120" rx="8" class="dg-box" />
<text x="14" y="66" class="dg-label">1 · Compression</text>
<text x="14" y="88" class="dg-mono dg-compact">zlib Compression</text>
<text x="14" y="108" class="dg-muted">Reduces Total Frames</text>
<line x1="175" y1="100" x2="195" y2="100" class="dg-line" />
<rect x="195" y="40" width="175" height="120" rx="8" class="dg-box-accent" />
<text x="209" y="66" class="dg-label">2 · Forward Error</text>
<text x="209" y="88" class="dg-mono dg-compact">XOR Parity Layout</text>
<text x="209" y="108" class="dg-muted">Packet Loss Recovery</text>
<line x1="370" y1="100" x2="390" y2="100" class="dg-line" />
<rect x="390" y="40" width="175" height="120" rx="8" class="dg-box" />
<text x="404" y="66" class="dg-label">3 · Encryption</text>
<text x="404" y="88" class="dg-mono dg-compact">ECDHE + AES-GCM</text>
<text x="404" y="108" class="dg-muted">Session Authentication</text>
<line x1="565" y1="100" x2="585" y2="100" class="dg-line-crit" marker-end="url(#gl-arrow)" />
<rect x="585" y="40" width="175" height="120" rx="8" class="dg-box-danger" />
<text x="599" y="66" class="dg-label">4 · Transport</text>
<text x="599" y="88" class="dg-mono dg-compact">9 Covert Channels</text>
<text x="599" y="108" class="dg-muted">DNS / ICMP / ISN</text>
</svg>
<figcaption>ghostlink architecture: Payloads undergo compression, XOR parity encoding, and AES-256-GCM encryption before routing across modular transport channels.</figcaption>
</figure>

### 1. Compression (zlib)
Covert channels like DNS or ICMP have severe bandwidth penalties. `ghostlink` compresses the raw byte stream using `zlib`, reducing character count and minimizing the total number of frames required to transmit a given command or exfiltrated file.

### 2. Forward Error Correction (XOR parity layout)
On lossy transports like UDP-based DNS or raw ICMP, packets can be dropped silently. Rather than relying on heavy TCP-style retransmission handshakes (which multiply network signatures), `ghostlink` generates **XOR parity shards**:

$$\text{Parity} = \text{Chunk}_1 \oplus \text{Chunk}_2 \oplus \dots \oplus \text{Chunk}_k$$

If any single data chunk in a group is lost in transit, the receiver mathematically reconstructs the missing byte sequence from the surviving chunks and the parity shard.

### 3. Framing & envelope protocol v2
Each chunk is packaged into a structured frame containing stream identifiers, sequence numbers, and total chunk counts:

```go
type FrameHeader struct {
    Magic       [2]byte // 0x47, 0x4C ('GL')
    Version     uint8   // Protocol v2
    StreamID    uint32  // Unique transaction identifier
    SeqNum      uint16  // Chunk index (0-based)
    TotalChunks uint16  // Total chunks in stream
    PayloadLen  uint16  // Length of chunk payload
    Flags       uint8   // Parity, control, or fin flags
}
```

### 4. Ephemeral session encryption (P-256 ECDHE + AES-256-GCM)
Even if an intermediate network tap captures the raw covert frames, the payload contents are protected. `ghostlink` performs an ephemeral **P-256 elliptic-curve Diffie-Hellman (ECDHE)** exchange (or uses a configured pre-shared key) to derive a 256-bit symmetric session key. Every framed chunk is encrypted using **AES-256-GCM** with a unique 12-byte initialization vector (IV), ensuring both confidentiality and cryptographic integrity.

## The 9 covert transport implementations

`ghostlink` features 9 registered channel drivers designed for controlled laboratory research:

| Transport | Channel Mechanism | Carrier Medium | Environment Requirements |
| :--- | :--- | :--- | :--- |
| **DNS** | Subdomain query framing | UDP / Port 53 | Dedicated authoritative nameserver or lab loopback |
| **DoH** | DNS queries over HTTPS (RFC 8484) | HTTPS / Port 443 | Reaches public DoH resolvers (Cloudflare, Google) |
| **HTTP** | Chunked request/response headers | HTTP / Port 80, 8080 | Standard web proxies, loopback HTTP |
| **WebSocket** | Persistent framed data streaming | WS / WSS | Full-duplex bidirectional stream |
| **ICMP** | Echo request & reply data fields | ICMP Type 8 / Type 0 | Requires raw socket (`CAP_NET_RAW` or root) |
| **TCP ISN** | Initial Sequence Number modulation | Raw TCP 32-bit SYN ISN | Experimental; OS TCP stack dependent |
| **IPv4 ID** | IP identification field modulation | 16-bit IPv4 header ID | Experimental; requires raw packet injection |
| **Timing** | Inter-arrival timing modulation | Packet arrival intervals | Highly sensitive to network jitter |
| **Packet Size** | Length modulation | Modulated frame padding | Sensitive to MTU and router fragmentation |

### Deep-dive: DNS & DoH query framing

In the DNS transport driver, binary ciphertext chunks are encoded into Base32 or Hex strings that strictly conform to RFC 1035 label specifications. A long exfiltration stream is fragmented into queries formatted as:

```text
[seq].[stream-id].[chunk-data].[signature].c2.lab.internal
```

The authoritative DNS server receives the iterative queries from upstream resolvers, strips the domain suffix, extracts the sequence headers, and feeds the stream to the receiver engine.

## Offline pipeline verification & deterministic test harness

Testing C2 transports on live networks without prior validation is reckless. `ghostlink` provides a dedicated offline validation harness that exercises the full cryptographic and framing pipeline without touching a network interface:

```bash
# Verify build, platform, and protocol readiness
ghostlink doctor

# List registered transport drivers and capabilities
ghostlink transports

# Run deterministic end-to-end pipeline validation
ghostlink demo
```

The `demo` command runs an end-to-end transfer entirely in-memory:

```text
GHOSTLINK REFERENCE DEMO
Platform: darwin/arm64 · Protocol: v2 · Mode: offline loopback

[1] P-256 ECDHE Ephemeral Key Exchange:
    Client Public Key:  04a1b2c3d4e5f6... (P-256)
    Server Public Key:  04f6e5d4c3b2a1... (P-256)
    Derived Shared Key: 256-bit AES-GCM session key confirmed

[2] Payload Processing:
    Raw Input:          "CONFIDENTIAL_LAB_RECORD_ARTIFACT_DATA_2026" (44 bytes)
    zlib Compression:   44 bytes -> 38 bytes
    XOR Parity Layout:  3 data shards + 1 parity shard generated

[3] Protocol v2 Framing & AES-256-GCM Encryption:
    Frame 0 (Seq 0/3): 14 bytes ciphertext + 12-byte IV + 16-byte GCM tag
    Frame 1 (Seq 1/3): 14 bytes ciphertext + 12-byte IV + 16-byte GCM tag
    Frame 2 (Seq 2/3): 10 bytes ciphertext + 12-byte IV + 16-byte GCM tag
    Frame 3 (Parity):  14 bytes ciphertext + 12-byte IV + 16-byte GCM tag

[4] Receiver Decryption & Reassembly:
    All frames verified against GCM authentication tags.
    Recovered Bytes:    44 bytes
    SHA-256 Checksum:   d4f8a1... [EXACT MATCH]
```

<figure class="evidence">
<a href="/images/posts/ghostlink-terminal-capture-evidence.webp" target="_blank" rel="noopener" aria-label="Open the full ghostlink terminal capture screenshot"><img src="/images/posts/ghostlink-terminal-capture-evidence.webp" alt="Terminal capture of ghostlink running offline P-256 key agreement, zlib compression, XOR parity encoding, and AES-256-GCM verification" loading="lazy" /></a>
<figcaption><span>ghostlink Offline Test Harness</span> End-to-end cryptographic and framing verification validating P-256 ECDHE, XOR forward error correction, and AES-256-GCM integrity on loopback.</figcaption>
</figure>

## Defensive detection engineering: sniffing the covert channels

Understanding how out-of-band channels operate is essential for engineering effective network detections:

### 1. DNS entropy and volume heuristics
Covert DNS channels produce distinctive statistical patterns. Defenders should monitor for:
- **High Shannon Entropy:** Base32/Hex encoded labels exhibit entropy above 3.8, while legitimate hostnames rarely exceed 3.0.
- **Query Volume Anomaly:** Thousands of unique subdomains queried under a single second-level domain (SLD) within minutes.
- **Resource Record Types:** Repeated `TXT` or `NULL` queries used to return large command responses to endpoints.

### 2. Wireshark protocol dissection (`tools/ghostlink.lua`)
To assist blue teams during packet analysis, `ghostlink` includes a custom Lua dissector for Wireshark. When inspecting network captures, Wireshark automatically identifies `GL` protocol v2 magic bytes, decodes frame headers, and displays stream sequencing:

```lua
-- tools/ghostlink.lua: Wireshark dissector snippet
local p_ghostlink = Proto("ghostlink", "Ghostlink Protocol v2")
local f_magic   = ProtoField.bytes("ghostlink.magic", "Magic")
local f_version = ProtoField.uint8("ghostlink.version", "Version")
local f_stream  = ProtoField.uint32("ghostlink.stream", "Stream ID", base.HEX)
local f_seq     = ProtoField.uint16("ghostlink.seq", "Sequence Number")
local f_total   = ProtoField.uint16("ghostlink.total", "Total Chunks")

function p_ghostlink.dissector(buffer, pinfo, tree)
    if buffer:len() < 14 then return end
    if buffer(0,2):string() ~= "GL" then return end

    pinfo.cols.protocol = "GHOSTLINK"
    local subtree = tree:add(p_ghostlink, buffer(), "Ghostlink Transport Frame")
    subtree:add(f_magic, buffer(0,2))
    subtree:add(f_version, buffer(2,1))
    subtree:add(f_stream, buffer(3,4))
    subtree:add(f_seq, buffer(7,2))
    subtree:add(f_total, buffer(9,2))
end
```

## Evidence and limits

All testing, throughput evaluations, and protocol dissections were performed in isolated laboratory virtual machines over loopback (127.0.0.1) and private virtual networks. Experimental raw-packet channels (TCP ISN, IPv4 ID, timing modulation) require root/administrator privileges and are sensitive to host scheduler jitter and intermediate router re-packetization. No unauthorized networks or live production systems were targeted.

## References

- **jankesec, ghostlink (Out-of-Band Transport Research Framework):** [github.com/jankesec/ghostlink](https://github.com/jankesec/ghostlink)
- **IETF, DNS Queries over HTTPS (RFC 8484):** [datatracker.ietf.org/doc/html/rfc8484](https://datatracker.ietf.org/doc/html/rfc8484)
- **NIST, Recommendation for Block Cipher Modes of Operation: GCM (SP 800-38D):** [csrc.nist.gov/publications/detail/sp/800-38d/final](https://csrc.nist.gov/publications/detail/sp/800-38d/final)
- **MITRE ATT&CK, Protocol Tunneling (T1572):** [attack.mitre.org/techniques/T1572/](https://attack.mitre.org/techniques/T1572/)
- **MITRE ATT&CK, Application Layer Protocol (T1071):** [attack.mitre.org/techniques/T1071/](https://attack.mitre.org/techniques/T1071/)