Malleable C2 Profiles
Operator-supplied profiles let a C2 framework reshape its beacon traffic to mimic legitimate web services, controlling URIs, headers, and how data is encoded and hidden in requests.
A Malleable C2 profile is a text configuration — popularised by Cobalt Strike but copied by other frameworks — that dictates exactly what a beacon's network traffic looks like. Instead of a fixed, fingerprintable protocol, the operator describes the URIs to request, the HTTP headers to send, the User-Agent, and how task data and results are transformed (base64, mask/XOR, prepend/append, netbios) and placed into a request (in a cookie, a header, a URI parameter, or the body). The result is a beacon that can convincingly impersonate jQuery CDN traffic, Office 365 telemetry, or an Amazon API call.
Profiles are powerful for the attacker precisely because they collapse a network signature into operator-chosen data. They are equally powerful for the defender: the profile is the protocol specification, so recovering it (from the beacon config or from public profile repositories) hands you a decoder for the traffic and a high-fidelity hunting signature.
How it works
A profile defines client and server transactions. The relevant fragment for an analyst describes where data hides and how it is encoded:
# Descriptive Cobalt-Strike-style profile excerpt.
http-get {
set uri "/jquery-3.3.1.min.js";
client {
header "Accept" "*/*";
metadata {
base64url; # encode beacon metadata
prepend "__cfduid="; # hide it inside a benign-looking cookie
header "Cookie";
}
}
server {
header "Content-Type" "application/javascript";
output {
base64;
print; # tasking returned in the response body
}
}
}The beacon reads this profile from its embedded configuration block and applies the transforms at runtime. For a reverser, the practical win is extracting that config: tools such as parse routines for the Cobalt Strike beacon config (the obfuscated settings table, historically single-byte XOR 0x2E/0x69) yield the exact URIs, headers, User-Agent, and transform chain.
Detection & analysis
Static analysis:
- Locate and decode the beacon configuration block. Cobalt Strike beacons store settings in an XOR-encoded table; community parsers (e.g.
1768.py, SentinelOne/Didier Stevens tooling) extract the profile fields directly, revealing URIs, headers, sleep/jitter, and the watermark. - The recovered transform chain (base64 → mask → prepend → store-in-cookie) is a complete decoder: apply it in reverse to captured traffic to read metadata and tasking.
- Match the recovered profile against public profile repositories; many actors reuse off-the-shelf profiles, which can attribute the intrusion set.
Dynamic analysis:
- Detonate and capture HTTP(S). Even when content is encoded, the structure is fixed by the profile: the same URI, the same header ordering, a static User-Agent, and regular beacon intervals modulated by jitter. Profile-driven traffic is rigidly self-consistent in a way real browser traffic is not.
- JA3/JA3S and HTTP header-order fingerprinting catch the divergence between a claimed application (e.g. "jQuery CDN") and the beacon's actual TLS/HTTP stack.
Detection rule hint:
Hunt for HTTP(S) sessions matching a known malleable profile's URI + header + User-Agent triad combined with regular, jittered beacon timing — and corroborate with a JA3 fingerprint inconsistent with the impersonated service. Because the profile fixes every observable field, a single high-fidelity profile signature plus periodic beaconing is a reliable detector.