CloudShark

Use the CloudShark integration to upload, share, and collaborate on network packet capture files using your on-premises CS Enterprise system.

Data Enrichment & Threat Intelligence · CloudShark

Details

IDCloudShark
ProviderQA Cafe LLC
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.8.3296088
Supported ModulesAgentix XSIAM

README

Overview

Designed for networking and security teams, CS Enterprise is a collaboration platform focused on network packet capture (PCAP) analysis. CS Enterprise delivers secure storage, organization, access control, and powerful analysis tools in an elegant, responsive browser-based interface.

Use Cases

  • Upload a network capture for analysis in your browser
  • Collaborate on network pcap analysis by easily sharing captures with others via a URL
  • Collect meta-information about a capture file
  • Manage and organize capture files in CS Enterprise

To set up CS Enterprise to work with Cortex XSOAR

You will need the following before setting up the CS Enterprise integration on
Cortex XSOAR:

  • CS Enterprise URL The URL of your CS Enterprise instance
  • API Token: An API Token from CloudShark with upload, info, download, and delete permissions enabled on it

Configure the CS Enterprise Integration on Cortex XSOAR

  1. Go to Settings > Integrations > Servers & Services
  2. Search for CloudShark
  3. Click Add instance to create and configure a new integration instance
    • Name: a textual name for the integration instance
    • CS Enterprise URL: The URL of your CS Enterprise Instance
    • API Token: Your API token
  4. Click Test to validate the URL

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details.

  1. cloudshark-upload: cloudshark-upload
  2. cloudshark-info: cloudshark-info
  3. cloudshark-download: cloudshark-download
  4. cloudshark-delete: cloudshark-delete

1. cloudshark-upload


Upload a capture file into CS Enterprise

Base Command

cloudshark-upload

Input
Argument Name Description Required
file EntryID of the capture to upload Required
additional_tags A comma-separated list of tags to apply to the capture file Optional
filename The filename of the capture in CS Enterprise Optional

 

Context Output
Path Type Description
URL.Data string URL of the capture file in CS Enterprise
CloudShark.CaptureID string Capture ID of the capture in CS Enterprise

 

Command Example

!cloudshark-upload file=494@1e6024f1-485b-4d1a-8ee3-b6bf51e8ca1a filename=demisto.pcapng additional_tags=demisto,cloudshark,test

Context Example
{
    "CloudShark": {
        "CaptureID": "5277a3a64076"
    },
    "URL": {
        "Data": "CLOUDSHARK_URL/captures/5277a3a64076"
    }
}
Human Readable Output

Open Capture in CloudShark

2. cloudshark-info


Retrieve meta-information about a capture file from CS Enterprise

Base Command

cloudshark-info

Input
Argument Name Description Required
capture_id Capture ID of the capture in CS Enterprise Required

 

Context Output
Path Type Description
CloudShark.CaptureInfo unknown Meta-information of capture file

 

Command Example

!cloudshark-info capture_id=ccaa62cbbb06

Context Example
{
    "CloudShark": {
        "CaptureInfo": {
            "avg_packet_rate": "3.24",
            "avg_packet_size": "70.19",
            "cap_file_id": 165174,
            "comments": "",
            "created_at": "2019-11-06T21:18:30+00:00",
            "data_bit_rate": "1817.36",
            "data_byte_rate": "227.17",
            "data_size": 4071908,
            "disable_autodelete": false,
            "duration": "17924.501967",
            "encapsulation": "Ethernet",
            "end_time": "2019-07-26T23:20:44+00:00",
            "file": "/var/www/cloudshark/current/uploads/2019/11/05/16/13172ab4-61a7-4439-aa27-292306c062c0.cap",
            "file_source": "upload",
            "file_type": "Wireshark/tcpdump/... - pcap",
            "filename": "capture.pcapng",
            "group": "",
            "group_write?": false,
            "id": "ccaa62cbbb06",
            "last_accessed": "2019-11-07T15:42:07+00:00",
            "num_packets": 58009,
            "public?": false,
            "sha1_hash": "e871eee9d85a9898d1f7aec37f22f291fb1d1971",
            "size": 5000076,
            "start_time": "2019-07-26T18:22:00+00:00",
            "tag_list": "",
            "truncated": "No",
        }
    }
}
Human Readable Output

Capture file info

