Encryption

Deep dive into NexusC2 encryption protocols, key exchange, and secure communications.

Overview

NexusC2 implements a multi-layer encryption scheme for secure communication between payloads (agents) and the server. The protocol uses industry-standard cryptographic algorithms with automatic key rotation to maintain forward secrecy.

Key Features:

  • Hybrid RSA + AES encryption for initial handshake
  • AES-256-GCM for ongoing communication
  • Automatic key rotation after each exchange
  • Automatic re-keying on desynchronization

Architecture

Initial Handshake

sequenceDiagram
    participant P as Payload
    participant S as Server

    P->>S: RSA+AES Encrypted Registration
(clientID, system info, seed) S->>P: RSA Signed Response
(newGUID, signature) Note over P,S: Both derive secrets from seed:
secret2 = HMAC(seed, initial_secret)
secret1 = HMAC(seed, secret2)

Ongoing Communication

sequenceDiagram
    participant P as Payload
    participant S as Server

    P->>S: GET (Poll for Commands)
    S->>P: XOR + AES Encrypted Response
    P->>S: POST (AES-256-GCM Results)
    S->>P: Acknowledgment
    Note over P,S: KEY ROTATION after each exchange

Initial Handshake

Step 1: Payload Registration

The payload initiates contact using hybrid encryption (RSA-2048 + AES-256-GCM):

  1. Generate AES Key: Random 32-byte key for this message
  2. Encrypt Data: JSON payload encrypted with AES-256-GCM
  3. Encrypt AES Key: The AES key encrypted with server’s RSA public key (OAEP-SHA256)
  4. Send: Both encrypted components sent to server

Registration Payload Structure:

{
  "clientID": "initial-client-id-from-build",
  "seed": "random-seed-for-key-derivation",
  "extIP": "external IP",
  "intIP": "internal IP",
  "username": "current user",
  "hostname": "machine name",
  "process": "process name",
  "pid": 1234,
  "arch": "amd64",
  "os": "windows"
}

Wire Format:

{
  "encrypted_key": "<base64 RSA-encrypted AES key>",
  "encrypted_data": "<base64 AES-GCM encrypted JSON>"
}

Step 2: Server Response

The server validates the registration and responds:

  1. Verify clientID: Check against inits table (from payload generation)
  2. Generate newGUID: New UUID becomes the agent’s permanent identifier
  3. Derive Secrets: Generate encryption keys from payload’s seed
  4. Sign Response: RSA-PKCS1v15 signature with SHA-256

Response Structure:

{
  "status": "success",
  "new_client_id": "550e8400-e29b-41d4-a716-446655440000",
  "signature": "<base64 RSA signature>",
  "seed": "<echoed seed for verification>"
}

Secret Derivation

Both payload and server independently derive identical secrets using HMAC-SHA256:

Input: seed (from payload), initial_secret (from build)

Step 1: h1 = HMAC-SHA256(seed, initial_secret)
        secret2 = hex(h1)

Step 2: h2 = HMAC-SHA256(seed, secret2)
        secret1 = hex(h2)

Result:

  • secret1: Current encryption key (64 hex chars = 256 bits)
  • secret2: Previous key for rotation (64 hex chars = 256 bits)

Ongoing Communication

GET Requests (Polling for Commands)

Payloads poll the server for pending commands:

Request:

GET /poll?id=<clientID>
Headers: User-Agent, custom headers from config

Response Encryption (XOR Layer):

The server wraps responses in XOR encryption for obfuscation:

  1. Derive XOR Key: SHA256(clientID + ":" + secret1)[:32]
  2. XOR Encrypt: Byte-wise cyclic XOR of JSON response
  3. Base64 Encode: Final encoding for transport

Response Structure (after XOR decryption):

{
  "status": "command_ready",
  "data": "<AES-encrypted command>",
  "lr": [],  // link responses (for SMB pivoting)
  "lc": []   // link commands (for SMB pivoting)
}

