{"id":188,"date":"2023-10-02T09:00:00","date_gmt":"2023-10-02T09:00:00","guid":{"rendered":"http:\/\/justruss.tech\/index.php\/2023\/10\/02\/hunting-cobalt-strike-beacons-in-network-traffic\/"},"modified":"2026-05-13T12:59:35","modified_gmt":"2026-05-13T12:59:35","slug":"hunting-cobalt-strike-beacons-in-network-traffic","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2023\/10\/02\/hunting-cobalt-strike-beacons-in-network-traffic\/","title":{"rendered":"Hunting Cobalt Strike Beacons in Network Traffic"},"content":{"rendered":"<p>Cobalt Strike is a commercial adversary simulation platform. A licensed copy costs around $3,500 per year per operator. Cracked versions have been in widespread circulation since approximately 2020 and appear in the majority of large-scale<br \/>\nransomware and APT intrusions. Hunting for it effectively requires understanding both the default configuration and how operators modify it.<\/p>\n<h3>How a Cobalt Strike beacon works<\/h3>\n<p>A beacon is a shellcode payload that runs in memory. Once executed, it decodes its configuration from an embedded XOR-encoded block, connects to the team server (the C2 infrastructure), and enters a sleep-check-task loop. The default sleep is 60<br \/>\nseconds with 0% jitter. The C2 protocol is HTTP or HTTPS by default, though DNS and SMB named pipe transports are also supported.<\/p>\n<p>The configuration block can be extracted from a beacon in memory or on disk. The structure is documented and tools like <code>CobaltStrikeParser<\/code> can decode it:<\/p>\n<pre>pip install cobaltstrikeparser\npython3 -m cobaltstrikeparser beacon.bin\n\n# Example output:\nBeaconType        - HTTPS\nPort              - 443\nSleepTime         - 60000 (ms)\nMaxGetSize        - 1048576\nJitter            - 0\nC2Server          - updates.microsoft-cdn.com\nHttpGet_Uri       - \/updates\/check\nUserAgent         - Mozilla\/5.0 (Windows NT 10.0; Win64; x64)...\nWatermark         - 305419896<\/pre>\n<p>The Watermark field is a numeric identifier tied to a specific licensed copy (or cracked build). Known watermarks for common cracked versions are documented in threat intelligence feeds.<\/p>\n<h3>Network indicators \u2014 default profile<\/h3>\n<p>Default HTTP beacon traffic has recognisable characteristics visible in Zeek conn.log and http.log:<\/p>\n<pre># Beaconing pattern: regular intervals, consistent byte counts\n# From conn.log:\nts          id.orig_h      id.resp_h        duration  orig_bytes  resp_bytes\n1696200000  192.168.1.101  203.0.113.50     0.341     285         2048\n1696200060  192.168.1.101  203.0.113.50     0.287     285         2048\n1696200120  192.168.1.101  203.0.113.50     0.301     285         2048\n1696200180  192.168.1.101  203.0.113.50     0.298     285         2048<\/pre>\n<p>Exactly 60-second intervals, almost identical byte counts \u2014 this is what 0% jitter looks like. Real user-generated traffic to the same host would show irregular timing.<\/p>\n<h3>JA3 fingerprinting<\/h3>\n<p>The default Cobalt Strike HTTPS beacon uses a specific TLS configuration that produces a consistent JA3 hash. From ssl.log:<\/p>\n<pre>ts          id.orig_h      id.resp_h      ja3                               ja3s                              server_name\n1696200000  192.168.1.101  203.0.113.50   a0e9f5d64349fb13191bc781f81f42e1  ec74a5c51106f0419184d0dd08fb05bc  updates.microsoft-cdn.com<\/pre>\n<p>The JA3 hash <code>a0e9f5d64349fb13191bc781f81f42e1<\/code> is the most widely documented Cobalt Strike default. It is generated by the Java TLS implementation used in the beacon stager. The JA3S hash <code>ec74a5c51106f0419184d0dd08fb05bc<\/code><br \/>\nidentifies the server-side response fingerprint.<\/p>\n<h3>Malleable C2 profile evasion<\/h3>\n<p>Experienced operators deploy custom Malleable C2 profiles that change every aspect of beacon traffic. A profile impersonating Amazon S3:<\/p>\n<pre>http-get {\n    set uri \"\/s3\/bucket\/update.bin\";\n    client {\n        header \"Host\" \"s3.amazonaws.com\";\n        header \"User-Agent\" \"aws-sdk-go\/1.44.0 (go1.18; linux; amd64)\";\n        metadata {\n            base64url;\n            prepend \"AWSAccessKeyId=\";\n            parameter \"X-Amz-Security-Token\";\n        }\n    }\n    server {\n        header \"Content-Type\" \"application\/octet-stream\";\n        header \"x-amz-id-2\" \"random\";\n        output {\n            base64;\n            print;\n        }\n    }\n}<\/pre>\n<p>Against a profile like this, content-based detection fails. The traffic looks indistinguishable from legitimate AWS SDK requests at the application layer.<\/p>\n<h3>Behavioural detection that survives profile changes<\/h3>\n<p>Regardless of what the traffic looks like, beacon behaviour has consistent properties:<\/p>\n<pre># Zeek: calculate coefficient of variation (CV) of connection intervals\n# Low CV = highly regular timing = beacon candidate\n# Run against conn.log with Python:\n\nimport json, statistics\nfrom collections import defaultdict\n\nconnections = defaultdict(list)\nwith open(\"conn.log\") as f:\n    for line in f:\n        rec = json.loads(line)\n        key = (rec[\"id.orig_h\"], rec[\"id.resp_h\"], rec[\"id.resp_p\"])\n        connections[key].append(float(rec[\"ts\"]))\n\nfor key, timestamps in connections.items():\n    if len(timestamps) &lt; 10:\n        continue\n    timestamps.sort()\n    intervals = [timestamps[i+1]-timestamps[i] for i in range(len(timestamps)-1)]\n    if len(intervals)  0 else 999\n    if cv &lt; 0.1 and 30 &lt; mean &lt; 300:  # very regular, 30s-5min intervals\n        print(f&quot;{key}: mean_interval={mean:.1f}s stdev={stdev:.1f}s cv={cv:.3f} samples={len(intervals)}&quot;)<\/pre>\n<p>A CV under 0.1 (10% standard deviation relative to mean) across 10+ samples in the 30-300 second interval range is a high-confidence beaconing indicator regardless of what the HTTP traffic looks like.<\/p>\n<h3>Hunting named pipe beacons<\/h3>\n<p>SMB beacon uses named pipes for C2 and leaves no network trace to external servers. Detection requires host-based telemetry. Sysmon Event ID 17\/18 (PipeCreated\/PipeConnected):<\/p>\n<pre>EventID: 17\nPipeName: \\MSSE-5d04b01a-0000-0000-0000-000000000000\nImage:    C:\\Windows\\System32\\svchost.exe<\/pre>\n<p>Default Cobalt Strike named pipe format: <code>\\MSSE-[guid]-0000-...<\/code> or <code>\\postex_[hex]<\/code>. These are documented in public threat intel and easy to Sigma-rule against.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cobalt Strike is everywhere in incident reports. Understanding<br \/>\nthe default beacon traffic patterns makes hunting it more approachable than most people expect.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"class_list":["post-188","post","type-post","status-publish","format-standard","hentry","category-threat-hunting"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/188","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=188"}],"version-history":[{"count":2,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/188\/revisions"}],"predecessor-version":[{"id":230,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/188\/revisions\/230"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}