OpenCTI

Overview

Description

OpenCTI is an opensource Threat Intelligence Platform ("TIP") that can be used by individuals and organizations to collect, track, correlate, visualize, and consume threat intelligence artifacts that have been observed by them, i.e. self-generated, or collected from others, e.g. OS-INT, peering, or commercial feeds. The data is/can be structured around the MITRE ATT&CK framework for an easy way to visualize and compare patterns among adversaries or between an arbitrary attacker and a company's defenses.

Created by the Agence nationale de la sécurité des systèmes d'information, it is now developed and maintained by Filigran.

Components

OpenCTI Architecture

From the official documentation:

OpenCTI architecture diagram
Figure 1: A diagram showing the dataflow architecture in OpenCTI

Running OpenCTI in Docker

Setup

Getting up and running with OpenCTI using #Docker is quick and easy (usage and configuration of Docker itself is out of scope for this article). Essentially, there are four components you need to be aware of and manage:

  1. The directory where the configuration files reside. Normally, you may create this by git cloneing the docker-focused repository under openCTI-Platform.
  2. The directory where you want the data to be stored (a fast drive with plenty of space)
  3. The .env file that will manage environment variables for the OpenCTI instance
  4. the docker-compose.yml file with specific declarations for the system, workers, and all the connectors.
UUIDv4 identifiers

Pretty much every component in OpenCTI requires a UUIDv4 unique identifier.
These can be quickly generated at https://www.uuidgenerator.net/version4.

Create the data directories

In order to persist the threat intel, we store it outside of the running containers. This step creates the directories referenced by the docker-compose.yml file.

sudo mkdir -p /opt/opencti
sudo mkdir -p /data/opencti/esdata
sudo mkdir -p /data/opencti/s3data
sudo mkdir -p /data/opencti/redisdata
sudo mkdir -p /data/opencti/amqpdata

The .env configuration file

Create a new file at /opt/opencti/.env and copy the following settings to it. Change the parameters listed as CHANGEME.

