Source code for dissect.cobaltstrike.version

"""
This module contains the :class:`BeaconVersion` class and mappings for determining the
Cobalt Strike version of beacon payloads.

.. note::
    Deducing the Cobalt Strike version using :meth:`BeaconVersion.from_pe_export_stamp`
    is more accurate than :meth:`BeaconVersion.from_max_setting_enum`. However, if the
    `pe_export_stamp` is not known, deducing from `max_setting_enum` is still a good
    version estimate.
"""

from __future__ import annotations

import datetime
import re
from typing import Dict, Optional, Tuple, Union

[docs] MAX_ENUM_TO_VERSION: Dict[int, str] = { 20: "Cobalt Strike 3.4 (Jul 29, 2016)", 31: "Cobalt Strike 3.6 (Dec 08, 2016)", 35: "Cobalt Strike 3.7 (Mar 15, 2017)", 36: "Cobalt Strike 3.8 (May 23, 2017)", 37: "Cobalt Strike 3.9 (Sep 26, 2017)", 38: "Cobalt Strike 3.11 (Apr 09, 2018)", 39: "Cobalt Strike 3.11 (May 24, 2018)", 48: "Cobalt Strike 3.12 (Sep 06, 2018)", 49: "Cobalt Strike 3.13 (Jan 02, 2019)", 53: "Cobalt Strike 3.14 (May 04, 2019)", 55: "Cobalt Strike 4.0 (Dec 05, 2019)", 58: "Cobalt Strike 4.1 (Jun 25, 2020)", 59: "Cobalt Strike 4.2 (Nov 06, 2020)", 70: "Cobalt Strike 4.3 (Mar 03, 2021)", 73: "Cobalt Strike 4.5 (Dec 14, 2021)", 74: "Cobalt Strike 4.7 (Aug 17, 2022)", 76: "Cobalt Strike 4.9 (Sep 19, 2023)", 78: "Cobalt Strike 4.10 (Jul 16, 2024)", }
""" Max setting enum to Cobalt Strike version mapping """
[docs] PE_EXPORT_STAMP_TO_VERSION: Dict[int, str] = { 0x579A6849: "Cobalt Strike 3.4 (Jul 29, 2016)", 0x57DCA5FC: "Cobalt Strike 3.5 (Sep 22, 2016)", 0x58487E41: "Cobalt Strike 3.6 (Dec 08, 2016)", 0x58BCA7CA: "Cobalt Strike 3.7 (Mar 15, 2017)", 0x5923564D: "Cobalt Strike 3.8 (May 23, 2017)", 0x59BE846A: "Cobalt Strike 3.9 (Sep 26, 2017)", 0x5A29B49D: "Cobalt Strike 3.10 (Dec 11, 2017)", 0x5AC92EA9: "Cobalt Strike 3.11 (Apr 09, 2018)", 0x5B048335: "Cobalt Strike 3.11 (May 24, 2018)", 0x5B90504C: "Cobalt Strike 3.12 (Sep 06, 2018)", 0x5C26A9C6: "Cobalt Strike 3.13 (Jan 02, 2019)", 0x5CB90DF5: "Cobalt Strike 3.14 (May 02, 2019)", 0x5CCCC811: "Cobalt Strike 3.14 (May 04, 2019)", 0x5D89B903: "Cobalt Strike 4.0 (Dec 05, 2019)", 0x5DE82DE4: "Cobalt Strike 4.0 (Dec 05, 2019)", 0x5DE8F170: "Cobalt Strike 4.0 (Dec 05, 2019)", 0x5DE8F1FA: "Cobalt Strike 4.0 (Dec 05, 2019)", 0x5EF2555B: "Cobalt Strike 4.1 (Jun 25, 2020)", 0x5EF25593: "Cobalt Strike 4.1 (Jun 25, 2020)", 0x5F94C216: "Cobalt Strike 4.2 (Nov 06, 2020)", 0x5FA0B201: "Cobalt Strike 4.2 (Nov 06, 2020)", 0x5FA0B264: "Cobalt Strike 4.2 (Nov 06, 2020)", 0x603E2D9D: "Cobalt Strike 4.3 (Mar 03, 2021)", 0x603E4EF7: "Cobalt Strike 4.3 (Mar 03, 2021)", 0x603F9D1D: "Cobalt Strike 4.3 (Mar 03, 2021)", 0x61093A45: "Cobalt Strike 4.4 (Aug 04, 2021)", 0x61093A9E: "Cobalt Strike 4.4 (Aug 04, 2021)", 0x619D3A1B: "Cobalt Strike 4.5 (Dec 14, 2021)", 0x619D3A40: "Cobalt Strike 4.5 (Dec 14, 2021)", 0x6255EB4E: "Cobalt Strike 4.6 (Apr 12, 2022)", 0x6255EB6E: "Cobalt Strike 4.6 (Apr 12, 2022)", 0x6255EB91: "Cobalt Strike 4.6 (Apr 12, 2022)", 0x62EBF2B8: "Cobalt Strike 4.7 (Aug 17, 2022)", 0x62EBF2DA: "Cobalt Strike 4.7 (Aug 17, 2022)", 0x62EBF2F9: "Cobalt Strike 4.7 (Aug 17, 2022)", 0x630CD51C: "Cobalt Strike 4.7.1 (Sep 16, 2022)", 0x630CD53B: "Cobalt Strike 4.7.1 (Sep 16, 2022)", 0x630CD559: "Cobalt Strike 4.7.1 (Sep 16, 2022)", 0x63EE0552: "Cobalt Strike 4.8 (Feb 28, 2023)", 0x63EE056C: "Cobalt Strike 4.8 (Feb 28, 2023)", 0x63EE0587: "Cobalt Strike 4.8 (Feb 28, 2023)", 0x64F88C5E: "Cobalt Strike 4.9 (Sep 19, 2023)", 0x64F88C7E: "Cobalt Strike 4.9 (Sep 19, 2023)", 0x64F88C9E: "Cobalt Strike 4.9 (Sep 19, 2023)", 0x64F88CDE: "Cobalt Strike 4.9 (Sep 19, 2023)", 0x6691500F: "Cobalt Strike 4.10 (Jul 16, 2024)", 0x66915020: "Cobalt Strike 4.10 (Jul 16, 2024)", 0x66915022: "Cobalt Strike 4.10 (Jul 16, 2024)", 0x66915024: "Cobalt Strike 4.10 (Jul 16, 2024)", 0x66915027: "Cobalt Strike 4.10 (Jul 16, 2024)", 0x6691503D: "Cobalt Strike 4.10 (Jul 16, 2024)", 0x674E0D02: "Cobalt Strike 4.10.1 (Dec 10, 2024)", 0x674E0D13: "Cobalt Strike 4.10.1 (Dec 10, 2024)", 0x674E0D17: "Cobalt Strike 4.10.1 (Dec 10, 2024)", }
""" PE export timestamp to Cobalt Strike version mapping """
[docs] class BeaconVersion(str): """Helper class for dealing with Cobalt Strike version strings"""
[docs] REGEX_VERSION = r"Cobalt Strike (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+))? \((?P<date>.*)\)"
def __init__(self, version: str) -> None:
[docs] self.version: str = version
"""full version string including date, e.g. ``"Cobalt Strike 4.5 (Dec 14, 2021)"``"""
[docs] self.tuple: Optional[Union[Tuple[int, int], Tuple[int, int, int]]] = None
"""the version as tuple of (major, minor) or (major, minor, patch), e.g. ``(4, 5)`` or ``(4, 7, 1)``. Otherwise, ``None``."""
[docs] self.date: Optional[datetime.date] = None
"""date of version as :class:`datetime.date` object, e.g. ``datetime.date(2021, 12, 14)``. Otherwise, ``None``.""" m = re.match(self.REGEX_VERSION, version) if m: self.date = datetime.datetime.strptime(m.group("date"), "%b %d, %Y").date() if m.group("patch"): self.tuple = (int(m.group("major")), int(m.group("minor")), int(m.group("patch"))) else: self.tuple = (int(m.group("major")), int(m.group("minor"))) @classmethod
[docs] def from_pe_export_stamp(cls, pe_export_stamp: int) -> "BeaconVersion": """Construct :class:`BeaconVersion` by looking up `pe_export_stamp` in the :attr:`PE_EXPORT_STAMP_TO_VERSION` map.""" return BeaconVersion(PE_EXPORT_STAMP_TO_VERSION.get(pe_export_stamp, "Unknown"))
@classmethod
[docs] def from_max_setting_enum(cls, enum: int) -> "BeaconVersion": """Construct :class:`BeaconVersion` by looking up `enum` in the :attr:`MAX_ENUM_TO_VERSION` map.""" return BeaconVersion(MAX_ENUM_TO_VERSION.get(enum, "Unknown"))
@property
[docs] def version_string(self) -> str: """The version string without the date. e.g. ``"Cobalt Strike 4.5"``""" return f"Cobalt Strike {self.version_only}"
@property
[docs] def version_only(self) -> str: """The version number only string. e.g. ``"4.5"``, or ``"Unknown"`` if version is unknown.""" if not self.tuple: return "Unknown" return ".".join(map(str, self.tuple))
[docs] def __str__(self) -> str: return self.version
[docs] def __repr__(self) -> str: return f"<BeaconVersion {self.version!r}, tuple={self.tuple}, date={self.date}>"