OpenCTI Feed 4.X

Ingest indicators from the OpenCTI feed. Compatible with OpenCTI 5.12.17 and above.

Data Enrichment & Threat Intelligence · OpenCTI Feed · Feed

Details

IDOpenCTI Feed 4.X
ProviderFiligran
CategoryData Enrichment & Threat Intelligence
From Version5.5.0
Docker Imagedemisto/vendors-sdk:1.0.0.115493
Supported ModulesAgentix XSIAM

README

Ingest indicators from the OpenCTI feed. Compatible with OpenCTI 5.12.17 and above.

Configure OpenCTI Feed 4.X in Cortex

Parameter Description Required
Base URL   True
API Key (leave empty. Fill in the API key in the password field.)   False
Indicator types to fetch The indicator types to fetch. Out-of-the-box indicator types supported in XSOAR are: Account, Domain, Email, File, Host, IP, IPv6, Registry Key, and URL. Other types will not cause automatic indicator creation in XSOAR. True
Max indicators per fetch   False
Fetch indicators   False
Indicator Reputation Indicators from this integration instance will get this reputation. If none of the options is chosen, the indicator reputation will be set according to the indicator data. False
Source Reliability Reliability of the source providing the intelligence data. True
Traffic Light Protocol Color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. If none of the options is chosen, the TLP color will be set according to the indicator data. False
Feed Fetch Interval   False
Tags CSV values are supported. False
Bypass exclusion list When selected, the exclusion list is ignored for indicators from this feed. This means that if an indicator from this feed is on the exclusion list, the indicator might still be added to the system. False
Trust any certificate (not secure)   False
Use system proxy settings   False
Score minimum value Score minimum value to filter by. Values range is 1-100. False
Score maximum value Score maximum value to filter by. Values range is 1-100. False

Commands

You can execute these commands from the 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.

opencti-reset-fetch-indicators


WARNING: This command will reset your fetch history.

Base Command

opencti-reset-fetch-indicators

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command Example


#### Human Readable Output

>Fetch history deleted successfully

### opencti-get-indicators

***
Gets indicators from the feed.

#### Base Command

`opencti-get-indicators`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| limit | The maximum number of indicators to return per fetch. Default value is 50. Maximum value is 500. | Optional |
| indicator_types | The indicator types to fetch. Out-of-the-box indicator types supported in XSOAR are: Account, Domain, Email, File, Host, IP, IPv6, Registry Key, and URL. Other types will not cause automatic indicator creation in XSOAR. Possible values are: ALL, Account, Domain, Email, File, Host, IP, IPv6, Registry Key, URL. Default is ALL. | Optional |
| last_run_id | The last ID from the previous call, from which to begin pagination for this call. You can find this value at the OpenCTI.IndicatorsList.LastRunID context path. | Optional |
| score_start | Score minimum value to filter by. Values range is 1-100. | Optional |
| score_end | Score naximum value to filter by. Values range is 1-100. | Optional |

#### Context Output

There is no context output for this command.

#### Command Example

```!opencti-get-indicators limit=2 indicator_types="IP"```

#### Context Example

