PcapConvert

Convert packet data to the standard pcap. Currently it only supports CDL(NGFW) pcap from which to convert.

python · PCAP Analysis

Source

import base64
from collections.abc import Generator
from typing import Any

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401


def convert_pcap(pcap_type: str, pcap_bytes: bytes) -> bytes | None:
    """
    Convert a CDL pcap to a standard PCAP.

    :param pcap_type: The data type of the PCAP.
    :param pcap_bytes: The data of a PCAP.
    :return: The data of the PCAP which can be parsed by pyshark.
    """
    if pcap_type in ("auto", "cdl-pcap"):
        while True:
            if pcap_bytes[0:1] not in (b"\x01", b"\x00") or pcap_bytes[1:2] not in (b"\x01", b"\x00"):
                if pcap_type != "auto":
                    raise ValueError("Invalid packet in CDL format")
                break

            POS_FIRST_PACKET = 0x24
            packet_time_be32 = pcap_bytes[16 : 16 + 4]
            packet_size_be16 = pcap_bytes[2 : 2 + 2]
            packet_size = int.from_bytes(packet_size_be16, byteorder="big")
            if len(pcap_bytes) < packet_size + POS_FIRST_PACKET:
                if pcap_type != "auto":
                    raise ValueError("Invalid packet in CDL format")
                break

            # pcap header
            pcap = b"\xa1\xb2\xc3\xd4"  # .magic
            pcap += b"\x00\x02"  # .major
            pcap += b"\x00\x04"  # .minor
            pcap += b"\x00\x00\x00\x00"  # .thiszone
            pcap += b"\x00\x00\x00\x00"  # .sigfigs
            pcap += b"\x00\x00\xff\xff"  # .snaplen
            pcap += b"\x00\x00\x00\x01"  # .linktype

            # packet header
            pcap += packet_time_be32  # .ts.tv_sec
            pcap += b"\x00\x00\x00\x00"  # .ts.tv_usec
            pcap += b"\x00\x00" + packet_size_be16  # .caplen: length of portion present
            pcap += b"\x00\x00" + packet_size_be16  # .len: length this packet (off wire)

            # packet payload
            pcap += pcap_bytes[POS_FIRST_PACKET : POS_FIRST_PACKET + packet_size]
            return pcap
    else:
        raise ValueError(f"Unknown pcap type: {pcap_type}")

    return None


def split_context_path(path: str) -> tuple[list[str], str]:
    """
    Split a context path separated by a dot with a replacement name
    following a comma into the key tree the replacement name.

    :param path: The context path (with an optional replacement name)
    :return: The key tree and the replacement name.
    """
    key_tree = []
    key = []
    itr = iter(path)
    for c in itr:
        if c == "\\":
            try:
                key.append(next(itr))
            except StopIteration:
                key.append("\\")
        elif c == ".":
            key_tree.append("".join(key))
            key = []
        else:
            key.append(c)

    names = "".join(key).rsplit(",", 1)
    if len(names) == 2:
        key_tree.append(names[0])
        return key_tree, names[1]
    elif len(names) == 1:
        key_tree.append(names[0])
        return key_tree, names[0]
    else:
        raise ValueError(f"Invalid path: {path}")


class MainProcess:
    def __init__(self, pcap_type: str, error_action: str):
        self.__pcap_type = pcap_type
        self.__error_action = error_action

    def __convert_pcap(self, node: Any) -> Generator[Any, None, None]:
        """
        Convert pcap(s) in the node.

        :param node: The pcap(s) node.
        :return: The pcap(s) converted.
        """
        if isinstance(node, list):
            for v in node:
                yield from self.__convert_pcap(v)
        else:
            try:
                if not isinstance((pcap_value := node), str):
                    raise ValueError(f"A pcap value is not str: {type(pcap_value)}")

                if pcap_bytes := base64.b64decode(pcap_value.encode()):
                    if (pcap := convert_pcap(self.__pcap_type, pcap_bytes)) is None:
                        raise ValueError("Invalid pcap")

                    yield base64.b64encode(pcap).decode()
            except Exception:
                if self.__error_action == "abort":
                    raise
                elif self.__error_action == "keep":
                    yield node
                elif self.__error_action == "ignore":
                    pass
                else:
                    raise ValueError(f"Invalid error action: {self.__error_action}")

    def convert_and_replace(self, node: Any, key_tree: list[str], repl_name: str) -> None:
        """
        Convert from pcaps specified with 'node' and 'key_tree',
        and set them into the node specified with 'repl_name'

        :param node: The node
        :param key_tree: The list of the keys to the node where the pcap exists.
        :param repl_name: The name of the key to set the standard pcap.
        """
        for i, key in enumerate(key_tree):
            if isinstance(node, list):
                for v in node:
                    self.convert_and_replace(v, key_tree[i:], repl_name)
                return

            if not isinstance(node, dict) or key not in node:
                # The key is not found in the node
                return

            if i != len(key_tree) - 1:
                node = node[key]
            else:
                node[repl_name] = self.convert_pcap(node[key])

    def convert_pcap(self, node: Any) -> Any:
        """
        Convert pcap(s) in the node.

        :param node: The pcap(s) node.
        :return: The pcap(s) converted.
        """
        pcaps = list(self.__convert_pcap(node))
        if isinstance(node, list) or len(pcaps) != 1:
            return pcaps
        else:
            return pcaps[0]


"""MAIN"""


def main():
    args = demisto.args()
    value = args.get("value", "")
    path = args.get("path")
    pcap_type = args.get("pcap_type") or "auto"
    error_action = args.get("error_action") or "abort"

    proc = MainProcess(pcap_type=pcap_type, error_action=error_action)

    if path:
        key_tree, repl_name = split_context_path(path)
        proc.convert_and_replace(value, key_tree, repl_name)
    else:
        value = proc.convert_pcap(value)

    return_results(value)


if __name__ in ("__main__", "builtin", "builtins"):
    main()

README

Convert packet data to the standard pcap. Currently it only supports CDL(NGFW) pcap from which to convert.

Script Data


Name Description
Script Type python3
Tags pcap, Utility, transformer

Inputs


Argument Name Description
value The value to be converted from.
path The context path to the pcap (e.g., PcapData.pcap). If you add a comma + a node name after the path, the output will be set to the node (e.g., PcapData.pcap,out).
pcap_type The data type of the pcap data.
error_action The action on error to parsing pcap. Possible values are abort (default), ignore, and keep.

Outputs


There are no outputs for this script.