OPENCTI_ADMIN_EMAIL=me@myplace.com
OPENCTI_ADMIN_PASSWORD=12SecretWords
OPENCTI_ADMIN_TOKEN=508f2261-cf67-431e-863f-a811b7819ef4
MINIO_ACCESS_KEY=asdfniuy78*&shdfadf
MINIO_SECRET_KEY=asdfjn(&sdfn
RABBITMQ_DEFAULT_USER=rmquser
RABBITMQ_DEFAULT_PASS=rmqpa88k1te3O
CONNECTOR_HISTORY_ID=23b17c40-e0c3-4552-bb7a-aff3903032d8
CONNECTOR_EXPORT_FILE_STIX_ID=e86e512b-9073-41eb-9165-5fc362be2d3d
CONNECTOR_EXPORT_FILE_CSV_ID=15670d8d-77e7-4f84-99af-d71e701bbfcb
CONNECTOR_IMPORT_FILE_STIX_ID=6d766fbe-f7b9-4d7d-b4f6-7f26e15692a1
CONNECTOR_IMPORT_FILE_PDF_OBSERVABLES_ID=8c452498-30d7-4d23-804c-733c3aa7c89a
MISP_CONNECTOR_ID=0920e90a-0e55-41ea-89c7-1998d831ab8b

Users and Passwords

The usernames / admin email, the passwords, and access keys are arbitrary and can be whatever you want

The docker-compose.yml launch file

Create a new file at /opt/opencti/docker-compose.yml and copy the following configuration into it. Change the parameters listed as CHANGEME.

It's recommended that you start with the file from the official repository and work from there. Each connector has its own docker compose declarations that can be found in the respective entries under the OpenCTI ecosystem. These can be dropped straight into your docker-compose.yml .

Notice near the bottom where I have specified the directory on the host where the data should reside. This is not necessarily recommended, just how I like to manage my instance.

the example below is quite old.

At this point, these notes were written quite a long time ago and the compose file below should be used as an example only and not copied into a new instance.

version: '3'
services:
  redis:
    image: redis:6.0.12
    restart: always
    volumes:
      - redisdata:/data
  elasticsearch:
#    image: docker.elastic.co/elasticsearch/elasticsearch:7.10.1
    image: docker.elastic.co/elasticsearch/elasticsearch:7.11.2
    volumes:
      - esdata:/usr/share/elasticsearch/data
    environment:
      - discovery.type=single-node
	  - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    restart: always
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
  minio:
#    image: minio/minio:RELEASE.2020-12-12T08-39-07Z
    image: minio/minio:RELEASE.2021-03-10T05-11-33Z
    volumes:
      - s3data:/data
    ports:
      - "9000:9000"
    environment:
      MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
      MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
    command: server /data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3
    restart: always
  rabbitmq:
    image: rabbitmq:3.8-management
    environment:
      - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}
    volumes:
      - amqpdata:/var/lib/rabbitmq
    restart: always
  opencti:
    image: opencti/platform:4.3.0
    environment:
      - APP__PORT=8080
      - APP__ADMIN__EMAIL=${OPENCTI_ADMIN_EMAIL}
      - APP__ADMIN__PASSWORD=${OPENCTI_ADMIN_PASSWORD}
      - APP__ADMIN__TOKEN=${OPENCTI_ADMIN_TOKEN}
      - APP__LOGS_LEVEL=error
      - APP__LOGS=./logs
      - APP__REACTIVE=true
      - APP__COOKIE_SECURE=false
      - REDIS__HOSTNAME=redis
      - REDIS__PORT=6379
      - ELASTICSEARCH__URL=http://elasticsearch:9200
      - MINIO__ENDPOINT=minio
      - MINIO__PORT=9000
      - MINIO__USE_SSL=false
      - MINIO__ACCESS_KEY=${MINIO_ACCESS_KEY}
      - MINIO__SECRET_KEY=${MINIO_SECRET_KEY}
      - RABBITMQ__HOSTNAME=rabbitmq
      - RABBITMQ__PORT=5672
      - RABBITMQ__PORT_MANAGEMENT=15672
      - RABBITMQ__MANAGEMENT_SSL=false
      - RABBITMQ__USERNAME=${RABBITMQ_DEFAULT_USER}
      - RABBITMQ__PASSWORD=${RABBITMQ_DEFAULT_PASS}
      - PROVIDERS__LOCAL__STRATEGY=LocalStrategy
    ports:
      - "8080:8080"
    depends_on:
      - redis
      - elasticsearch
      - minio
      - rabbitmq
    restart: always

  worker1:
    image: opencti/worker:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=c5697196-ebf5-499a-a287-388eeffe4062
      - WORKER_LOG_LEVEL=info
    depends_on:
      - opencti
    restart: always

  connector-opencti:
    image: opencti/connector-opencti:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=28629b22-e8cf-4002-a0e4-1564ce56ac4a
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=OpenCTI
      - CONNECTOR_SCOPE=identity,sector,region,country,city
      - CONNECTOR_CONFIDENCE_LEVEL=5
      - CONNECTOR_UPDATE_EXISTING_DATA=true
      - CONNECTOR_LOG_LEVEL=info
      - CONFIG_SECTORS_FILE_URL=https://raw.githubusercontent.com/OpenCTI-Platform/datasets/master/data/sectors.json
      - CONFIG_GEOGRAPHY_FILE_URL=https://raw.githubusercontent.com/OpenCTI-Platform/datasets/master/data/geography.json
      - CONFIG_INTERVAL=11 # In days, must be strictly greater than 1
    restart: always
    depends_on:
      - opencti
  connector-history:
    image: opencti/connector-history:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=${CONNECTOR_HISTORY_ID} # Valid UUIDv4
      - CONNECTOR_TYPE=STREAM
      - CONNECTOR_NAME=History
      - CONNECTOR_SCOPE=history
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_LOG_LEVEL=info
    restart: always
  connector-export-file-stix:
    image: opencti/connector-export-file-stix:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=${CONNECTOR_EXPORT_FILE_STIX_ID} # Valid UUIDv4
      - CONNECTOR_TYPE=INTERNAL_EXPORT_FILE
      - CONNECTOR_NAME=ExportFileStix2
      - CONNECTOR_SCOPE=application/json
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_LOG_LEVEL=info
    restart: always
  connector-export-file-csv:
    image: opencti/connector-export-file-csv:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=${CONNECTOR_EXPORT_FILE_CSV_ID} # Valid UUIDv4
      - CONNECTOR_TYPE=INTERNAL_EXPORT_FILE
      - CONNECTOR_NAME=ExportFileCsv
      - CONNECTOR_SCOPE=text/csv
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_LOG_LEVEL=info
    restart: always
  connector-import-file-stix:
    image: opencti/connector-import-file-stix:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=${CONNECTOR_IMPORT_FILE_STIX_ID} # Valid UUIDv4
      - CONNECTOR_TYPE=INTERNAL_IMPORT_FILE
      - CONNECTOR_NAME=ImportFileStix2
      - CONNECTOR_SCOPE=application/json
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_LOG_LEVEL=info
    restart: always
  connector-import-file-pdf-observables:
    image: opencti/connector-import-file-pdf-observables:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=${CONNECTOR_IMPORT_FILE_PDF_OBSERVABLES_ID} # Valid UUIDv4
      - CONNECTOR_TYPE=INTERNAL_IMPORT_FILE
      - CONNECTOR_NAME=ImportFilePdfObservables
      - CONNECTOR_SCOPE=application/pdf
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_LOG_LEVEL=info
      - PDF_OBSERVABLES_CREATE_INDICATOR=False
    restart: always
  connector-alienvault:
    image: opencti/connector-alienvault:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=2eb38e0d-933c-42e0-893b-654202c52880
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=AlienVault
      - CONNECTOR_SCOPE=alienvault
      - CONNECTOR_CONFIDENCE_LEVEL=15
      - CONNECTOR_UPDATE_EXISTING_DATA=false
      - CONNECTOR_LOG_LEVEL=info
      - ALIENVAULT_BASE_URL=https://otx.alienvault.com
      - ALIENVAULT_API_KEY=CHANGEME
      - ALIENVAULT_TLP=White
      - ALIENVAULT_CREATE_OBSERVABLES=true
      - ALIENVAULT_CREATE_INDICATORS=true
      - ALIENVAULT_PULSE_START_TIMESTAMP=2019-05-01T00:00:00                  # BEWARE! Could be a lot of pulses!
      - ALIENVAULT_REPORT_TYPE=threat-report
      - ALIENVAULT_REPORT_STATUS=New
      - ALIENVAULT_GUESS_MALWARE=true                                         # Use tags to guess malware.
      - ALIENVAULT_GUESS_CVE=true                                            # Use tags to guess CVE.
      - ALIENVAULT_EXCLUDED_PULSE_INDICATOR_TYPES=FileHash-MD5,FileHash-SHA1  # Excluded Pulse indicator types.
      - ALIENVAULT_INTERVAL_SEC=1800
    restart: always
    depends_on:
      - opencti
  connector-cve:
    image: opencti/connector-cve:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=c0b60738-4152-4ee8-bc13-f0b188447d36
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=Common Vulnerabilities and Exposures
      - CONNECTOR_SCOPE=identity,vulnerability
      - CONNECTOR_CONFIDENCE_LEVEL=4
      - CONNECTOR_UPDATE_EXISTING_DATA=true
      - CONNECTOR_LOG_LEVEL=info
      - CVE_IMPORT_HISTORY=true # Import history at the first run (after only recent), reset the connector state if you want to re-import
      - CVE_NVD_DATA_FEED=https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-recent.json.gz
      - CVE_HISTORY_DATA_FEED=https://nvd.nist.gov/feeds/json/cve/1.1/
      - CVE_INTERVAL=7 # Days
    depends_on:
      - opencti
    restart: always
  connector-mitre:
    image: opencti/connector-mitre:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=c66e21e7-e75f-4424-9096-06fffd650af2
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=MITRE ATT&CK
      - CONNECTOR_SCOPE=identity,attack-pattern,course-of-action,intrusion-set,malware,tool,report,external-reference-as-report
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_UPDATE_EXISTING_DATA=true
      - CONNECTOR_LOG_LEVEL=info
      - MITRE_ENTERPRISE_FILE_URL=https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json
      - MITRE_PRE_ATTACK_FILE_URL=https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json
      - MITRE_MOBILE_ATTACK_FILE_URL=https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json
      - MITRE_INTERVAL=7 # Days
    depends_on:
      - opencti
    restart: always
  connector-ipinfo:
    image: opencti/connector-ipinfo:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=ChangeMe
      - CONNECTOR_TYPE=INTERNAL_ENRICHMENT
      - CONNECTOR_NAME=IpInfo
      - CONNECTOR_SCOPE=IPv4-Addr
      - CONNECTOR_AUTO=true
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_LOG_LEVEL=info
      - IPINFO_TOKEN=a35d86eb43e581
      - IPINFO_MAX_TLP=TLP:AMBER
    restart: always
    depends_on:
      - opencti
  connector-valhalla:
    image: opencti/connector-valhalla:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=f2267032-889c-4a03-b289-717408bd6b6c
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=Valhalla
      - CONNECTOR_SCOPE=valhalla
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_UPDATE_EXISTING_DATA=false
      - CONNECTOR_LOG_LEVEL=info
      - VALHALLA_API_KEY= # Empty key only fetches public/demo information
      - VALHALLA_INTERVAL_SEC=86400 # Run once every day
    restart: always
    depends_on:
      - opencti
  connector-cryptolaemus:
    image: opencti/connector-cryptolaemus:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=2a7a5d13-405c-4584-8249-7d094ee1a0c5
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=Cryptolaemus
      - CONNECTOR_SCOPE=cryptolaemus
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_UPDATE_EXISTING_DATA=true
      - CONNECTOR_LOG_LEVEL=info
    restart: always
    depends_on:
      - opencti
  connector-misp:
    image: opencti/connector-misp:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=615d93a4-b2f9-4128-9315-c107e7b0f5e2
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=MISP
      - CONNECTOR_SCOPE=misp
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_UPDATE_EXISTING_DATA=false
      - CONNECTOR_LOG_LEVEL=info
      - MISP_URL=https://CHANGEME # Required
      - MISP_KEY=CHANGEME
      - MISP_SSL_VERIFY=False # Required
      - MISP_CREATE_REPORTS=True # Required, create report for MISP event
      - MISP_CREATE_INDICATORS=True # Required, create indicators from attributes
      - MISP_CREATE_OBSERVABLES=True # Required, create observables from attributes
      - MISP_REPORT_CLASS=MISP Event # Optional, report_class if creating report for event
      - MISP_IMPORT_FROM_DATE=2000-01-01 # Optional, import all event from this date
