dissect.cobaltstrike.c2 ======================= .. py:module:: dissect.cobaltstrike.c2 .. autoapi-nested-parse:: This module is responsible for working with Cobalt Strike C2 traffic. Attributes ---------- .. autoapisummary:: dissect.cobaltstrike.c2.TransformStep dissect.cobaltstrike.c2.C2Packet dissect.cobaltstrike.c2.logger Classes ------- .. autoapisummary:: dissect.cobaltstrike.c2.EncryptedPacket dissect.cobaltstrike.c2.C2Data dissect.cobaltstrike.c2.ServerC2Data dissect.cobaltstrike.c2.ClientC2Data dissect.cobaltstrike.c2.HttpRequest dissect.cobaltstrike.c2.HttpResponse dissect.cobaltstrike.c2.BeaconKeys dissect.cobaltstrike.c2.HttpDataTransform dissect.cobaltstrike.c2.C2Http Functions --------- .. autoapisummary:: dissect.cobaltstrike.c2.enable_reprlib_c2 dissect.cobaltstrike.c2.c2packet_to_record dissect.cobaltstrike.c2.parse_raw_http dissect.cobaltstrike.c2.decrypt_metadata dissect.cobaltstrike.c2.encrypt_metadata dissect.cobaltstrike.c2.derive_aes_hmac_keys dissect.cobaltstrike.c2.pad dissect.cobaltstrike.c2.encrypt_data dissect.cobaltstrike.c2.decrypt_data dissect.cobaltstrike.c2.decrypt_packet dissect.cobaltstrike.c2.encrypt_packet Module Contents --------------- .. py:data:: TransformStep Type TransformStep. .. py:data:: C2Packet Type that is either a :class:`BeaconMetadata`, a :class:`TaskPacket` or a :class:`CallbackPacket`. .. py:data:: logger .. py:class:: EncryptedPacket Bases: :py:obj:`NamedTuple` Container to hold ciphertext and HMAC signature. .. py:attribute:: ciphertext :type: bytes .. py:attribute:: signature :type: bytes .. py:method:: dumps() Return the EncryptedPacket as a bytes object with a size frame header. ``| size | ciphertext | signature |`` .. py:method:: raise_for_signature(hmac_key: bytes) :param hmac_key: HMAC key to use for signature verification :raises ValueError: if signature of the ciphertext is incorrect. .. py:class:: C2Data Bases: :py:obj:`NamedTuple` Container for holding C2 data that is used for transform and recover steps. .. py:attribute:: output :type: Optional[bytes] :value: None .. py:attribute:: metadata :type: Optional[bytes] :value: None .. py:attribute:: id :type: Optional[bytes] :value: None .. py:class:: ServerC2Data Bases: :py:obj:`C2Data` Container for holding recovered server-side C2Data. .. py:method:: iter_encrypted_packets() -> Iterator[EncryptedPacket] Iterate over ``EncryptedPacket``, parsed from server-side `c2data.output` data. For server-side data this is always one packet. .. py:class:: ClientC2Data Bases: :py:obj:`C2Data` Container for holding recovered client-side C2Data. .. py:method:: iter_encrypted_packets() -> Iterator[EncryptedPacket] Iterate over ``EncryptedPacket``, parsed from client-side `c2data.output` data. For client-side data this could be one or more packets. .. py:class:: HttpRequest Bases: :py:obj:`NamedTuple` HTTP Request container. .. py:attribute:: method :type: bytes .. py:attribute:: uri :type: bytes .. py:attribute:: params :type: Dict[bytes, bytes] .. py:attribute:: headers :type: Dict[bytes, bytes] .. py:attribute:: body :type: bytes .. py:class:: HttpResponse Bases: :py:obj:`NamedTuple` HTTP Response container. .. py:attribute:: status :type: int .. py:attribute:: headers :type: Dict[bytes, bytes] .. py:attribute:: reason :type: bytes .. py:attribute:: body :type: bytes .. py:attribute:: request :type: Optional[HttpRequest] :value: None .. py:class:: BeaconKeys Bases: :py:obj:`NamedTuple` Helper container to hold beacon session keys (AES + HMAC). .. py:attribute:: DEFAULT_AES_IV :value: b'abcdefghijklmnop' .. py:attribute:: aes_key :type: Optional[bytes] .. py:attribute:: hmac_key :type: Optional[bytes] :value: None .. py:attribute:: iv :type: bytes :value: b'abcdefghijklmnop' .. py:method:: from_aes_rand(aes_rand: bytes, iv: bytes = DEFAULT_AES_IV) -> BeaconKeys :classmethod: Create a :class:`BeaconKeys` instance from AES random bytes. .. py:method:: from_beacon_metadata(metadata: dissect.cobaltstrike.c_c2.BeaconMetadata, iv: bytes = DEFAULT_AES_IV) -> BeaconKeys :classmethod: Create a :class:`BeaconKeys` instance from :class:`BeaconMetadata`. .. py:function:: enable_reprlib_c2() Enables reprlib __repr__ for most of the namedtuple classes in this module. .. py:function:: c2packet_to_record(c2packet: C2Packet) -> flow.record.Record Convert `c2packet` to a flow.record. .. py:function:: parse_raw_http(data: bytes) -> Union[HttpRequest, HttpResponse] Parse a raw HTTP request/response bytes and returns a :class:`HttpRequest` or :class:`HttpResponse` accordingly. :param data: raw HTTP request or response data bytes. :returns: Either a :class:`HttpRequest` or :class:`HttpResponse` object based on the data. :raises ValueError: if it cannot be parsed as :class:`HttpRequest` or :class:`HttpResponse`. .. py:class:: HttpDataTransform(steps: List[TransformStep], reverse: bool = False, build: str = None) Transform and recover Cobalt Strike HTTP C2 data using transformation steps. .. py:attribute:: tsteps :type: List[TransformStep] .. py:attribute:: rsteps :type: List[TransformStep] .. py:method:: transform(c2data: C2Data, request: Optional[HttpRequest] = None) -> HttpRequest Transform `c2data` information into a :class:`HttpRequest` namedtuple. :param c2data: :class:`C2Data` named tuple that needs to be transformed :param request: Optional initial HTTP request data :returns: Transformed HTTP request data :rtype: HttpRequest .. py:method:: recover(http: HttpRequest) -> ClientC2Data recover(http: HttpResponse) -> ServerC2Data Recovers the transformed data in `http` object and returns a C2Data namedtuple. :param http: a :class:`HttpRequest` or :class:`HttpResponse` namedtuple :returns: Either a :class:`ClientC2Data` or :class:`ServerC2Data` namedtuple based on the `http` data. .. py:class:: C2Http(bconfig: dissect.cobaltstrike.beacon.BeaconConfig, aes_key: Optional[bytes] = None, hmac_key: Optional[bytes] = None, aes_rand: Optional[bytes] = None, rsa_private_key: Optional[Crypto.PublicKey.RSA.RsaKey] = None, verify_hmac=True) Class for decrypting and encrypting Cobalt Strike HTTP C2 traffic. It requires to be initialized with a :class:`BeaconConfig` and one of the following *key* material: * `aes_key` and optionally `hmac_key` * `aes_rand` * `rsa_private_key` (most preferred when available) .. py:attribute:: bconfig .. py:attribute:: aes_key :value: None .. py:attribute:: hmac_key :value: None .. py:attribute:: verify_hmac :value: True .. py:attribute:: pub .. py:attribute:: priv :value: None .. py:attribute:: submit_uri :type: bytes .. py:attribute:: submit_verb :type: bytes .. py:attribute:: get_uris :type: Tuple[bytes, Ellipsis] .. py:attribute:: get_verb :type: bytes .. py:attribute:: transform_submit .. py:attribute:: transform_get .. py:attribute:: transform_response .. py:attribute:: metadata_cache :type: Dict[bytes, dissect.cobaltstrike.c_c2.BeaconMetadata] .. py:attribute:: beacon_keys .. py:method:: get_transform_for_http(http: Union[HttpRequest, HttpResponse, bytes]) -> HttpDataTransform Return the correct :class:`HttpDataTransform` instance for given `http`. :param http: either a :class:`HttpRequest` or :class:`HttpResponse` object or raw HTTP bytes. :returns: The correct :class:`HttpDataTransform` instance for given `http`. :rtype: HttpDataTransform :raises ValueError: if no correct transform can be found for given `http` object. .. py:method:: iter_recover_http(http: Union[bytes, HttpRequest, HttpResponse], keys: Optional[BeaconKeys] = None) -> Iterator[C2Packet] Yield decrypted :class:`C2Packet` objects from given `http` object. You can pass your own set of :class:`BeaconKeys` `keys` to use for decryption instead of the default initialized ones. This can be useful if you are processing multiple Beacon sessions and do some sort of session tracking outside this class. :param http: A :class:`HttpRequest` or :class:`HttpResponse` object, or raw HTTP request or response bytes. :param keys: Optional :class:`BeaconKeys` to use for decryption instead of current default keys. :Yields: *C2Packet* -- A :class:`C2Packet` object for each decrypted packet found in the HTTP request or response. .. py:function:: decrypt_metadata(encrypted_metadata: bytes, private_key: Crypto.PublicKey.RSA.RsaKey) -> dissect.cobaltstrike.c_c2.BeaconMetadata Decrypt `encrypted_metadata` using RSA `private_key`. :param encrypted_metadata: the encrypted metadata bytes :param private_key: the RSA private key used for decryption :returns: The decrypted metadata. :rtype: BeaconMetadata :raises ValueError: if RSA failed to decrypt or metadata magic is invalid .. py:function:: encrypt_metadata(metadata: dissect.cobaltstrike.c_c2.BeaconMetadata, public_key: Crypto.PublicKey.RSA.RsaKey) -> bytes Encrypt `metadata` using RSA `public_key`. :param metadata: :class:`BeaconMetadata` object to encrypt :param public_key: the RSA public key used for encryption :returns: The encrypted metadata as bytes .. py:function:: derive_aes_hmac_keys(aes_random: bytes) -> Tuple[bytes, bytes] Derive the AES and HMAC keys from the `aes_random` bytes. :param aes_random: the bytes to derive the keys from :returns: Tuple of (aes_key, hmac_key) .. py:function:: pad(data: bytes, block_size: int = AES.block_size) -> bytes Mimics the padding behaviour in Cobalt Strike (which is to fill it with b'A'). :param data: the data to pad :param block_size: the block size to use for padding :returns: The padded data .. py:function:: encrypt_data(data: bytes, aes_key: bytes, iv: bytes) -> bytes AES encrypt `data` with given `aes_key` and `iv`. :param data: the data to encrypt :param aes_key: the AES key to use :param iv: the initialization vector to use :returns: The encrypted data as bytes .. py:function:: decrypt_data(data: bytes, aes_key: bytes, iv: bytes) -> bytes AES decrypt the `data` with given `aes_key` and `iv` and return the decrypted bytes. :param data: the encrypted data :param aes_key: the AES key to use for decryption :param iv: the AES IV to use for decryption :returns: The decrypted data as bytes .. py:function:: decrypt_packet(packet: EncryptedPacket, aes_key: bytes, hmac_key: Optional[bytes] = None, iv: bytes = BeaconKeys.DEFAULT_AES_IV, verify: bool = True) -> bytes Decrypt :class:`EncryptedPacket` `packet` and return the decrypted plaintext bytes. If `hmac_key` is defined, the signature of the ciphertext is verified first before decrypting. :param packet: the :class:`EncryptedPacket` to decrypt :param aes_key: the AES key to use for decryption :param hmac_key: the HMAC key to use for signature verification :param iv: the AES IV to use for decryption :param verify: whether to verify the HMAC signature of the ciphertext :returns: The decrypted plaintext bytes .. py:function:: encrypt_packet(plaintext: bytes, aes_key: bytes, hmac_key: bytes, iv: bytes = BeaconKeys.DEFAULT_AES_IV) -> EncryptedPacket Encrypt `plaintext` bytes and return a :class:`EncryptedPacket`. :param plaintext: the plaintext bytes to encrypt :param aes_key: the AES key to use for encryption :param hmac_key: the HMAC key to use for signature generation :param iv: the AES IV to use for encryption :returns: The :class:`EncryptedPacket` containing the ciphertext and HMAC signature