avg_packet_rate avg_packet_size cap_file_id comments created_at data_bit_rate data_byte_rate data_size disable_autodelete duration encapsulation end_time file file_source file_type filename group group_write? id last_accessed num_packets public? sha1_hash size start_time tag_list truncated user
3.24 70.19 165174 2019-11-06T21:18:30+00:00 1817.36 227.17 4071908 false 17924.501967 Ethernet 2019-07-26T23:20:44+00:00 /var/www/cloudshark/current/uploads/2019/11/05/16/13172ab4-61a7-4439-aa27-292306c062c0.cap upload Wireshark/tcpdump/... - pcap capture.pcapng false ccaa62cbbb06 2019-11-07T15:42:07+00:00 58009 false e871eee9d85a9898d1f7aec37f22f291fb1d1971 5000076 2019-07-26T18:22:00+00:00 No

3. cloudshark-download


Download a capture file from CS Enterprise

Base Command

cloudshark-download

Input
Argument Name Description Required
capture_id Capture ID of the capture in CS Enterprise Optional

 

Context Output
Path Type Description
File unknown File downloaded from CloudShark

 

Command Example

!cloudshark-download capture_id=ccaa62cbbb06

Human Readable Output

4. cloudshark-delete


Delete a capture file from CS Enterprise

Base Command

cloudshark-delete

Input
Argument Name Description Required
capture_id Delete a capture file from CS Enterprise Optional

 

Context Output
Path Type Description
CloudShark.Result unknown Result of delete command

 

Command Example

!cloudshark-delete capture_id=ccaa62cbbb06

Context Example
{
    "CloudShark": {
        "Result": {
            "id": "5277a3a64076",
            "message": "Capture deleted successfully.",
            "status": 200
        }
    }
}
Human Readable Output

Result

Response
id: ccaa62cbbb06
status: 200
message: Capture deleted successfully.

Configuration parameters

  • url — CS Enterprise URL (required)
  • apikey — API Token (required)
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)

Commands (4)

  • cloudshark-delete

    Deletes a capture file from CS Enterprise.

  • cloudshark-download

    Downloads a capture file from CS Enterprise.

  • cloudshark-info

    Retrieves meta-information about a capture file from CS Enterprise.

  • cloudshark-upload

    Uploads a capture file into CS Enterprise

import collections

import demistomock as demisto
import requests
import urllib3
from CommonServerPython import *

# disable insecure warnings
urllib3.disable_warnings()

BASE_URL = demisto.params().get("url")
USE_SSL = not demisto.params().get("insecure", False)
PROXY = demisto.params().get("proxy")
API_KEY = demisto.params().get("apikey")

"""HELPER FUNCTIONS"""
capture_info_outputs = {
    "id": "CaptureID",
    "group_write?": "GroupWrite",
    "size": "Size",
    "data_byte_rate": "DataByteRate",
    "tag_list": "TagList",
    "data_bit_rate": "DataBitRate",
    "filename": "FileName",
    "sha1_hash": "SHA1",
    "avg_packet_rate": "AvgPacketRate",
    "start_time": "StartTime",
    "created_at": "CreatedAt",
    "avg_packet_size": "AvgPacketSize",
    "user": "User",
    "last_accessed": "LastAccessed",
    "disable_autodelete": "DisableAutodelete",
    "cap_file_id": "CapFileID",
    "end_time": "EndTime",
    "num_packets": "NumPackets",
    "encapsulation": "Encapsulation",
    "duration": "Duration",
    "comments": "Comments",
    "public?": "Public",
    "data_size": "DataSize",
    "file": "File",
    "file_type": "FileType",
    "file_source": "FileSource",
    "truncated": "Truncated",
    "group": "Group",
}


# Translates the capture info field names to standard outputs
def transalte_capture_info(capture_info):
    translated_output = {}
    for field_name, standard_name in capture_info_outputs.items():
        if field_name in capture_info:
            translated_output[standard_name] = capture_info.get(field_name)
    return translated_output


# Allows nested keys to be accesible
def makehash():
    return collections.defaultdict(makehash)


def parse_error_response(http_response):
    try:
        response = http_response.json()
    except ValueError:
        return (
            f"Error in API call. Recived status code: {http_response.status_code};"
            f" Reason: {http_response.reason}; contents: {http_response.content}"
        )
    exceptions = response.get("exceptions", "")
    if exceptions:  # noqa: RET503
        return f"Call Failed. status code:{http_response.status_code}, reason: {exceptions}"


def http(method, url_suffix, params=None, data=None, files=None):
    try:
        response = requests.request(
            method, BASE_URL + "/api/v1/" + API_KEY + url_suffix, verify=USE_SSL, params=params, data=data, files=files
        )
        if response.status_code == 200:
            return response
        elif (response.status_code == 403) and (url_suffix == "/search/"):
            # API Token cannot execute API method.
            # If testing toekn is valid using the /search/ endpoint this is ok
            return response
        elif response.status_code == 404:
            return_error(parse_error_response(response))
        else:
            return_error(parse_error_response(response))
    except requests.exceptions.ConnectionError as err:
        return_error("Could not connect to CS Enterprise URL ", str(err))
    except requests.exceptions.MissingSchema:
        return_error("Invalid Schema. URL must start with http:// or https://")
    except requests.exceptions.InvalidSchema:
        return_error("Invalid Schema. URL must start with http:// or https://")