#      - MISP_IMPORT_TAGS=opencti:import,type:osint,type:OSINT # Optional, list of tags used for import events
      - MISP_IMPORT_TAGS=     # If blank, import everything
      - MISP_IMPORT_TAGS_NOT= # Optional, list of tags to not include
      - MISP_INTERVAL=300 # Required, in minutes
    restart: always

  connector-cybercrimetracker:
    image: opencti/connector-cybercrime-tracker:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=3b5c7c9c-1a18-4712-ae34-d05be9f17c5b
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=Cybercrime-Tracker
      - CONNECTOR_SCOPE=cybercrime-tracker
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_UPDATE_EXISTING_DATA=true
      - CONNECTOR_LOG_LEVEL=info
      - CYBERCRIME_TRACKER_FEED_URL=http://cybercrime-tracker.net/rss.xml
      - CYBERCRIME_TRACKER_TLP=WHITE
      - CYBERCRIME_TRACKER_INTERVAL=86400
      - CYBERCRIME_TRACKER_CREATE_INDICATORS=true
      - CYBERCRIME_TRACKER_CREATE_OBSERVABLES=true
    restart: always
    depends_on:
      - opencti
  connector-hygiene:
    image: opencti/connector-hygiene:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=645997a7-dfbe-46b1-a3f5-72b53bf0a19a
      - CONNECTOR_TYPE=INTERNAL_ENRICHMENT
      - CONNECTOR_NAME=Hygiene
      - CONNECTOR_SCOPE=IPv4-Addr,IPv6-Addr,Domain-Name,StixFile,Artifact
      - CONNECTOR_AUTO=true
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_LOG_LEVEL=info
    restart: always
    depends_on:
      - worker2
   connector-virustotal:
     image: opencti/connector-virustotal:4.3.0
     environment:
       - OPENCTI_URL=http://opencti:8080
       - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
       - CONNECTOR_ID=412e22c5-6fce-4674-9a43-dc53d403b2bb
       - CONNECTOR_TYPE=INTERNAL_ENRICHMENT
       - CONNECTOR_NAME=VirusTotal
       - CONNECTOR_SCOPE=StixFile,Artifact
       - CONNECTOR_AUTO=true
       - CONNECTOR_CONFIDENCE_LEVEL=3
       - CONNECTOR_LOG_LEVEL=info
       - VIRUSTOTAL_TOKEN=CHANGEME
       - VIRUSTOTAL_MAX_TLP=TLP:AMBER
     restart: always
  connector-abuseipdb:
    image: opencti/connector-abuseipdb:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=48d14f7c-29d0-42e1-aa12-ba425638f889
      - CONNECTOR_TYPE=INTERNAL_ENRICHMENT
      - CONNECTOR_NAME=AbuseIPDB
      - CONNECTOR_SCOPE=IPv4-Addr
      - CONNECTOR_AUTO=true
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_LOG_LEVEL=info
      - ABUSEIPDB_API_KEY=CHANGEME
      - ABUSEIPDB_MAX_TLP=TLP:AMBER
    restart: always
    depends_on:
      - worker3

  connector-malbeacon:
    image: opencti/connector-malbeacon:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=54137329-5776-49ec-8646-7efc12207e66
      - CONNECTOR_TYPE=INTERNAL_ENRICHMENT
      - CONNECTOR_NAME=Malbeacon
      - CONNECTOR_AUTO=false # Enable/disable auto-enrichment of observables
      - CONNECTOR_SCOPE=ipv4-addr,ipv6-addr,domain-name
      - CONNECTOR_CONFIDENCE_LEVEL=30 # From 0 (Unknown) to 100 (Fully trusted)
      - CONNECTOR_LOG_LEVEL=info
      - MALBEACON_API_KEY=CHANGEME
    restart: always

  connector-urlhaus:
    image: opencti/connector-urlhaus:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=0e110c0b-b98f-4ef5-97e9-5422be5143df
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=Abuse.ch\ URLhause
      - CONNECTOR_SCOPE=urlhaus
      - CONNECTOR_CONFIDENCE_LEVEL=40
      - CONNECTOR_UPDATE_EXISTING_DATA=true
      - CONNECTOR_LOG_LEVEL=info
      - URLHAUS_CSV_URL=https://urlhaus.abuse.ch/downloads/csv_recent/
      - URLHAUS_IMPORT_OFFLINE=true
      - URLHAUS_INTERVAL=8 # In days, must be strictly greater than 1
    restart: always
    depends_on:
      - worker4
  connector-malpedia:
    image: opencti/connector-malpedia:4.3.0
    environment:
      - OPENCTI_URL=http://opencti:8080
      - OPENCTI_TOKEN=${OPENCTI_ADMIN_TOKEN}
      - CONNECTOR_ID=28399d60-8155-49f3-a75b-f6aa22b64e91
      - CONNECTOR_TYPE=EXTERNAL_IMPORT
      - CONNECTOR_NAME=Malpedia
      - CONNECTOR_SCOPE=malpedia
      - CONNECTOR_CONFIDENCE_LEVEL=3
      - CONNECTOR_UPDATE_EXISTING_DATA=false
      - CONNECTOR_LOG_LEVEL=info
      - MALPEDIA_AUTH_KEY= # Empty key only fetches TLP:WHITE information
      - MALPEDIA_INTERVAL_SEC=90000 # Run once a little less than every day
      - MALPEDIA_IMPORT_INTRUSION_SETS=true
      - MALPEDIA_IMPORT_YARA=true
      - MALPEDIA_CREATE_INDICATORS=true
      - MALPEDIA_CREATE_OBSERVABLES=true
    restart: always
    
