DHS Feed v2

The Cybersecurity and Infrastructure Security Agency’s (CISA’s) free Automated Indicator Sharing (AIS) capability enables the exchange of cyber threat indicators, at machine speed, to the Federal Government community.

Data Enrichment & Threat Intelligence · DHS Feed · Feed

Details

IDDHS Feed v2
ProviderDHS
CategoryData Enrichment & Threat Intelligence
From Version6.5.0
Docker Imagedemisto/taxii2:1.0.0.9117276
Supported ModulesAgentix XSIAM

README

The Cybersecurity and Infrastructure Security Agency’s (CISA’s) free Automated Indicator Sharing (AIS) capability enables the exchange of cyber threat indicators, at machine speed, to the Federal Government community.
Use this version if your certificate supports TAXII 2 protocol.

Some changes have been made that might affect your existing content.
If you are upgrading from a previous version of this integration, see Breaking Changes.

Configure DHS Feed v2 in Cortex

Parameter Description Required
Fetch indicators   False
Discovery Service URL (e.g., https://ais2.cisa.dhs.gov/taxii2/)   True
Key File as Text For more information, visit https://us-cert.cisa.gov/ais. True
Certificate File as Text For more information, visit https://us-cert.cisa.gov/ais. True
Default API Root to use The default API root to use (e.g., default, public). If left empty, the server default API root will be used. When the server has no default root, the first available API root will be used instead. The user must be authorized to reach the selected API root. False
Collection Name To Fetch Indicators From Indicators will be fetched from this collection. Run the “dhs-get-collections” command to get a valid value. If left empty, the instance will try to fetch from all the collections in the given discovery service. False
Indicator Reputation Indicators from this integration instance will be marked with this reputation. 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. False
Feed Fetch Interval   False
First Fetch Time The time interval for the first fetch (retroactive) in the following format: <number> <time unit> of type minute/hour/day. For example, 1 minute, 12 hour. Limited to 48 hours. False
STIX Objects To Fetch The objects to fetch, most likely indicators. Might slow down fetch time. False
Trust any certificate (not secure)   False
Use system proxy settings   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
Max Indicators Per Fetch The maximum number of indicators that can be fetched per fetch. If this field is left empty, there will be no limit on the number of indicators fetched. False
Max STIX Objects Per Poll Set the number of STIX objects that will be requested with each TAXII poll (http request). A single fetch is made of several TAXII polls. Changing this setting can help speed up fetches, or fix issues on slower networks. Please note server restrictions may apply, overriding and limiting the requested limit. False
Complex Observation Mode Choose how to handle complex observations. Two or more Observation Expressions MAY be combined using a complex observation operator such as “AND”, “OR”. For example, `[ IP = ‘b’ ] AND [ URL = ‘d’ ]` False
Tags Supports CSV values. 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.

dhs-get-indicators


Allows you to test your feed and to make sure you can fetch indicators successfully.
Due to API limitations, running this command may take longer than the default 5 minutes.
To overcome this issue increase the execution-timeout from 300 seconds to a higher value, the recommended value is 1800 seconds.

Base Command

dhs-get-indicators

Input

Argument Name Description Required
raw Will return only the rawJSON of the indicator object. Possible values are: true, false. Default is false. Optional
limit Maximum number of indicators to return. Default is 10. Optional
added_after Fetch only indicators that were added to the server after the given time. Provide a <number> and <time unit> of type minute/hour/day. For example, 1 minute, 12 hour, 24 days. Limited to 48 hours. Default is 24 hours. Optional

Context Output

Path Type Description
DHS.Indicators.value String Indicator value.
DHS.Indicators.type String Indicator type.
DHS.Indicators.rawJSON String Indicator rawJSON.

Command Example

!dhs-get-indicators limit=3 execution-timeout=1800

Context Example

{
  "DHS.Indicators": [
    {
      "fields": {
        "tags": [
          "cisa-proprietary-false"
        ]
      },
      "rawJSON": {
        "created": "2021-08-09T00:42:54.000Z",
        "created_by_ref": "identity--e8",
        "id": "indicator--e0",
        "indicator_types": [
          "anomalous-activity",
          "attribution"
        ],
        "labels": [
          "cisa-proprietary-false"
        ],
        "modified": "2021-09-26T04:16:13.000Z",
        "name": "sometimes",
        "object_marking_refs": [
          "marking-definition--633",
          "marking-definition--f51"
        ],
        "pattern": "[domain-name:value = 'coronashop.jp']",
        "pattern_type": "stix",
        "pattern_version": "2.1",
        "spec_version": "2.1",
        "type": "Domain",
        "valid_from": "2021-09-26T00:09:38Z",
        "value": "coronashop.jp"
      },
      "type": "Domain",
      "value": "coronashop.jp"
    },
    {
      "fields": {
        "description": "A totally famous IP Address",
        "tags": [
          "elevated"
        ]
      },
      "rawJSON": {
        "created": "2022-03-01T14:17:59.000Z",
        "created_by_ref": "identity--a9",
        "description": "A totally famous IP Address",
        "id": "indicator--2f5",
        "labels": [
          "elevated"
        ],
        "modified": "2022-03-01T14:17:59.000Z",
        "object_marking_refs": [
          "marking-definition--633"
        ],
        "pattern": "[ipv4-addr:value = '1.1.1.1']",
        "pattern_type": "stix",
        "spec_version": "2.1",
        "type": "IP",
        "valid_from": "2022-03-01T14:17:59.000000Z",
        "value": "1.1.1.1"
      },
      "type": "IP",
      "value": "1.1.1.1"
    },
    {
      "fields": {
        "tags": [
          "elevated"
        ]
      },
      "rawJSON": {
        "created": "2022-02-28T13:18:49.000Z",
        "created_by_ref": "identity--8c4",
        "id": "indicator--9a6",
        "indicator_types": [
          "file-hash-watchlist"
        ],
        "labels": [
          "elevated"
        ],
        "modified": "2022-02-28T13:18:49.000Z",
        "object_marking_refs": [
          "marking-definition--633"
        ],
        "pattern": "[file:hashes.MD5 = 'e6ecb146f469d243945ad8a5451ba1129c5b190f7d50c64580dbad4b8246f88e']",
        "pattern_type": "stix",
        "spec_version": "2.1",
        "type": "File",
        "valid_from": "2022-02-28T13:18:49.000000Z",
        "value": "e6ecb146f469d243945ad8a5451ba1129c5b190f7d50c64580dbad4b8246f88e"
      },
      "type": "File",
      "value": "e6ecb146f469d243945ad8a5451ba1129c5b190f7d50c64580dbad4b8246f88e"
    }
  ]
}

Human Readable Output

Found 3 results added after 2022-12-07T10:29:13.079493Z UTC:

DHS Indicators

value type
coronashop.jp Domain
1.1.1.1 IP
e6ecb146f469d243945ad8a5451ba1129c5b190f7d50c64580dbad4b8246f88e File

dhs-get-collections


Gets the list of collections from the discovery service.

Base Command

dhs-get-collections

Input

There are no input arguments for this command.

Context Output

Path Type Description
DHS.Collections.ID String Collection ID.
DHS.Collections.Name String Collection name.

Command Example


#### Context Example

```json
{
  "DHS.Collections": [
    {
      "ID": "3",
      "Name": "Public Collection"
    }
  ]
}

Human Readable Output

DHS Server Collections

Name ID
Public Collection 3

Breaking Changes

The following are the breaking changes from the previous version of this integration.

Arguments

The following argument was removed in this version

In the dhs-get-indicators command, tlp_colorwas removed.

The behavior of the following arguments was changed

In the dhs-get-indicators command, the default value of the limit argument was changed to ‘10’.

Outputs

The following outputs were removed in this version

In the dhs-get-indicators command:

  • DHS.type - this output was replaced by DHS.Indicators.type.
  • DHS.value - this output was replaced by DHS.Indicators.value.
  • DHS.tlp - this output was removed.

Additional Considerations for this version

Use this version if your certificate supports TAXII 2 protocol.

Known Limitations

“First Fetch Time” parameter can be configured for a maximum of 48 hours, due to limitations in DHS TAXII2 API.
Therefore, it is not possible to fetch indicators that last appeared in the feed more than 48 hours ago.

Configuration parameters

  • feed — Fetch indicators
  • url — Discovery Service URL (e.g., https://ais2.cisa.dhs.gov/taxii2/) (required)
  • key — (required)
  • certificate — Certificate File as Text (required)
  • default_api_root — Default API Root to use
  • collection_to_fetch — Collection Name To Fetch Indicators From
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability (required)
  • tlp_color — Traffic Light Protocol Color
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedFetchInterval — Feed Fetch Interval
  • initial_interval — First Fetch Time
  • objects_to_fetch — STIX Objects To Fetch
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • feedBypassExclusionList — Bypass exclusion list
  • limit — Max Indicators Per Fetch
  • limit_per_request — Max STIX Objects Per Poll
  • observation_operator_mode — Complex Observation Mode
  • feedTags — Tags
  • feedIncremental — Incremental Feed

Commands (2)

  • dhs-get-collections

    Gets the list of collections from the discovery service.

  • dhs-get-indicators

    Allows you to test your feed and to make sure you can fetch indicators successfully. Due to API limitations, this command may take a long time to run. Make sure the 'execution-timeout' argument is increased. See the integration readme for further information.

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401
from pytz import utc
from TAXII2ApiModule import *  # noqa: E402
from taxii2client.common import _ensure_datetime_to_string

""" CONSTANTS """

COMPLEX_OBSERVATION_MODE_SKIP = "Skip indicators with more than a single observation"
MAX_FETCH_INTERVAL = "48 hours"
DEFAULT_FETCH_INTERVAL = "24 hours"
DEFAULT_LIMIT_PER_REQUEST = 1000  # DHS default limit

""" HELPER FUNCTIONS """


def get_datetime(given_interval: Union[str, datetime]) -> datetime:
    """
    Receives an interval and returns the corresponding datetime.
    """
    if isinstance(given_interval, datetime):
        return given_interval
    date = dateparser.parse(given_interval, date_formats=[TAXII_TIME_FORMAT])
    if not date:
        raise DemistoException("Given time interval is not in a valid format.")
    return date.replace(tzinfo=utc)  # type: ignore[union-attr]


def get_limited_interval(
    given_interval: Union[str, datetime], fetch_interval: Optional[Union[str, datetime]] = MAX_FETCH_INTERVAL
) -> datetime:
    """
    Returns the closer time between the two time intervals given.
    """
    given_interval: datetime = get_datetime(given_interval)
    fetch_interval: datetime = get_datetime(fetch_interval or MAX_FETCH_INTERVAL)
    return max(given_interval, fetch_interval)  # later time is bigger


def fetch_one_collection(client: Taxii2FeedClient, limit: int, initial_interval: datetime, last_run_ctx: Optional[dict] = None):
    demisto.debug(f"Fetching collection {client.collection_to_fetch.id=}")
    last_fetch_time = last_run_ctx.get(client.collection_to_fetch.id) if last_run_ctx else None
    # initial_interval gets here limited so no need to check limitation with default value
    added_after: datetime = get_limited_interval(initial_interval, last_fetch_time)

    indicators = client.build_iterator(limit, added_after=added_after)

    if last_run_ctx is not None:  # in case we got {}, we want to set it because we are in fetch incident run
        last_run_ctx[client.collection_to_fetch.id] = _ensure_datetime_to_string(
            client.last_fetched_indicator__modified if client.last_fetched_indicator__modified else added_after
        )

    return indicators, last_run_ctx


def fetch_all_collections(client: Taxii2FeedClient, limit: int, initial_interval: datetime, last_run_ctx: Optional[dict] = None):
    indicators: list = []
    for collection in client.collections:  # type: ignore[attr-defined]
        client.collection_to_fetch = collection
        fetched_iocs, last_run_ctx = fetch_one_collection(client, limit, initial_interval, last_run_ctx)
        indicators.extend(fetched_iocs)

        if limit >= 0:
            limit -= len(fetched_iocs)
            if limit <= 0:
                break

    return indicators, last_run_ctx


""" COMMAND FUNCTIONS """


def command_test_module(client: Taxii2FeedClient, initial_interval: str):
    if get_datetime(MAX_FETCH_INTERVAL) > get_datetime(initial_interval):
        return 'Due to DHS API limitations, "First Fetch Time" is limited to 48 hours.'

    try:
        client.initialise()
        if client.collections:
            get_indicators_command(client, {"limit": "1", "added_after": get_limited_interval("6 hours", initial_interval)})
        else:
            return "Could not connect to server"
    except requests.exceptions.ConnectTimeout:
        return (
            "Connection Timeout Error - potential reasons might be that the 'Discovery Service URL' parameter"
            " is incorrect or that the server is not accessible from your host."
        )
    except requests.exceptions.SSLError:
        return (
            "SSL Certificate Verification Failed - try selecting 'Trust any certificate' checkbox in"
            " the instance configuration. If this doesn't work, verify that your certificate and key are valid  and matching."
        )
    except requests.exceptions.ProxyError:
        return (
            "Proxy Error - if the 'Use system proxy' checkbox in the integration configuration is"
            " selected, try clearing the checkbox."
        )
    except requests.exceptions.ConnectionError:
        return (
            "Verify that the server URL parameter is correct and that you have access to the server from your host."
            " Run the test again."
        )
    except requests.HTTPError:
        return (
            "HTTP error - check your certificate and key, and that you are trying to reach a valid URL and API root."
            " Wait and run the test again."
        )

    return "ok"


def fetch_indicators_command(
    client: Taxii2FeedClient, limit: int, last_run_ctx: dict, initial_interval: str = DEFAULT_FETCH_INTERVAL
) -> tuple[list, dict]:
    """
    Fetch indicators from TAXII 2 server
    :param client: Taxii2FeedClient
    :param limit: upper limit of indicators to fetch
    :param last_run_ctx: last run dict with {collection_id: last_run_time string}
    :param initial_interval: initial interval in human readable format
    :return: indicators in cortex TIM format, updated last_run_ctx
    """
    initial_interval: datetime = get_limited_interval(get_datetime(initial_interval or DEFAULT_FETCH_INTERVAL))

    if client.collection_to_fetch:
        indicators, last_run_ctx = fetch_one_collection(client, limit, initial_interval, last_run_ctx)  # type: ignore[arg-type]
    else:
        indicators, last_run_ctx = fetch_all_collections(client, limit, initial_interval, last_run_ctx)  # type: ignore[arg-type]

    return indicators, last_run_ctx


def get_indicators_command(
    client: Taxii2FeedClient, args: Dict[str, Any]
) -> Union[CommandResults, Dict[str, List[Optional[str]]]]:
    """
    Fetch indicators from TAXII 2 server
    :param client: Taxii2FeedClient
    :param args: Dict that holds
        raw: When set to 'true' will return only rawJSON
        limit: upper limit of indicators to fetch
        added_after: added after time string in parse_date_range format
    :return: indicators in cortex TIM format
    """
    limit = arg_to_number(args.get("limit")) or 10
    raw = argToBoolean(args.get("raw", "false"))
    max_fetch_datetime = get_datetime(MAX_FETCH_INTERVAL)
    added_after: datetime = get_datetime(args.get("added_after", DEFAULT_FETCH_INTERVAL))
    if max_fetch_datetime > added_after:
        raise DemistoException('Due to DHS API limitations, "added_after" is limited to 48 hours.')

    if client.collection_to_fetch:
        indicators = client.build_iterator(limit, added_after=added_after)
    else:
        indicators, _ = fetch_all_collections(client, limit, added_after)  # type: ignore[arg-type]

    if raw:
        return {"indicators": [x.get("rawJSON") for x in indicators]}

    return CommandResults(
        readable_output=f"Found {len(indicators)} results added after {_ensure_datetime_to_string(added_after)} UTC:\n"
        + tableToMarkdown(name="DHS Indicators", t=indicators, headers=["value", "type"], removeNull=True),
        outputs_prefix="DHS.Indicators",
        outputs_key_field="value",
        outputs=indicators,
        raw_response=indicators,
    )


def get_collections_command(client: Taxii2FeedClient) -> CommandResults:
    """
    Get the available collections in the DHS server
    """
    collections = []
    for collection in client.collections:  # type: ignore[attr-defined]
        collections.append({"Name": collection.title, "ID": collection.id})
    return CommandResults(
        readable_output=tableToMarkdown("DHS Server Collections", t=collections, headers=["Name", "ID"]),
        outputs_prefix="DHS.Collections",
        outputs_key_field="ID",
        outputs=collections,
    )


""" MAIN FUNCTION """


def main():  # pragma: no cover
    params = demisto.params()
    url = params.get("url", "https://ais2.cisa.dhs.gov/taxii2/")
    key = params.get("key", {}).get("password")
    certificate = params.get("certificate")
    verify_certificate = not params.get("insecure", False)
    proxies = handle_proxy()

    collection_to_fetch = params.get("collection_to_fetch")
    skip_complex_mode = params.get("observation_operator_mode") == COMPLEX_OBSERVATION_MODE_SKIP
    feed_tags = argToList(params.get("feedTags"))
    tlp_color = params.get("tlp_color", "")
    objects_to_fetch = params.get("objects_to_fetch", [])

    initial_interval = params.get("initial_interval", DEFAULT_FETCH_INTERVAL)
    limit = arg_to_number(params.get("limit", -1)) or -1
    limit_per_request = arg_to_number(params.get("limit_per_request", DEFAULT_LIMIT_PER_REQUEST)) or DEFAULT_LIMIT_PER_REQUEST
    default_api_root = params.get("default_api_root", "public")

    command = demisto.command()
    demisto.info(f"Command being called is {command}")

    try:
        client = Taxii2FeedClient(
            url=url,
            collection_to_fetch=collection_to_fetch,
            proxies=proxies,
            verify=verify_certificate,
            objects_to_fetch=objects_to_fetch,
            skip_complex_mode=skip_complex_mode,
            tags=feed_tags,
            limit_per_request=limit_per_request,
            tlp_color=tlp_color,
            certificate=certificate,
            key=key,
            default_api_root=default_api_root,
        )

        start_time = time.time()
        if command == "test-module":
            return_results(command_test_module(client, initial_interval))

        elif command == "fetch-indicators":
            client.initialise()
            last_run_indicators = demisto.getLastRun()
            indicators, last_run_indicators = fetch_indicators_command(client, limit, last_run_indicators, initial_interval)
            for iter_ in batch(indicators, batch_size=2000):
                demisto.createIndicators(iter_)

            demisto.setLastRun(last_run_indicators)

        elif command == "dhs-get-indicators":
            client.initialise()
            return_results(get_indicators_command(client, demisto.args()))

        elif command == "dhs-get-collections":
            client.initialise()
            return_results(get_collections_command(client))

        else:
            raise NotImplementedError(f"{command} command is not implemented.")

        demisto.debug(f"Running {command} took {round(time.time() - start_time)}sec")

    except Exception as error:
        error_msg = str(error)
        if isinstance(error, requests.exceptions.SSLError):
            error_msg = (
                "Encountered an HTTPS certificate error. This error can be ignored by enabling "
                '"Trust any certificate (not secure)" in the instance configuration.'
            )
        elif isinstance(error, requests.HTTPError):
            error_msg = (
                "Encountered an HTTP error. Please check your certificate and key, and that you are trying to reach a "
                'valid URL and API root. If this occurs when the test works, increase the "Max STIX Objects Per Poll" '
                'in the instance configuration, reduce the "Max Indicators Per Fetch" in the instance configuration or '
                'reduce the "limit" in the command argument.'
            )
        return_error(error_msg, error)


""" ENTRY POINT """

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