BitDam

BitDam secure email gateway protects from advanced content-borne threats with the most accurate prevention of known and unknown threats, at their source.

IT Services · BitDam

Details

IDBitDam
ProviderKaseya
CategoryIT Services
From Version5.0.0
Docker Imagedemisto/python3:3.12.8.3296088
Supported ModulesAgentix XSIAM

README

Overview


BitDam cyber security blocks advanced content-borne attacks across all enterprise communication channels, empowering organisations to collaborate safely. Founded by elite intelligence professionals, BitDam proactively stops malware from running, pre-delivery, preventing hardware and logical exploits, ransomware, phishing, N-Day and Zero-Day attacks contained in any type of attachment or URL. BitDam ensures the highest attack detection rates and delivers the fastest protection from today’s email borne attacks making enterprise communications safe to click.

For more information, see the BitDam documentation.

Use cases


Scan any supported time in a short time. The BitDam scan file playbook enables you to scan a file and return the result as soon as the file scan completes. This provides a decisive verdict, stating whether the file is benign or malicious.

Configure BitDam on Cortex XSOAR


  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for BitDam.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • BitDam API URL
    • API Token
    • Trust any certificate
    • Use proxy settings
  4. Click Test to validate the URLs, token, and connection.

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. Upload a file: bitdam-upload-file
  2. Get the verdict for a file: bitdam-get-verdict

1. Upload a file


Uploads and submits a file sample to the BitDam service.

Supported types

  • doc, dot, docx, docm, dotx, dotm
  • pdf
  • rtf
  • xls, xlt, xlsx, xlsm, xltx, xltm, xlsb, xlam
  • csv
  • ppt, pptx, pptm, potx, potm, ppam, ppsx, ppsm, pps
Base Command
bitdam-upload-file
Input
Argument Name Description Required
entryId File's entry ID from the War Room Required
 
Context Output
Path Type Description
BitDam.FileScan.SHA1 string SHA-1
 
Command Example
!bitdam-upload-file entryId=499@16
Context Example

root:{} 3 items
BitDam:{} 1 item
FileScan:{} 1 item
SHA1:68f009dc92a405d1015026e8e30e6d1598047124

Human Readable Output

image

2. Get the verdict of a file


Returns the verdict of a scanned file.

Base Command
bitdam-get-verdict
Input
Argument Name Description Required
idValue The value of the file's unique identifier. Example: the file SHA-1. Required
idType Identifier type. Default is SHA-1. Optional
 