Command Data (after AES decryption):

{
  "command_type": 1,
  "command": "whoami",
  "command_id": "cmd-uuid",
  "command_db_id": 42,
  "agent_id": "agent-uuid"
}

POST Requests (Sending Results)

Payloads send command results using AES-256-GCM:

Request:

POST /submit?id=<clientID>
Content-Type: application/json
Body: {"data": "<base64 AES-GCM encrypted results>"}

Encryption Process:

  1. key = SHA256(secret1) (32 bytes)
  2. nonce = random(12 bytes)
  3. ciphertext = AES-GCM.Seal(plaintext, nonce)
  4. output = base64(nonce || ciphertext)

Results Structure (plaintext):

{
  "agent_id": "550e8400-e29b-41d4-a716-446655440000",
  "results": [
    {
      "command": "whoami",
      "command_id": "cmd-uuid",
      "command_db_id": 42,
      "output": "DOMAIN\\user",
      "exit_code": 0,
      "error": "",
      "timestamp": "2025-01-15T10:30:00Z"
    }
  ]
}

Key Rotation

Keys are automatically rotated after each successful exchange to maintain forward secrecy.

Rotation Formula

newSecret = HMAC-SHA256(secret2, secret1)
secret2 = secret1
secret1 = newSecret

Rotation Timing

EventWho Rotates
After receiving commands (GET response)Payload
After receiving results (POST request)Server

Both sides rotate independently but synchronously, maintaining key agreement.


Re-keying Mechanism

If keys become desynchronized (e.g., network failure, crash), the system can re-establish encryption:

Automatic Re-key

When decryption fails, the payload automatically triggers a re-key:

  1. Detection: AES-GCM decryption returns error
  2. Flag: Atomic flag prevents concurrent re-key attempts
  3. Handshake: Background goroutine performs fresh handshake
  4. Recovery: New secrets derived, communication resumes

Manual Re-key

The server can force a re-key via special status:

{
  "status": "rekey_required",
  "command_db_id": 42
}

The payload then performs refreshHandshake() to re-establish keys.


Security Summary

LayerAlgorithmKey SizePurpose
Handshake Key ExchangeRSA-OAEP-SHA2562048-bitProtect initial AES key
Handshake DataAES-256-GCM256-bitEncrypt registration data
Handshake SignatureRSA-PKCS1v15-SHA2562048-bitAuthenticate server response
Secret DerivationHMAC-SHA256256-bitDerive symmetric keys
Command EncryptionAES-256-GCM256-bitEncrypt commands/results
Response ObfuscationXOR256-bitLightweight GET response encryption
Key RotationHMAC-SHA256256-bitForward secrecy

Protocol Security Properties

  • Confidentiality: All data encrypted with AES-256-GCM
  • Integrity: GCM mode provides authenticated encryption
  • Authentication: RSA signatures verify server identity
  • Forward Secrecy: Key rotation limits exposure from compromise
  • Replay Protection: Nonces prevent message replay

Configuration

Encryption parameters are set during payload generation:

ParameterDescriptionLocation
PUBLIC_KEYRSA public key (base64)Build environment
SECRETInitial secret for derivationBuild environment
XOR_KEYAdditional obfuscation keyBuild environment

ComponentFile Path
Payload Encryptionserver/docker/payloads/Linux/encryption.go
Payload Secret Managementserver/docker/payloads/Linux/secure_comms.go
Payload Handshakeserver/docker/payloads/Linux/handshake.go
Server Encryptionserver/internal/agent/listeners/encryption.go
Server Handshake Handlerserver/internal/agent/listeners/handler_handshake.go
Server GET Handlerserver/internal/agent/listeners/handler_get.go
Server POST Handlerserver/internal/agent/listeners/handler_active.go
XOR Implementationserver/internal/agent/listeners/xor.go
to navigate to select ESC to close
Powered by Pagefind