Inception

Stairwell Inception is a security intelligence engine that automates the continuous capture, storage, and of executable files and other primary security artifacts to improve detection and response against advanced attacks that evade traditional security tools.

Data Enrichment & Threat Intelligence · Stairwell

Details

IDInception
ProviderStairwell
CategoryData Enrichment & Threat Intelligence
From Version6.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Use the Stairwell Inception integration to enrich data in XSOAR using Stairwell’s knowledge and perform automated variant discovery.

Not a customer and interested in signing up? You can request access here.

Generate required API key

Follow these steps for a self-deployed configuration.

  1. Access the Inception web UI and generate a API/CLI token here.
  2. Copy your API token for the integration configuration usage.

Configure Stairwell Inception in Cortex

Parameter Required
API Key True
Use system proxy settings False
Trust any certificate (not secure) 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.

inception-file-enrichment


Enrich files using file hash (MD5, SHA1, SHA256) with Stairwell’s knowledge.

Base Command

inception-file-enrichment

Input

Argument Name Description Required
fileHash File hash (MD5, SHA1, SHA256) to lookup. Required

Context Output

Path Type Description
Inception.File_Details Dict Raw JSON output from API

Command Example

!inception-file-enrichment fileHash=9fe1ac46f0cdebf03156a6232d771c14559f8daf

Context Example

{
 "inception": {
  "file_details": {
   "type": "file",
   "id": "e7762f90024c5366807c7c145d3456f0ac3be086c0ec3557427d3c2c10a2052d",
   "links": {
    "self": "/api/v3/files/e7762f90024c5366807c7c145d3456f0ac3be086c0ec3557427d3c2c10a2052d"
   },
   "data": {
    "attributes": {
     "md5": "00ddbafe247c891eed36bd74f66f936b",
     "sha1": "9fe1ac46f0cdebf03156a6232d771c14559f8daf",
     "sha256": "e7762f90024c5366807c7c145d3456f0ac3be086c0ec3557427d3c2c10a2052d",
     "size": 118024,
     "creation_date": 1653722032,
     "last_analysis_results": {
      "ClamAV": {
       "category": "undetected",
       "engine_name": "ClamAV",
       "result": "undetected"
      },
      "Engine2": {
       "category": "malicious",
       "engine_name": "Engine2",
       "result": "Trojan/Win.Hermeticwiper"
      }
     },
     "last_analysis_stats": {
      "harmless": 1,
      "malicious": 1
     },
     "names": [
      "C:\\GimmeCreds.dll"
     ],
     "meaningful_name": "C:\\GimmeCreds.dll",
     "type_description": "EXE",
     "crowdsourced_yara_results": [{
       "rule_name": "wiper_HermeticWiper"
      },
      {
       "rule_name": "MAL_HERMETIC_WIPER"
      }
     ],
     "inception": {
      "environments": [{
       "environment_id": {
        "id": "AAAAAA-BBBBBB-CCCCCC-DDDDDDD"
       }
      }],
      "assets": [{
       "asset_id": {
        "id": "DDDDDD-CCCCCC-BBBBBB-AAAAAAA"
       },
       "name": "WORKGROUP\\IDABEAR"
      }]
     },
     "magic": "EXE",
     "imphash": "fe4a2284122da348258c83ef437fbd7b",
     "ssdeep": "1536:WBOoa7Nn54urilmw9BgjKu1sPPxaS4NOyqC:WBOoa7P4xlPwV16PkS4NVqC"
    }
   }
  }
 }
}

Human Readable Output

Stairwell Inception

MD5: 00ddbafe247c891eed36bd74f66f936b
SHA256: e7762f90024c5366807c7c145d3456f0ac3be086c0ec3557427d3c2c10a2052d
Seen Assets: 1
Matching YARA Intel: wiper_HermeticWiper,MAL_HERMETIC_WIPER

AV Scanning Results

Engine Name Result
ClamAV undetected
Engine2 Trojan/Win.Hermeticwiper

inception-variant-discovery


Hunt for variants using a SHA256 across all files you have access to, including your environments and Stairwell’s malware feeds.