Context Output
Path Type Description
BitDam.Analysis.Status string Status of the analysis ("DONE" or "IN_PROGRESS")
BitDam.Analysis.Verdict string Final verdict of the analysis ("Clean", "Malicious", or empty if the analysis is not finished.
BitDam.Analysis.ID string Unique identifier
DBotScore.Indicator string The Indicator
DBotScore.Score number The DBot score
DBotScore.Type string The indicator type
DBotScore.Vendor string The DBot score vendor
File.Malicious.Name string File name
File.Malicious.Vendor string For malicious files, the vendor that made the decision
File.Malicious.Description string For malicious files, the reason that the vendor made the decision
 
Command Example
!bitdam-get-verdict idValue=68f009dc92a405d1015026e8e30e6d1598047124
Context Example

root:{} 4 items
BitDam:{} 2 items
Analysis:{} 3 items
ID:68f009dc92a405d1015026e8e30e6d1598047124
Status:DONE
Verdict:CLEAN
FileScan:{} 1 item
SHA1:68f009dc92a405d1015026e8e30e6d1598047124
DBotScore:{} 4 items
Indicator:68f009dc92a405d1015026e8e30e6d1598047124
Score:1
Type:File
Vendor:BitDam

Human Readable Output

image

Configuration parameters

  • url — BitDam API URL (required)
  • apitoken — API Token (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (2)

  • bitdam-get-verdict

    Returns the verdict for a scanned file.

  • bitdam-upload-file

    Uploads a file sample to the BitDam service.

import demistomock as demisto
from CommonServerPython import *

"""IMPORTS"""
import requests
import base64
import urllib3

urllib3.disable_warnings()

"""INTEGRATION PARAMS"""
API_TOKEN = demisto.params().get("apitoken")
URL_BASE = demisto.params().get("url")
USE_PROXY = demisto.params().get("proxy", False)
UNSECURE = not demisto.params().get("insecure", False)

"""CONSTANTS"""
READ_BINARY_MODE = "rb"
SLASH = "/"
SCAN_FILE_URL = "direct/scan/file/"
GET_FILE_VERDICT_URL = "direct/verdict/?hash={}"
TOKEN_PREFIX = "Bearer"  # guardrails-disable-line
RESPONSE_CODE_OK = 200
STATUS_IN_PROGRESS = "IN_PROGRESS"
STATUS_DONE = "DONE"
AUTH_HEADERS = {"Authorization": f"{TOKEN_PREFIX} {API_TOKEN}"}

VERDICT_SCANNING = "Scanning"
VERDICT_MALICIOUS = "Malicious"
VERDICT_APPROVED = "Approved"
VERDICT_ERROR = "Error"
VERDICT_BENIGN = "Benign"
VERDICT_TIMEOUT = "Timeout"
SCAN_ONGOING = "Still scanning..."

BITDAM_COMMAND_PREFIX = "bitdam"
DBOTSCORE_UNKNOWN = 0
DBOTSCORE_CLEAN = 1
DBOTSCORE_MALICIOUS = 3

"""HANDLE PROXY"""
handle_proxy()


"""HELPER FUNCTIONS"""


def get_file_bytes(entry_id):
    get_file_path_res = demisto.getFilePath(entry_id)
    file_path = get_file_path_res["path"]
    with open(file_path, READ_BINARY_MODE) as fopen:
        bytes = fopen.read()
    return base64.b64encode(bytes)


def get_url_base_with_trailing_slash():
    """
    Returns the intergation's base url parameter, making sure it contains an trailing slash
    """
    url_base = URL_BASE
    return url_base if url_base.endswith(SLASH) else url_base + SLASH


def build_json_response(content, context, human_readable):
    return {
        "Type": entryTypes["note"],
        "ContentsFormat": formats["json"],
        "Contents": content,
        "ReadableContentsFormat": formats["markdown"],
        "HumanReadable": tableToMarkdown(human_readable, content),
        "EntryContext": context,
    }


def get_file_name(entry_id):
    get_file_path_res = demisto.getFilePath(entry_id)
    return get_file_path_res["name"]


def verdict_to_dbotscore(verdict):
    if verdict == VERDICT_APPROVED:
        return DBOTSCORE_CLEAN
    elif verdict == VERDICT_MALICIOUS:
        return DBOTSCORE_MALICIOUS
    elif verdict == VERDICT_SCANNING:
        return DBOTSCORE_UNKNOWN
    else:
        return DBOTSCORE_UNKNOWN


"""API_IMPL"""


def scan_file():
    response = scan_file_command()
    returned_sha1 = parse_scan_file_response(response)
    # Build demisto reponse
    response_content = {"SHA1": returned_sha1}
    response_context = {"BitDam": {"FileScan": {"SHA1": returned_sha1}}}
    return build_json_response(response_content, response_context, "File was submitted successfully")


def scan_file_command():
    # Get data to build the request
    entry_id = demisto.args().get("entryId")
    file_name = get_file_name(entry_id)
    file_bytes = get_file_bytes(entry_id)
    json_data = {"file_name": file_name, "file_data_base64": base64.b64encode(file_bytes)}
    raw_json = json.dumps(json_data, ensure_ascii=False)
    url = f"{get_url_base_with_trailing_slash()}{SCAN_FILE_URL}"

    # Send the HTTP request
    response = requests.post(url, data=raw_json, headers=AUTH_HEADERS, verify=UNSECURE)
    return response


def parse_scan_file_response(response):
    # Parse response
    if response.status_code != RESPONSE_CODE_OK:
        raise Exception(f"Scan file failed. Response code -{str(response.status_code)}, Data- '{response.content}'")
    response_json = json.loads(response.content)
    if "sha1" not in response_json:
        raise Exception(f"Scan file failed. Bad response json - {response.content}")
    returned_sha1 = response_json["sha1"]
    return returned_sha1


def get_file_verdict():
    identifier_value = demisto.args().get("idValue")
    response = get_file_verdict_command(identifier_value)
    verdict, status = parse_get_file_verdict_response(response)
    response_content = {"STATUS": status, "VERDICT": verdict, "ID": identifier_value}
    context = {}
    context["BitDam.Analysis(val.ID && val.ID == obj.ID)"] = {"Status": status, "Verdict": verdict, "ID": identifier_value}

    if verdict == VERDICT_MALICIOUS:
        context[outputPaths["file"]] = {"SHA1": identifier_value}
        context[outputPaths["file"]]["Malicious"] = {
            "Vendor": "BitDam",
            "Description": "Process whitelist inconsistency by bitdam-get-file-verdict",
            "Name": identifier_value,
        }

    dbotscore = verdict_to_dbotscore(verdict)
    if dbotscore:
        context[outputPaths["dbotscore"]] = {
            "Indicator": identifier_value,
            "Type": "File",
            "Vendor": "BitDam",
            "Score": dbotscore,
        }
    response_context = context
    return build_json_response(response_content, response_context, "Get file verdict was performed successfully")


def parse_get_file_verdict_response(response):
    # Parse results
    if response.status_code != RESPONSE_CODE_OK:
        raise Exception(f"Get file verdict failed. Response code -{str(response.status_code)}, Data- '{response.content}'")
    response_json = json.loads(response.content)
    status = ""
    verdict = ""
    if "scan_data" not in response_json or "verdict" not in response_json["scan_data"]:
        raise Exception(f"Get file verdict failed. Unknown response schema. Data- '{response.content}'")

    verdict = response_json["scan_data"]["verdict"]
    if verdict in (SCAN_ONGOING, VERDICT_SCANNING):
        # Still in progress
        verdict = VERDICT_SCANNING
        status = STATUS_IN_PROGRESS
    else:
        status = STATUS_DONE

    return verdict, status


def get_file_verdict_command(identifier_value):
    # Get data to build the request
    scan_file_relative_url_formatted = GET_FILE_VERDICT_URL.format(identifier_value)

    url = f"{get_url_base_with_trailing_slash()}{scan_file_relative_url_formatted}"
    # Send the request
    response = requests.get(url, headers=AUTH_HEADERS, verify=UNSECURE)
    return response


def upload_test_file_to_scan():
    d = {"file_name": "demisto.txt", "file_data_base64": "ZGVtaXN0bw=="}
    url = f"{get_url_base_with_trailing_slash()}{SCAN_FILE_URL}"
    response = requests.post(url, headers=AUTH_HEADERS, json=d, verify=UNSECURE)
    return response


def test_module():
    response = upload_test_file_to_scan()
    if response.status_code == RESPONSE_CODE_OK:
        return True
    raise Exception(f"Status code - {str(response.status_code)}, Error- '{response.content}'")


"""COMMAND_CLASIFIER"""
try:
    if demisto.command() == "test-module":
        # This is the call made when pressing the integration test button.
        if test_module():
            demisto.results("ok")
        sys.exit(0)
    elif demisto.command() == "bitdam-upload-file":
        demisto.results(scan_file())
    elif demisto.command() == "bitdam-get-verdict":
        demisto.results(get_file_verdict())
except Exception as e:
    LOG(e)
    return_error(f"Error: {str(e)}")