volumes:
  esdata:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: /data/opencti/esdata
  s3data:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: /data/opencti/s3data
  redisdata:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: /data/opencti/redisdata
  amqpdata:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: /data/opencti/amqpdata
    

OpenCTI Workers

The number of workers is up to you and your system capabilities. The more disk I/O and CPU available, the more workers that can be running at any given time. I wouldn't recommend any less than 4, and assuming decent specs, I would start with 8 and see how the system performs. Too few and you may never catch up with the backlog of events that need to be ingested and correlated.

Update vm.max_map_count on the host

As OpenCTI has a dependency on ElasticSearch, you have to set the vm.max_map_count before running the containers, as mentioned in the ElasticSearch documentation.

$ sudo sysctl -w vm.max_map_count=1048575

Make this parameter persistent; add vm.max_map_count=1048575 at the end of your /etc/sysctl.conf file.

$ sudo echo -e "\nvm.max_map_count=1048575" >> /etc/sysctl.conf

Allow overcommit of memory for Redis

sudo sysctl vm.overcommit_memory=1

Make this parameter persistent; add vm.overcommit_memory=1 at the end of your /etc/sysctl.conf file.

$ sudo echo -e "\nvm.overcommit_memory=1" >> /etc/sysctl.conf

Updating OpenCTI

