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:
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:
- The directory where the configuration files reside. Normally, you may create this by
git clone
ing the docker-focused repository under openCTI-Platform. - The directory where you want the data to be stored (a fast drive with plenty of space)
- The
.env
file that will manage environment variables for the OpenCTI instance - the
docker-compose.yml
file with specific declarations for the system, workers, and all the connectors.
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
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.
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.
- Take down the system with
docker compose down
- Update all the tags in the
docker-compose.yml
file to the newer version, then restart: 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
- External Import - for the pulling of threat intelligence
- Internal Enrichment - for adding context to data (especially IOCs) in the platform
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:
- Add the relevant information from the connectors
docker-compose.yml
file to the configuration in/opt/opencti
- Ensure the URL points to
http://opencti:8080
- Set the
OPENCTI_TOKEN
to${OPENCTI_ADMIN_TOKEN}
, which will use the token set in your.env
file for simplicity - Generate an unique UUIDv4 token for the connector ID and populatre that for
CONNECTOR_ID
- 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.
- 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)
- 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 . - 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.
- Along the top of the page is a navigation bar, starting with "OVERVIEW", "KNOWLEDGE", etc. Click on KNOWLEDGE
- 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.
- "Command and Scripting Interpreter" should be red because it is frequently used by APT37, click on it and choose view
- 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
- 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.