Base Command

inception-variant-discovery

Input

Argument Name Description Required
sha256 SHA256 of file to hunt for variants on. Required

Context Output

Path Type Description
Inception.Variants Dict Raw JSON output from API

Command Example

!inception-variant-discovery sha256=30e27357b7b773b226d4ee638e17b19b954226d197b0781822859269a5c22b4d

Context Example

{
 "inception": {
  "variants": {
   "name": "variants/30e27357b7b773b226d4ee638e17b19b954226d197b0781822859269a5c22b4d",
   "variants": [{
     "similarity": 1,
     "sha256": "e1a00d8923bac6f863c262236f15eb60d80571f8b31e7220c4b2912fae7e9a14"
    },
    {
     "similarity": 0.9875,
     "sha256": "d2a00d8923bac6f863c262236f15eb60d80571f8b31e7220c4b2912fae7e9a12"
    }
   ],
   "variant_count": 2,
   "original_object": "30e27357b7b773b226d4ee638e17b19b954226d197b0781822859269a5c22b4d"
  }
 }
}

Human Readable Output

File Variants Discovered

sha256 similarity
e1a00d8923bac6f863c262236f15eb60d80571f8b31e7220c4b2912fae7e9a14 1
d2a00d8923bac6f863c262236f15eb60d80571f8b31e7220c4b2912fae7e9a12 0.9875

Configuration parameters

  • apikey — (required)
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)

Commands (2)

  • inception-file-enrichment

    Enrich files using file hash (MD5, SHA1, SHA256).

  • inception-variant-discovery

    Discover variants using a file hash (SHA256).

import json

import demistomock as demisto
import urllib3
from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import

from CommonServerUserPython import *  # noqa # pylint: disable=unused-wildcard-import

urllib3.disable_warnings()  # pylint: disable=no-member


class Client(BaseClient):
    def get_file_reputation(self, file_hash: str) -> Dict[str, Any]:
        return self._http_request(method="GET", url_suffix=file_hash)

    def get_file_variants(
        self,
        file_hash: str,
    ) -> Dict[str, Any]:
        return self._http_request(method="GET", url_suffix=file_hash)


def test_module(client):  # pragma: no cover
    try:
        # We'll use a default file hash, accessible by all, to test the connection
        response = client.get_file_reputation("e7762f90024c5366807c7c145d3456f0ac3be086c0ec3557427d3c2c10a2052d")
        response = json.dumps(response)
        if "attributes" in response:
            return "ok"
    except Exception:
        return "Authorization Error: make sure API Key is correctly set"