```json
{
    "OpenCTI": {
        "Indicators": {
            "IndicatorsList": [
                {
                    "createdBy": "1e12fe87-db3e-4838-8391-6910547bf60d",
                    "description": "test_desc",
                    "externalReferences": [],
                    "id": "700c8187-2dce-4aeb-bf3a-0864cb7b02c7",
                    "labels": [
                        "dev1"
                    ],
                    "marking": [
                        "TLP:AMBER"
                    ],
                    "score": 70,
                    "type": "IP",
                    "value": "1.2.3.4"
                },
                {
                    "createdBy": "1e12fe87-db3e-4838-8391-6910547bf60d",
                    "description": "test fetch one",
                    "externalReferences": [],
                    "id": "33bd535b-fa1c-41e2-a6f9-80d82dd29a9b",
                    "labels": [
                        "dev1",
                        "test-label-1"
                    ],
                    "marking": [],
                    "score": 100,
                    "type": "IP",
                    "value": "1.1.1.1"
                }
            ],
            "lastRunID": "YXJyYXljb25uZWN0aW9uOjI="
        }
    }
}

Human Readable Output

Indicators

type value id
IP 1.2.3.4 700c8187-2dce-4aeb-bf3a-0864cb7b02c7
IP 1.1.1.1 33bd535b-fa1c-41e2-a6f9-80d82dd29a9b

Configuration parameters

  • base_url — Base URL (required)
  • credentials — API Key (leave empty. Fill in the API key in the password field.)
  • indicator_types — Indicator types to fetch (required)
  • max_indicator_to_fetch — Max indicators per fetch
  • feed — Fetch indicators
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability (required)
  • tlp_color — Traffic Light Protocol Color
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedFetchInterval — Feed Fetch Interval
  • feedTags — Tags
  • feedBypassExclusionList — Bypass exclusion list
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • score_start — Score minimum value
  • score_end — Score maximum value

Commands (2)

  • opencti-get-indicators

    Gets indicators from the feed.

  • opencti-reset-fetch-indicators

    WARNING: This command will reset your fetch history.

from typing import Any

import demistomock as demisto  # noqa: F401
import urllib3
from CommonServerPython import *  # noqa: F401
from pycti import OpenCTIApiClient

# Disable insecure warnings
urllib3.disable_warnings()

# Disable info logging from the api
logging.getLogger().setLevel(logging.ERROR)
OPENCTI_LOGS = "opencti_logs"

XSOAR_TYPES_TO_OPENCTI = {
    "account": "User-Account",
    "domain": "Domain-Name",
    "email": "Email-Addr",
    "file-md5": "StixFile",
    "file-sha1": "StixFile",
    "file-sha256": "StixFile",
    "file": "StixFile",
    "host": "X-OpenCTI-Hostname",
    "ip": "IPv4-Addr",
    "ipv6": "IPv6-Addr",
    "registry key": "Windows-Registry-Key",
    "url": "Url",
}
OPENCTI_TYPES_TO_XSOAR = {
    "User-Account": "Account",
    "Domain-Name": "Domain",
    "Email-Addr": "Email",
    "StixFile": "File",
    "X-OpenCTI-Hostname": "Host",
    "IPv4-Addr": "IP",
    "IPv6-Addr": "IPv6",
    "Windows-Registry-Key": "Registry Key",
    "Url": "URL",
}


def build_indicator_list(indicator_list: list[str]) -> list[str]:
    """Builds an indicator list for the query
    Args:
        indicator_list: List of XSOAR indicators types to return..

    Returns:
        indicators: list of OPENCTI indicators types"""
    result = []
    if "ALL" in indicator_list:
        # Replaces "ALL" for all types supported on XSOAR.
        result = [
            "User-Account",
            "Domain-Name",
            "Email-Addr",
            "StixFile",
            "X-OpenCTI-Hostname",
            "IPv4-Addr",
            "IPv6-Addr",
            "Windows-Registry-Key",
            "Url",
        ]
    else:
        result = [XSOAR_TYPES_TO_OPENCTI.get(indicator.lower(), indicator) for indicator in indicator_list]
    return result


def reset_last_run():
    """
    Reset the last run from the integration context
    """
    demisto.setIntegrationContext({})
    return CommandResults(readable_output="Fetch history deleted successfully")


def get_indicators(
    client: OpenCTIApiClient,
    indicator_types: list[str],
    score: list[str] = None,
    limit: int | None = 500,
    last_run_id: str | None = None,
    tlp_color: str | None = None,
    tags: list[str] = None,
) -> tuple[str, list]:
    """Retrieving indicators from the API

    Args:
        score: Range of scores to filter by.
        client: OpenCTI Client object.
        indicator_types: List of indicators types to return.
        last_run_id: The last id from the previous call to use pagination.
        limit: the max indicators to fetch
        tlp_color: traffic Light Protocol color
        tags: user tags

    Returns:
        new_last_run: the id of the last indicator
        indicators: list of indicators
    """
    indicator_type = build_indicator_list(indicator_types)
    demisto.debug(f"{OPENCTI_LOGS} - in get_indicators - builded indicator type  : {indicator_type}")
    filters: dict[str, Any] = {
        "mode": "and",
        "filters": [{"key": "entity_type", "values": indicator_type, "operator": "eq", "mode": "or"}],
        "filterGroups": [],
    }
    if score:
        filters["filters"].append({"key": "x_opencti_score", "values": score, "operator": "eq", "mode": "or"})
    demisto.debug(f"{OPENCTI_LOGS} - in get_indicators - {filters=}")
    observables = client.stix_cyber_observable.list(filters=filters, after=last_run_id, first=limit, withPagination=True)
    new_last_run = observables.get("pagination").get("endCursor")
    demisto.debug(f"{OPENCTI_LOGS} - in get_indicators - {new_last_run=}")

    indicators = []
    for item in observables.get("entities"):
        indicator = {
            "value": item["observable_value"],
            "type": OPENCTI_TYPES_TO_XSOAR.get(item["entity_type"], item["entity_type"]),
            "rawJSON": item,
            "fields": {
                "tags": [tag.get("value") for tag in item.get("objectLabel")],
                "description": item.get("x_opencti_description"),
            },
        }
        if tags:
            indicator["fields"]["tags"] += tags

        if not tlp_color:
            if object_marking := item.get("objectMarking", []):
                new_tlp_color = object_marking[0].get("definition", "").split(":")
                indicator["fields"]["trafficlightprotocol"] = new_tlp_color[1]
        else:
            indicator["fields"]["trafficlightprotocol"] = tlp_color

        score = item.get("x_opencti_score")
        if score in [*range(21)]:
            indicator["score"] = 1
        elif score in [*range(21, 71)]:
            indicator["score"] = 2
        elif score in [*range(71, 101)]:
            indicator["score"] = 3

        indicators.append(indicator)
    demisto.debug(f"{OPENCTI_LOGS} - in get_indicators - sum of indicators: {len(indicators)}")
    return new_last_run, indicators


def fetch_indicators_command(
    client: OpenCTIApiClient, indicator_types: list, max_fetch: int, score: list = None, tlp_color=None, tags=None, is_test=False
) -> list:
    """fetch indicators from the OpenCTI

    Args:
        score: Range of scores to filter by.
        client: OpenCTI Client object
        indicator_types(list): List of indicators types to get.
        max_fetch: (int) max indicators to fetch.
        tlp_color: (str)
        tags: (list)
        is_test: (bool) Indicates that it's a test and then does not save the last run.
    Returns:
        list of indicators(list)
    """
    last_run_id = demisto.getIntegrationContext().get("last_run_id")
    demisto.info(f"{OPENCTI_LOGS} - in fetch_indicators_command - get last run = {last_run_id}")

    new_last_run, indicators_list = get_indicators(
        client, indicator_types, limit=max_fetch, last_run_id=last_run_id, tlp_color=tlp_color, score=score, tags=tags
    )

    if new_last_run and not is_test:
        demisto.setIntegrationContext({"last_run_id": new_last_run})
        demisto.debug(f"{OPENCTI_LOGS} - in fetch_indicators_command - set last run = {new_last_run}")
        # we submit the indicators in batches
        for b in batch(indicators_list, batch_size=2000):
            demisto.createIndicators(b)
            demisto.debug(f"{OPENCTI_LOGS} - in fetch_indicators_command - indicators created successfully.")

    return indicators_list


def get_indicators_command(client: OpenCTIApiClient, args: dict) -> CommandResults:
    """Gets indicator from opencti to readable output

    Args:
        client: OpenCTI Client object
        args: demisto.args()

    Returns:
        readable_output, raw_response
    """
    indicator_types = argToList(args.get("indicator_types"))
    last_run_id = args.get("last_run_id")
    limit = arg_to_number(args.get("limit", 50))
    start = arg_to_number(args.get("score_start", 1))
    end = arg_to_number(args.get("score_end", 100)) + 1  # type:ignore
    demisto.debug(f"{OPENCTI_LOGS} - in get_indicators_command - {indicator_types=} {last_run_id=} {limit=} {start=} {end=}")
    score = None
    if start or end:
        score = [str(i) for i in range(start, end)]  # type:ignore
    demisto.debug(f"{OPENCTI_LOGS} - in get_indicators_command -{score=}")
    last_run_id, indicators_list = get_indicators(
        client=client, indicator_types=indicator_types, limit=limit, last_run_id=last_run_id, score=score
    )

    if indicators_list:
        indicators = [
            {
                "type": indicator["type"],
                "value": indicator["value"],
                "id": indicator["rawJSON"]["id"],
                "rawJSON": indicator["rawJSON"],
            }
            for indicator in indicators_list
        ]
        readable_output = tableToMarkdown("Indicators", indicators, headers=["type", "value", "id"], removeNull=True)
        return CommandResults(readable_output=readable_output, raw_response=indicators_list)
    else:
        return CommandResults(readable_output="No indicators")


def main():
    params = demisto.params()
    args = demisto.args()

    credentials = params.get("credentials", {})
    api_key = credentials.get("password")
    base_url = params.get("base_url").strip("/")
    indicator_types = params.get("indicator_types", ["ALL"])
    max_fetch = params.get("max_indicator_to_fetch")
    tlp_color = params.get("tlp_color")
    tags = argToList(params.get("feedTags"))

    if max_fetch:
        max_fetch = arg_to_number(max_fetch)
    else:
        max_fetch = 500
    start = arg_to_number(params.get("score_start", 1))
    end = arg_to_number(params.get("score_end", 100)) + 1  # type:ignore
    score = None
    if start or end:
        score = [str(i) for i in range(start, end)]  # type:ignore
    try:
        client = OpenCTIApiClient(base_url, api_key, ssl_verify=params.get("insecure"), log_level="error", proxies=handle_proxy())
        command = demisto.command()
        demisto.info(f"Command being called is {command}")

        # Switch case
        if command == "fetch-indicators":
            fetch_indicators_command(client, indicator_types, max_fetch, tlp_color=tlp_color, score=score, tags=tags)

        elif command == "test-module":
            """When setting up an OpenCTI Client it is checked that it is valid and allows requests to be sent.
            and if not he immediately sends an error"""
            fetch_indicators_command(client, indicator_types, max_fetch, is_test=True)
            return_results("ok")

        elif command == "opencti-get-indicators":
            return_results(get_indicators_command(client, args))

        elif command == "opencti-reset-fetch-indicators":
            return_results(reset_last_run())

    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Error:\n [{e}]")


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