Updating is as easy as changing the container tags.

  1. Take down the system with docker compose down
  2. Update all the tags in the docker-compose.yml file to the newer version, then restart:
  3. docker compose up -d

OpenCTI Enterprise Edition

OpenCTI has added an enterprise edition to the product lineup. This mode can be activated for free by individuals and non-profits, and it adds capabilities such as #AI review and support of the stored artifacts. The Enterprise Edition comprises five key components

Automation and Analytics

As one might expect, the first enhancement with EE is some automation to address routine tasks.

Generative AI Summaries

It came out in 2024, so of course they had to sprinkle in some #AI using your choice of a commercial or locally-hosted #LLM. Given OpenCTI development is in Europe, Mistral is a popular option.

RBAC and Data segregation

Role-based access is a true need for enterprise organizations, so this makes sense

Audit-logging and UBA

Another set of standard enterprise features are audit logging and user analytics so that the organization can be sure no one is abusing the system.

Full-text file indexing

An OpenCTI instance
comprehensive searching across both structured and unstructured data

Connectors

"Connectors" are the link between the OpenCTI platform and the data. Connectors come in a few basic types

Adding Connectors

The list of connectors are available to be added to the installed platform. Some are free, some freemium, and some purely commercial, requiring the configuration of credentials and other context.

