What the evidence must prove
“Looks like Cobalt Strike” is not an analytical endpoint. The useful question is narrower: what observable behavior or recovered structure is difficult to explain without Beacon? That shift matters because many Beacon-adjacent indicators are shared with ordinary software, other command-and-control frameworks, and operator-created infrastructure.
I use four evidence families. None is mandatory in every investigation, but confidence rises when they agree:
| Evidence family | Useful observations | Common failure |
|---|---|---|
| Payload and configuration | Recovered Beacon settings, public key material, sleep values, configured hosts, process-injection choices | Treating a packer or generic loader as proof of Beacon |
| Network | Timing, request and response shape, channel behavior, infrastructure history, and a profile-consistent sequence | Matching one URI, header, JA3 value, or certificate |
| Host | Process lineage, injected memory, named pipes, loaded modules, token activity, and execution artifacts | Looking only for a Beacon process name |
| Operational context | What happened before and after the suspected callback | Ignoring the intrusion chain that gives the signal meaning |
Establishing Beacon identity
The shortest path to high confidence is usually configuration recovery. A Beacon configuration can expose the values that produced the observed traffic: callback hosts, ports, sleep and jitter, HTTP verb and URI choices, headers, proxy behavior, and other build-specific settings. Recovering those values lets the analyst compare an artifact to telemetry instead of comparing telemetry to folklore.
Configuration parsers such as 1768.py are useful here, but parser output must be handled as derived evidence. Preserve the original bytes, record the hash, parser version, offsets, and any warnings. If the parser finds a candidate configuration in memory, verify that the values align with the process and network timeline.
A practical confidence ladder
- Low: a mutable or broadly shared network indicator.
- Moderate: a coherent cluster of Beacon-like network and host behavior.
- High: recovered configuration or memory structures that agree with telemetry.
- Confirmed: the recovered artifact, configuration, execution chain, and callback sequence explain one another.
Reading the network evidence
Beacon can communicate over HTTP, HTTPS, DNS, and peer-to-peer channels. For HTTP and HTTPS, the profile may reshape URIs, headers, parameters, metadata placement, and output encoding. The analyst should therefore work from the sequence and its relationship to the endpoint, not from a single packet feature.
Start with time
Plot the intervals between outbound connections. Repeated callbacks with a stable center and bounded variation may be consistent with sleep and jitter. It is not a Beacon-exclusive pattern. Update agents, monitoring software, and browser background activity can look similar. The value is in using timing to define a cluster for deeper comparison.
# Pseudocode for interval analysis
events = outbound_connections(host, destination)
intervals = difference(sort(events.timestamp))
report median(intervals)
report median_absolute_deviation(intervals)
plot intervals over the full incident windowThen compare structure
- Does the same process own the connections across the cluster?
- Do request sizes, response sizes, verbs, and content types form repeatable roles?
- Does the destination infrastructure predate the incident, or was it staged shortly before use?
- Do bursts of network activity line up with interactive actions on the host?
- If configuration was recovered, do its hosts, URIs, headers, and timing explain the capture?
Host and memory evidence
Beacon frequently operates inside another process. That means the most valuable host question is not “where is beacon.exe?” but “which process contains code, memory, handles, or threads that its on-disk image does not explain?” Process ancestry, unsigned executable memory, anomalous thread starts, remote process access, named pipes, and token operations become more useful when read together.
Collection priorities
- Preserve the suspicious process tree and command-line history.
- Collect the owning process’s network connections and DNS history.
- Acquire memory before terminating the process when policy and risk allow.
- Record executable-memory regions, thread start addresses, loaded modules, handles, and named pipes.
- Correlate the findings with authentication, service, task, and persistence artifacts.
A memory signature can accelerate triage, but the region around the match is usually more informative than the match itself. Determine allocation type, protection, mapped image, nearby strings, referencing threads, and whether the bytes decode into a supported Beacon structure.
Payload triage without shortcuts
Encoded or wrapped payloads should be handled as a reversible chain. For each transformation, save the input, the operation, and the output hash. This prevents the analysis from becoming a series of unrepeatable tool clicks and makes it possible for another analyst to reproduce the result.
- Identify the container: script, PE, shellcode, archive, or memory region.
- Extract obvious encodings without executing the sample.
- Check decoded bytes for structure, not just strings.
- Separate packing or loader behavior from payload identity.
- Run Beacon-specific parsers only after preserving the raw artifact.
A single-byte XOR sweep is acceptable as a quick test against a small blob, but it is not a general Cobalt Strike decoder. Binary-safe code also matters; converting bytes through text can destroy the artifact.
from pathlib import Path
blob = Path("candidate.bin").read_bytes()
for key in range(256):
decoded = bytes(value ^ key for value in blob)
if decoded.startswith(b"MZ"):
Path(f"candidate-xor-{key:02x}.bin").write_bytes(decoded)
print(f"PE-like output with key 0x{key:02x}")A repeatable hunting workflow
- Define the seed. State whether it came from EDR, proxy, DNS, memory, a file, or external reporting.
- Build the smallest coherent timeline. Include process creation, network connections, image loads, authentication, and persistence around the seed.
- Recover structure. Extract configuration or memory evidence when available.
- Test competing explanations. Ask what legitimate software or another framework would need to look the same.
- Expand only on supported pivots. Use values recovered from the case, not an unlimited list of public defaults.
- Write confidence and gaps. Separate what was observed, derived, inferred, and not collected.
Writing the assessment
The final product should explain the join between artifacts. A useful conclusion sounds like this: a process with no legitimate network role contained an executable memory region from which a Beacon configuration was recovered; its configured host, URI behavior, and sleep interval matched the process’s outbound traffic during the incident window.
That statement is stronger than a page of generic Cobalt Strike indicators because every clause is tied to the case. The hunt remains portable even when the next operator changes the profile.