"""MAIN FUNCTIONS"""


def upload(file_entry_id, additional_tags=None, filename=None):
    # Get file
    cmd_res = demisto.getFilePath(file_entry_id)
    file_path = cmd_res.get("path")
    name = cmd_res.get("name")

    # Setup optional parameters
    params = {}
    if filename is not None:
        params["filename"] = filename
    if additional_tags is not None:
        params["additional_tags"] = additional_tags

    files = {"file": (name, open(file_path, "rb"))}
    response = http("POST", "/upload", files=files, params=params)
    capture_id = response.json()["id"]
    return capture_id


def upload_command():
    # Get arguments
    file_entry_id = demisto.args().get("file")
    filename = demisto.args().get("filename")
    additional_tags = demisto.args().get("additional_tags")

    # Create hashes
    contxt = makehash()

    # Upload capture and get capture_id
    capture_id = upload(file_entry_id, additional_tags=additional_tags, filename=filename)
    url = BASE_URL + "/captures/" + capture_id

    # Set Demisto Context
    contxt["URL"]["Data"] = url
    contxt["CloudShark"]["CaptureID"] = capture_id
    ec = contxt

    # Create markdown link to capture
    markdown_url = "CaptureID: " + capture_id + " - [Open Capture in CloudShark](" + url + ")"

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["markdown"],
            "Contents": ec,
            "HumanReadable": markdown_url,
            "EntryContext": ec,
        }
    )


def info(capture_id):
    url_suffix = "/info/" + capture_id
    response = http("GET", url_suffix)
    info = response.json()
    return info


def info_command():
    # Get arguments
    capture_id = demisto.args().get("capture_id")

    # Create hashes
    contxt = makehash()

    # Request meta-info from CloudShark
    file_info = info(capture_id)
    # Set Demisto Context
    capture_info = transalte_capture_info(file_info)
    contxt["CloudShark.CaptureInfo(val.CaptureID && val.CaptureID == obj.CaptureID)"] = capture_info
    ec = contxt

    # Create table with capture info
    info_table = tableToMarkdown("Capture file info", capture_info)

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["markdown"],
            "Contents": file_info,
            "HumanReadable": info_table,
            "EntryContext": ec,
        }
    )


def download(capture_id):
    url_suffix = "/download/" + capture_id
    response = http("GET", url_suffix)
    filename = re.findall("filename=(.+)", response.headers["Content-Disposition"])[0]
    file = response.content
    files = {"filename": filename, "file": file}
    return files


def download_command():
    # Get argument
    capture_id = demisto.args().get("capture_id")

    # Download file
    files = download(capture_id)

    demisto.results(fileResult(files["filename"], files["file"]))


def delete(capture_id):
    url_suffix = "/delete/" + capture_id
    response = http("POST", url_suffix)
    msg = response.json()
    return msg


def delete_command():
    # Get argument
    capture_id = demisto.args().get("capture_id")

    # Create hashes
    contxt = makehash()
    human_readable = makehash()

    # Delete capture
    result = delete(capture_id)

    # Set result
    contents = result
    human_readable["Response"] = result
    contxt["CloudShark"]["Result"] = result
    ec = contxt

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["markdown"],
            "Contents": contents,
            "HumanReadable": tableToMarkdown("Result", human_readable),
            "EntryContext": ec,
        }
    )


""" EXECUTION CODE """
LOG(f"command is {demisto.command()}")
try:
    handle_proxy()
    # The command demisto.command() holds the command sent from the user.
    if demisto.command() == "cloudshark-upload":
        upload_command()
    elif demisto.command() == "cloudshark-info":
        info_command()
    elif demisto.command() == "cloudshark-download":
        download_command()
    elif demisto.command() == "cloudshark-delete":
        delete_command()
    elif demisto.command() == "test-module":
        # This is the call made when pressing the integration test button.
        if API_KEY == "":
            return_error("Must enter API Token")
        response = http("GET", "/search/")
        if response.status_code == 200 or response.status_code == 403:
            # Token is valid but search method not allowed
            demisto.results("ok")
        else:
            demisto.results(f"Error: Server returned {response.status_code}: {response.reason}")
        sys.exit(0)
except Exception as e:
    LOG(e)
    LOG.print_log()
    raise