Steps to add a connector

Specific configuration may vary, but the basic steps for adding an OpenCTI connector are as follows:

  1. Add the relevant information from the connectors docker-compose.yml file to the configuration in /opt/opencti
  2. Ensure the URL points to http://opencti:8080
  3. Set the OPENCTI_TOKEN to ${OPENCTI_ADMIN_TOKEN}, which will use the token set in your .env file for simplicity
  4. Generate an unique UUIDv4 token for the connector ID and populatre that for CONNECTOR_ID
  5. Restart your instance with docker compose down && docker-compose up -d

Adding RSS Feed data

DamagedRoot has a nice medium article about adding threat intel sources, and it includes a lot of feeds and how to make them work with OpenCTI. Worth a read.

Usage

Now that the system is up and riunning, we want to use it! Unfortunately, the first thing you'll need to do is wait. A fresh install does not have any data, but the workers will immediately

Getting Started

Read up on the official documentation

How OpenCTI fits in an Intel workflow

OpenCTI is a Threat Intelligence Platform ("TIP"), and as such, it is a central figure in a complete threat intelligence architecture.

Architecture

graph TD

%% ThreatIntel tooling
%% TIP(TheatIntel Platform)
TIP.1(OpenCTI)
TIP.2(Cortex)

%% ThreatIntel sources