def file_enrichment_command(client, file_hash):
    try:
        responseJson = client.get_file_reputation(file_hash)
        if "attributes" in responseJson["data"]:
            md = "# Stairwell Inception\n"
            file_md5 = responseJson["data"]["attributes"]["md5"]
            file_sha1 = responseJson["data"]["attributes"]["sha1"]
            file_sha256 = responseJson["data"]["attributes"]["sha256"]
            md += f"MD5: {file_md5}\n"
            md += f"SHA1: {file_sha1}\n"
            md += f"SHA256: {file_sha256}\n"

            # List the filename(s) if present
            if responseJson["data"]["attributes"]["names"]:
                filenames = []
                for ind in responseJson["data"]["attributes"]["names"]:
                    if "\\" in ind:
                        full_path = ind.split("\\")
                        filename = full_path[-1].lower()
                        filenames.append(filename) if filename not in filenames else filenames
                    elif "/" in ind:
                        full_path = ind.split("/")
                        filename = full_path[-1].lower()
                        filenames.append(filename) if filename not in filenames else filenames
                filenames_string = ", ".join([str(x) for x in filenames])
                md += f"Filename(s): {filenames_string}\n"

            # Count the number of assets if there are seen assets
            if responseJson["data"]["attributes"]["occurrences"]:
                seen_envs = len(responseJson["data"]["attributes"]["occurrences"])
                # Orgs may have multiple environments, especially partners
                if seen_envs > 1:
                    env_list = []
                    for indEnv in responseJson["data"]["attributes"]["occurrences"]:
                        asset_count = len(indEnv["assets"])
                        envName = indEnv["environment"]["name"]
                        seen_str = envName + "(" + str(asset_count) + ")"
                        env_list.append(seen_str)
                    env_string = ", ".join([str(x) for x in env_list])
                    md += f"Seen_Assets: {env_string}\n"
                else:
                    seen_assets = len(responseJson["data"]["attributes"]["occurrences"][0]["assets"])
                    seen_env = responseJson["data"]["attributes"]["occurrences"][0]["environment"]["name"]
                    md += f"Seen Assets: {seen_env}({seen_assets})\n"

            # Show matching YARA intelligence if present
            if responseJson["data"]["attributes"]["crowdsourced_yara_results"]:
                yara_rules = []
                for yara in responseJson["data"]["attributes"]["crowdsourced_yara_results"]:
                    yara_rules.append(yara["rule_name"])
                yara_string = ", ".join([str(x) for x in yara_rules])
                md += f"Matching YARA Intel: {yara_string}\n"

            # Show Mal-Eval result
            mal_eval_output = responseJson["data"]["attributes"]["mal_eval_result"]
            if mal_eval_output:
                if mal_eval_output["label"]:
                    mal_eval_label = mal_eval_output["label"]
                    md += f"Mal-Eval Label: {mal_eval_label}\n"
                if mal_eval_output["probability_bucket"]:
                    mal_eval_prob = mal_eval_output["probability_bucket"]
                    md += f"Mal-Eval Malicious Likelihood: {mal_eval_prob}\n"
            results = CommandResults(
                readable_output=md,
                outputs_prefix="Inception.File_Details",
                outputs=responseJson,
            )
            return results
    except DemistoException as err:
        # API will return 404 if the file is not found
        if "404" in str(err):
            results = CommandResults(readable_output="File not found: " + file_hash)
            return results
        else:
            raise err


def variant_discovery_command(client, file_hash):
    try:
        response = client.get_file_reputation(file_hash)
        response = json.dumps(response)
        if "similarity" in response:
            response_json = json.loads(response)
            md_string = tableToMarkdown("File Variants Discovered", response_json["variants"])
            results = CommandResults(
                outputs_prefix="Inception.Variants",
                readable_output=md_string,
                outputs=response_json,
            )
            return results
        elif "variants" in response:
            results = CommandResults(readable_output="No variants discovered for: " + file_hash)
            return results
    except DemistoException as err:
        # API will return 500 if the file is not found
        if "500" in str(err):
            results = CommandResults(readable_output="File not found: " + file_hash)
            return results
        else:
            raise err


def main() -> None:  # pragma: no cover
    params = demisto.params()
    args = demisto.args()
    command = demisto.command()

    api_key = params.get("apikey", {}).get("password")

    # Params enabled by XSOAR functionality
    verify_certificate = not params.get("insecure", False)
    proxy = params.get("proxy", False)

    demisto.debug(f"Command being called is {command}")
    try:
        # Built-in command
        if command == "test-module":
            # This is the call made when clicking the integration Test button.
            client = Client(
                base_url="https://reputation.app.stairwell.com/api/v3/files/",
                verify=verify_certificate,
                headers={"X-Apikey": api_key},
                proxy=proxy,
            )
            result = test_module(client)
            return_results(result)

        elif command == "inception-file-enrichment":
            client = Client(
                base_url="https://reputation.app.stairwell.com/api/v3/files/",
                verify=verify_certificate,
                headers={"X-Apikey": api_key},
                proxy=proxy,
            )
            result = file_enrichment_command(client, args.get("fileHash"))
            return_results(result)

        elif command == "inception-variant-discovery":
            client = Client(
                base_url="https://app.stairwell.com/v202112/variants/",
                verify=verify_certificate,
                headers={"Authorization": api_key},
                proxy=proxy,
                timeout=120,
            )
            result = variant_discovery_command(client, args.get("sha256"))
            return_results(result)

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

    # Log exceptions and return errors
    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error("\n".join(("Failed to execute {command} command.", "Error:", str(e))))


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