OpenCTI
Two OpenCTI connectors for Stairwell. One enriches files, domains, IPs and ASNs on demand; the other imports the files Stairwell judges malicious as indicators on a schedule.
Stairwell publishes two connectors for OpenCTI, the open-source threat intelligence platform. The Stairwell enrichment connector adds Stairwell's verdict, AI triage, variants, and infrastructure history to a file, domain, IP address, or autonomous system already in OpenCTI. The Stairwell Import connector brings the files Stairwell has judged malicious into OpenCTI as indicators, on a schedule you set. Both are part of the official OpenCTI connector catalog and run as containers beside your platform.
You can run either one on its own. Most teams start with enrichment, because it answers questions about indicators they already hold, and add the import once they know how much volume they want flowing in.
What does each connector do?
| Stairwell (enrichment) | Stairwell Import | |
|---|---|---|
| OpenCTI type | Internal enrichment | External import |
| Runs | When an analyst or a playbook asks, or automatically on new observables if you turn that on | On a schedule, once a day by default |
| Works on | Files, domain names, IPv4 and IPv6 addresses, autonomous systems | The files Stairwell judges malicious that your token can see |
| Adds to OpenCTI | A score, labels, and notes on the observable, plus related variants, domains, and IP addresses | File hash indicators, with the domains, IP addresses, and URLs found in each file as related observables |
What does enrichment add to a file?
- A score from the file's MalEval verdict. Enrichment only ever raises an observable's score, never lowers it, so a higher score set by another source is kept.
- Labels for the verdict's severity, matched YARA rules, the malware family when Stairwell names one, and your own Stairwell tags (prefixed
stairwell:tag:). - Notes: a Stairwell Enrichment Summary with the file's metadata, prevalence, and signing certificates; a Stairwell AI File Triage note with the AI Triage assessment; and a Stairwell Variants note.
- Variants as related file observables, linked with
derived-from, up to 25 per file by default. See Variants. - Sightings, only if you turn them on. Read Should I turn on sightings? first.
A hash Stairwell has never seen gets a stairwell:not-found label and a link to the Stairwell search, and nothing else.
What does enrichment add to a domain, IP address, or ASN?
A domain gets a Stairwell DNS History note with up to 50 resolutions, and the addresses it resolved to as related observables. An IP address gets a Stairwell IP Intelligence note with its WHOIS record and the hostnames that resolve to it. An autonomous system gets a Stairwell ASN WHOIS note. This is the same history described in Network Intelligence.
What do I need before I start?
- OpenCTI 7.261109.0 or later. The Stairwell connector images are published from that release onward.
- A Stairwell auth token. Both connectors can share one, or you can give each its own so you can revoke them separately.
- An OpenCTI API token for the user the connectors will act as.
- A UUID for each connector instance. Any UUIDv4 generator will do, for example
uuidgenon macOS or Linux.
How do I create the Stairwell token?
- In Stairwell, open Settings.
- Under Organization, select Auth tokens.
- Create a token and copy it. It is shown once.
Quick Start: API Access covers the same steps in more detail. Store the token where your OpenCTI deployment keeps its other secrets, not in a compose file you commit.
How do I deploy the connectors?
If your OpenCTI deployment manages connectors from the platform, both appear in the connector catalog as Stairwell and Stairwell Import, and you fill in the same settings described below in the catalog's form.
Otherwise, add them to the Docker Compose file that runs your platform. Use the image tag that matches your OpenCTI version, as you would for any OpenCTI connector.
Enrichment
connector-stairwell:
image: opencti/connector-stairwell:7.261109.0
environment:
- OPENCTI_URL=http://opencti:8080
- OPENCTI_TOKEN=<your OpenCTI API token>
- CONNECTOR_ID=<a new UUIDv4>
- STAIRWELL_API_TOKEN=<your Stairwell auth token>
restart: always
depends_on:
opencti:
condition: service_healthyImport
connector-stairwell-import:
image: opencti/connector-stairwell-import:7.261109.0
environment:
- OPENCTI_URL=http://opencti:8080
- OPENCTI_TOKEN=<your OpenCTI API token>
- CONNECTOR_ID=<a different UUIDv4>
- STAIRWELL_IMPORT_API_TOKEN=<your Stairwell auth token>
- STAIRWELL_IMPORT_FIRST_RUN_WINDOW=P7D
restart: always
depends_on:
opencti:
condition: service_healthyOPENCTI_URL is the address of the platform as the connector container sees it. In the standard OpenCTI Docker deployment that is http://opencti:8080. Each connector instance needs its own CONNECTOR_ID.
Then start them:
docker compose up -d connector-stairwell connector-stairwell-importHow do I know it's working?
Shortly after they start, both connectors appear in OpenCTI under Data, then Ingestion, then Connectors.
To test enrichment, open any file observable, select Enrichment, and choose Stairwell. When it finishes, the observable has a score, labels, and the notes described above. If it gets a stairwell:not-found label instead, the connector is working and Stairwell has no record of that hash.
The import makes its first run shortly after it starts, then runs on its schedule. Each run lands under its own Grouping, named Stairwell MalEval import with the run's date and time, so you can see exactly what one batch brought in. A run that finds nothing new produces no Grouping.
If nothing appears, read the connector's log (docker compose logs connector-stairwell). A setting that is out of range stops the connector at startup with a message that names it.
How do I configure the enrichment connector?
Set these as environment variables, or in a config.yml beside the connector. Environment variables win.
| Variable | Default | What it does |
|---|---|---|
OPENCTI_URL | (required) | The OpenCTI platform address |
OPENCTI_TOKEN | (required) | The OpenCTI API token |
CONNECTOR_ID | (required) | A UUIDv4 unique to this instance |
STAIRWELL_API_TOKEN | (required) | Your Stairwell auth token |
CONNECTOR_NAME | Stairwell | The name shown in OpenCTI |
CONNECTOR_SCOPE | all five types | Which of StixFile, Domain-Name, IPv4-Addr, IPv6-Addr, Autonomous-System to enrich |
CONNECTOR_AUTO | false | Enrich every new observable in scope as it is created |
CONNECTOR_MAX_TLP | amber | The highest TLP an observable can carry and still be sent to Stairwell |
CONNECTOR_LOG_LEVEL | error | debug, info, warn, or error |
STAIRWELL_DEFAULT_TLP | amber | The TLP marking on everything the connector writes |
STAIRWELL_VARIANT_LIMIT | 25 | Most variants added per file. 0 turns variants off |
STAIRWELL_RESOLUTIONS_LIMIT | 50 | Most DNS resolutions per domain. 0 turns DNS history off |
STAIRWELL_SIGHTINGS_LIMIT | 0 | Most assets added as sightings per file. 0, the default, turns sightings off |
Should I turn on automatic enrichment?
Only if the volume of new observables in your OpenCTI instance is modest. With CONNECTOR_AUTO=true, every in-scope observable any connector creates is sent to Stairwell, including the domains and addresses a busy feed brings in by the thousand. The default, enrichment on request and from playbooks, keeps the lookups to the observables someone cares about.
What does CONNECTOR_MAX_TLP protect?
CONNECTOR_MAX_TLP protect?It decides what is allowed to leave OpenCTI. An observable marked above this level is never sent to Stairwell: the connector refuses it, logs why, and makes no request. An observable with no TLP marking is treated as unrestricted. In a playbook, the markings on the step's input are checked too. Set it to the level your sharing agreements allow you to query an outside service with.
Should I turn on sightings?
Sightings tell you which of your hosts have seen a file, which is often the first thing an analyst wants to know. They are off by default because turning them on writes data about your fleet into OpenCTI.
With STAIRWELL_SIGHTINGS_LIMIT above 0, each host that has seen the file becomes a Sighting in OpenCTI, with first seen, last seen, and a count, and an Identity for the host. That puts your asset names, Stairwell environment IDs, and sample file paths, which often contain usernames, into OpenCTI, marked with STAIRWELL_DEFAULT_TLP and visible to everyone who can read the file. Turn it on when everyone with access to your OpenCTI instance is entitled to see that. See Sightings for what a sighting means in Stairwell.
How do I configure the import connector?
| Variable | Default | What it does |
|---|---|---|
OPENCTI_URL | (required) | The OpenCTI platform address |
OPENCTI_TOKEN | (required) | The OpenCTI API token |
CONNECTOR_ID | (required) | A UUIDv4 unique to this instance |
STAIRWELL_IMPORT_API_TOKEN | (required) | Your Stairwell auth token |
CONNECTOR_NAME | Stairwell Import | The name shown in OpenCTI |
CONNECTOR_DURATION_PERIOD | P1D | Time between runs, as an ISO 8601 duration. PT6H is every six hours |
CONNECTOR_LOG_LEVEL | error | debug, info, warn, or error |
STAIRWELL_IMPORT_FIRST_RUN_WINDOW | P1D | How far back the first run reaches. P30D imports the last 30 days |
STAIRWELL_IMPORT_MIN_BUCKET | HIGH | The lowest verdict confidence imported: LOW, MEDIUM, HIGH, or VERY_HIGH |
STAIRWELL_IMPORT_MAX_INDICATORS | 1000 | About how many files one run imports. The rest wait for the next run |
STAIRWELL_IMPORT_TLP | green | The TLP marking on everything imported |
STAIRWELL_IMPORT_INDICATOR_VALIDITY_DAYS | 90 | How long each indicator stays valid |
STAIRWELL_IMPORT_WRAPPER | grouping | Put each run under a grouping or a report |
STAIRWELL_IMPORT_PAGE_SIZE | 100 | Files requested from Stairwell per page |
How do I choose the threshold and the first-run window?
Start with the defaults and watch a few runs. HIGH imports files Stairwell is confident are malicious, which is the right population for indicators that may end up on a blocklist. Lower it to MEDIUM if you want a wider net for hunting and your downstream consumers can tolerate more review.
The first-run window decides how much history you bring in on day one. A large window is safe: when a run reaches STAIRWELL_IMPORT_MAX_INDICATORS, it stops and the next run picks up where it left off, so a 30-day backfill arrives over several runs rather than all at once. Nothing is skipped and nothing is imported twice.
Why are the domains and IP addresses not indicators?
Because malware contains a lot of infrastructure that is not malicious. A file's strings include operating system update hosts, public DNS resolvers, certificate authorities, and XML namespaces alongside its command and control. The import makes each file hash an indicator, and links the domains, IP addresses, and URLs found in that file to it as observables. Promoting them to indicators would put that benign infrastructure on any blocklist fed from OpenCTI.
Both connectors also leave out values that would only be noise: private, loopback, link-local, multicast, and other addresses that are not publicly routable, and strings that look like hostnames but are not, such as version numbers and file names. A DNS answer left out this way still appears in the domain's DNS History note.
What does the import not pick up?
The import reads files by when Stairwell first saw them. A file first seen in a window that the import already read, whose verdict reached your threshold only afterwards, is not imported later. For a file you already have in OpenCTI, run the enrichment connector on it: enrichment always reflects the current verdict.
Troubleshooting
The connector stops at startup with a configuration error. The message names the setting. Durations must be ISO 8601 (P7D, PT12H), TLP values must be one of clear, white, green, amber, amber+strict, or red, and limits must be whole numbers no lower than their minimum.
Every enrichment fails with an authorization error. The Stairwell token is wrong, revoked, or belongs to a user without access to the data. Create a new one and replace it.
Enrichment completes but nothing changes. Check the connector log for a TLP refusal: the observable may be marked above CONNECTOR_MAX_TLP. Also check that its type is in CONNECTOR_SCOPE.
The import ran but brought in fewer files than expected. Check STAIRWELL_IMPORT_MIN_BUCKET and the first-run window, then look at the next run. A large backlog arrives in batches of about STAIRWELL_IMPORT_MAX_INDICATORS.
For anything else, contact Stairwell support with the connector log at debug level.
Updated less than a minute ago