TIS.2(Alienvault OTX)
TIS.3(MITRE)
TIS.4(CVEs)
TIS.5(MISP)

%% ThreatIntel enrichment
TIE.1(IPinfo.io)
TIE.2(Malbeacon)


%% Incident Response tooling
%% IR(IR)
IR.1(TheHive)
IR.2(Splunk)

%% SOAR
SOAR.1(Phantom/Tines/etc)

TIP.1 -- Enrich --> TIE.1 & TIE.2
TIS.2 & TIS.3 & TIS.4 & TIS.5 --> TIP.1 -- Correlate --> IR.1
IR.1 -- Enrich --> TIP.2 & TIP.1



SOAR.1 -- SOAR --> IR.1 & IR.2

Figure 2: A sample OpenCTI architecture showing in/outflows of data

Example: Reviewing an Intrusion Set

Let's look into #APT37 and see what we can glean about this intrusion set from our data.

  1. The first thing to do is to pull up the APT37 object in the system. Navigate to Threats > Intrusion Sets and type "APT37" in the filter box. (You can simply search the platform, but that will be slower and the purpose here is to highlight where the data resides within OpenCTI)
  2. The results should include (and may only be) a box titled APT37. If this does not appear, check that workers are running and that you have decent data sources configured. At a minimum, make sure connector-mitre has been configured to pull in data from MITRE ATT&CK .
  3. From here, there is a good summary of the set, and a number of boxes with links to different objects:
    • Latest relationships: observables/artifacts that are related to APT37, most recent first
    • Latest containers about the object: reports that have been ingested into the platform that are about or related to APT37
    • External references: links to original reporting material on the Internet known to be connected to APT37
    • Recent History: changes to the APT37 object in the OpenCTI platform you're using.
  4. Along the top of the page is a navigation bar, starting with "OVERVIEW", "KNOWLEDGE", etc. Click on KNOWLEDGE
  5. Notice the new navigation tree on the right, this is a quick way to bring up specific information about this set, such as "Attack Patterns", which will show a MITRE ATT&CK matrix in a heatmap showing #TTPs used by this set so the analyst can know what to expect from APT37.
    • The diamond model at the bottom of the page can also be used to navigate to specific areas. e.g. the left side of the diamond will also bring up the MITRE TTPs.
  6. "Command and Scripting Interpreter" should be red because it is frequently used by APT37, click on it and choose view
  7. This takes you directly to the information for Command and Scripting Interpreter under Techniques > Attack Patterns.
    • Alternatively, you could navigate here from the left tree menu and searching for the technique
  8. Again, we have a number of boxes showing the relationships this entity. Essentially, we have pivoted on the TTP and can navigate to other groups using the technique, recent observed IPs known to be associated, etc.