Disappearing Into SaaS Traffic: Krait's Malleable Communication Profiles
Encrypting C2 traffic with TLS solves one problem and creates another. A network defender can’t read the payload, but they can still see connection metadata: destination IP, SNI hostname, request frequency, packet sizes, and timing patterns. A process making HTTPS requests to an unknown domain every 60 seconds, with consistent request/response sizes, looks like what it is — a beacon.
The standard answer is domain fronting or CDN tunneling, but both are becoming less reliable as cloud providers crack down on fronting and as defenders get better at fingerprinting CDN abuse. The alternative approach is to make the C2 traffic look like something the network already expects: legitimate SaaS API calls.
Krait’s malleable profile system shapes every aspect of the HTTP transaction to mimic a specific SaaS application’s API traffic. The implant doesn’t just connect to a C2 server — it appears to be polling a Slack workspace, syncing a Teams channel, checking Google Drive for updates, or downloading from OneDrive.
Anatomy of a Profile
A traffic profile defines six components that together shape how C2 communication looks on the wire:
URIs. The poll, registration, task retrieval, and result submission endpoints use paths that match the target SaaS application’s API structure. A Slack profile polls at a path that looks like the RTM API. A Teams profile uses paths that match the Graph API’s channel sync endpoints. These aren’t random strings — they’re modeled on the real API’s URL structure, so a network analyst comparing the traffic against documented API patterns sees plausible paths.
User-Agent. Each profile sets a User-Agent string matching the real application’s desktop client. The Electron version, app version, and OS identifier match a recent release of the actual application. This is one of the first things a SOC analyst checks when investigating suspicious HTTPS traffic — a User-Agent that doesn’t match any known application is an immediate red flag.
Headers. SaaS APIs use distinctive header patterns. Slack sends Authorization: Bearer xoxb-* tokens. Teams sends Authorization: Bearer with a JWT-shaped string. Google Drive uses OAuth2 headers. Each profile injects the appropriate headers with realistic-looking (but non-functional) values. The values don’t need to authenticate against the real service — they just need to survive visual inspection by an analyst or automated header-matching in a proxy.
Content-Type. Most SaaS APIs communicate in JSON. The profile wraps C2 payloads in a JSON envelope that matches the target application’s API response format. The actual C2 data is base64-encoded inside a JSON field, so the HTTP body parses as valid JSON with a structure that looks right for the application.
Request wrapping. The raw C2 payload is encoded and placed inside a JSON object that matches the target API’s request format. A Slack profile wraps the payload in a structure that looks like an RTM message post. A Drive profile wraps it in something that looks like a file content upload. The wrapper is cosmetic — the server strips it and extracts the real payload — but to a proxy or DPI engine parsing the JSON, the body looks like a legitimate API call.
Response wrapping. The server’s response to the implant is wrapped in the same application-specific JSON format. The agent registration response looks like a Slack API acknowledgment. Task data looks like a channel message payload. Again, the wrapping is cosmetic, but it survives inspection by anything that parses the JSON structure.
Matching the Binary to the Traffic
Traffic mimicry is only half the story. If the process generating Slack-like HTTPS traffic is loader.exe with no version information and a compilation timestamp from yesterday, the illusion breaks immediately. A SOC analyst who correlates the suspicious network connection with the originating process sees the mismatch.
Krait’s cover system matches the binary’s identity to the traffic profile. When you build with --profile slack, the build system automatically applies a PE cover that modifies:
- FileDescription, ProductName, CompanyName — set to match the real application’s version info fields
- Original filename — matches the real application’s binary name
- File and product version — matches a recent release
- Compilation timestamp — set to the real application’s build date
- Output filename — the binary is named to match the real application
The result is a binary that looks like a legitimate application in Task Manager, Process Explorer, and EDR telemetry. The process making Slack API calls appears to be the Slack desktop client. The version info matches, the filename matches, and the network traffic matches.
Cover selection is automatic when a profile is specified, but can be overridden with --cover <name> for cases where the operator wants a different process identity than the traffic profile suggests.
Redirector Integration
Traffic profiles compose with Krait’s redirector layer. The redirector (typically nginx) sits between the internet and the C2 server, filtering traffic that doesn’t match the expected profile. Krait ships a config generator that produces nginx location blocks matching the profile’s URIs, User-Agent patterns, and content types.
Traffic that doesn’t match — a security scanner probing the domain, a researcher poking at the infrastructure, or a misconfigured agent using the wrong profile — gets rejected or redirected to a benign site. Only requests matching the full profile signature (correct URI path, correct User-Agent pattern, correct content type) are proxied to the C2 server.
When running multiple profiles on the same server (different agents using different SaaS covers), the redirector routes each profile’s traffic to the correct handler. This allows a single C2 server to support agents that appear as Slack clients, Teams clients, and Drive clients simultaneously — each on its own redirector with its own domain.
What This Defeats (and What It Doesn’t)
Proxy-based inspection. A corporate proxy that logs and categorizes HTTPS traffic by URL pattern, User-Agent, and content type sees API calls to a known SaaS platform. If the C2 domain is categorized appropriately (or fronted through a CDN), the proxy passes the traffic without flagging it.
SOC triage. An analyst investigating outbound connections sees a process that identifies as a known application making API calls that look like legitimate usage. The visual check passes. Deeper investigation (comparing against real API traffic, checking certificate chains, correlating with SaaS license inventory) would reveal the discrepancy, but triage-level inspection doesn’t catch it.
Automated traffic classification. Network security tools that classify traffic by header patterns, URI structure, and payload format categorize the C2 connection as legitimate SaaS usage. The traffic volume and timing are the only anomalies — and those depend on how well the operator configures the poll interval and jitter to match realistic usage patterns.
What it doesn’t defeat: A defender who compares the actual SaaS vendor’s IP ranges against the C2 server’s IP address will see a mismatch — unless the traffic goes through a CDN or cloud provider where the SaaS application also hosts services. Domain reputation analysis catches newly registered C2 domains regardless of what the traffic looks like. TLS certificate transparency logs reveal certificates issued for the C2 domain. And a SOC that tracks which SaaS applications are licensed and deployed can flag a “Slack client” on a machine where Slack isn’t installed.
Traffic mimicry buys time during initial access and extends dwell time by slowing triage. It’s not a silver bullet against a thorough investigation — it’s a layer that makes the initial detection signal weaker and the investigation path longer. Combined with legitimate-looking infrastructure (aged domains, cloud-hosted redirectors, valid TLS certificates), it significantly raises the bar for network-based detection.
The Profile Tradeoff
Malleable profiles add bytes. The JSON wrapping, base64 encoding, and extra headers increase the on-wire size of every poll and result submission. Base64 alone inflates the payload by 33%. The JSON wrapper adds another few hundred bytes per request. For small BOF results, this overhead is negligible. For large file transfers (nanodump, downloads), it’s measurable but still within normal API response sizes.
The bigger cost is maintenance. SaaS APIs evolve — Slack changes its API paths, Teams updates its authentication headers, Chrome updates its User-Agent format. A profile that perfectly mimics today’s API traffic may look slightly wrong in six months. Keeping profiles current requires periodic review against the real APIs.
Krait ships profiles modeled on stable, well-documented API patterns that change infrequently. The profile format is JSON, so operators can modify or create profiles without touching source code. For engagements where the target environment uses a specific SaaS stack, building a custom profile that matches the exact applications in use provides the strongest cover.