Collect MISP IOC logs

Supported in:

This guide outlines the steps to ingest MISP (Malware Information Sharing Platform) IOC (Indicators of Compromise) logs to Google Security Operations using Bindplane. The parser extracts IOCs from MISP data formatted as JSON or CSV. It parses the input, maps fields to the Unified Data Model (UDM), handles various IOC types (such as IP, domain, and file hashes), and enriches the data with threat intelligence context like confidence and severity. The parser also performs specific logic for different data formats and handles cases with missing or unsupported fields.

Before you begin

Make sure you have the following prerequisites:

  • Google SecOps instance
  • Linux host with systemd
  • If running behind a proxy, firewall ports are open
  • Privileged access to your MISP server

Get MISP API Key

  1. Sign in to your MISPweb UI as an Administrator.
  2. Go to Administration > List Auth Keys.
  3. Click Add authentication key.
  4. Provide the following Key configuration:
    • User: Select the user account associated with the key.
    • Allowed IPs: You can optionally specify allowed IP addresses for the key.
    • Copy and save the key in a secure location.
    • Click I have noted down my key.

Configure MISP logs export

  1. Sign in to your MISPinstance using SSH.
  2. Install PyMISPusing the following command:

     pip3  
    install  
    pymisp  
     
    
  3. Modify the get_csv.py export script using the following:

      #!/usr/bin/env python3 
     # -*- coding: utf-8 -*- 
     import 
      
     argparse 
     from 
      
     pymisp 
      
     import 
     ExpandedPyMISP 
     from 
      
     keys 
      
     import 
     misp_url 
     , 
     misp_key 
     , 
     misp_verifycert 
     if 
     __name__ 
     == 
     '__main__' 
     : 
     parser 
     = 
     argparse 
     . 
     ArgumentParser 
     ( 
     description 
     = 
     'Get MISP data in a CSV format.' 
     ) 
     parser 
     . 
     add_argument 
     ( 
     "--controller" 
     , 
     default 
     = 
     'attributes' 
     , 
     help 
     = 
     "Attribute to use for the search (events, objects, attributes)" 
     ) 
     parser 
     . 
     add_argument 
     ( 
     "-e" 
     , 
     "--event_id" 
     , 
     help 
     = 
     "Event ID to fetch. Without it, it will fetch the whole database." 
     ) 
     parser 
     . 
     add_argument 
     ( 
     "-a" 
     , 
     "--attribute" 
     , 
     nargs 
     = 
     '+' 
     , 
     help 
     = 
     "Attribute column names" 
     ) 
     parser 
     . 
     add_argument 
     ( 
     "-o" 
     , 
     "--object_attribute" 
     , 
     nargs 
     = 
     '+' 
     , 
     help 
     = 
     "Object attribute column names" 
     ) 
     parser 
     . 
     add_argument 
     ( 
     "-t" 
     , 
     "--misp_types" 
     , 
     nargs 
     = 
     '+' 
     , 
     help 
     = 
     "MISP types to fetch (ip-src, hostname, ...)" 
     ) 
     parser 
     . 
     add_argument 
     ( 
     "-c" 
     , 
     "--context" 
     , 
     action 
     = 
     'store_true' 
     , 
     help 
     = 
     "Add event level context (tags...)" 
     ) 
     parser 
     . 
     add_argument 
     ( 
     "-f" 
     , 
     "--outfile" 
     , 
     help 
     = 
     "Output file to write the CSV." 
     ) 
     parser 
     . 
     add_argument 
     ( 
     "-l" 
     , 
     "--last" 
     , 
     required 
     = 
     True 
     , 
     help 
     = 
     "can be defined in days, hours, minutes (for example 5d or 12h or 30m)." 
     ) 
     args 
     = 
     parser 
     . 
     parse_args 
     () 
     pymisp 
     = 
     ExpandedPyMISP 
     ( 
     misp_url 
     , 
     misp_key 
     , 
     misp_verifycert 
     , 
     debug 
     = 
     True 
     ) 
     attr 
     = 
     [] 
     if 
     args 
     . 
     attribute 
     : 
     attr 
     += 
     args 
     . 
     attribute 
     if 
     args 
     . 
     object_attribute 
     : 
     attr 
     += 
     args 
     . 
     object_attribute 
     if 
     not 
     attr 
     : 
     attr 
     = 
     None 
     print 
     ( 
     args 
     . 
     context 
     ) 
     response 
     = 
     pymisp 
     . 
     search 
     ( 
     return_format 
     = 
     'csv' 
     , 
     controller 
     = 
     args 
     . 
     controller 
     , 
     eventid 
     = 
     args 
     . 
     event_id 
     , 
     requested_attributes 
     = 
     attr 
     , 
     publish_timestamp 
     = 
     args 
     . 
     last 
     , 
     type_attribute 
     = 
     args 
     . 
     misp_types 
     , 
     include_context 
     = 
     args 
     . 
     context 
     ) 
     if 
     args 
     . 
     outfile 
     : 
     with 
     open 
     ( 
     args 
     . 
     outfile 
     , 
     'w' 
     ) 
     as 
     f 
     : 
     f 
     . 
     write 
     ( 
     response 
     ) 
     else 
     : 
     print 
     ( 
     response 
     ) 
     
    
  4. Edit the keys.py file to include your MISP API credentials and URL, as follows:

      misp_url 
     = 
     'https://<MISP_URL>' 
     misp_key 
     = 
     '<MISP_API_KEY>' 
     misp_verifycert 
     = 
     False 
     misp_client_cert 
     = 
     '' 
     
    
    • Replace <MISP_URL> with your MISP IP or hostname.
    • Replace <MISP_API_KEY with the actual API key generated previously.
  5. Open crontab using the crontab -e command and enter the following:

      0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/url.log  
    -t  
     "url" 
      
    -l  
    1d  
    -c 0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/ip-dst.log  
    -t  
     "ip-dst" 
      
    -l  
    1d  
    -c 0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/ip-src.log  
    -t  
     "ip-src" 
      
    -l  
    1d  
    -c 0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/domain.log  
    -t  
     "domain" 
      
    -l  
    1d  
    -c 0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/sha256.log  
    -t  
     "sha256" 
      
    -l  
    1d  
    -c 0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/file.log  
    -t  
     "filename" 
      
    -l  
    1d  
    -c 0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/registry.log  
    -t  
     "registry" 
      
    -l  
    1d  
    -c 0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/mutex.log  
    -t  
     "mutex" 
      
    -l  
    1d  
    -c 0 
      
     20 
      
    *  
    *  
    *  
    python3  
    /opt/misp/<YOUR_EXPORT_SCRIPT_PATH>  
    -f  
    /opt/misp/ioc_export/threat-actor.log  
    -t  
     "threat-actor" 
      
    -l  
    1d  
    -c 
    
    • Update <YOUR_EXPORT_SCRIPT_PATH> according to the actual export script location.

Get Google SecOps ingestion authentication file

  1. Sign in to the Google SecOps console.
  2. Go to SIEM Settings > Collection Agents.
  3. Download the Ingestion Authentication File. Save the file securely on the system where Bindplane will be installed.

Get Google SecOps customer ID

  1. Sign in to the Google SecOps console.
  2. Go to SIEM Settings > Profile.
  3. Copy and save the Customer IDfrom the Organization Detailssection.

Install the Bindplane agent on MISP Server

Linux installation

  1. Open a terminal with root or sudo privileges.
  2. Run the following command:

     sudo  
    sh  
    -c  
     " 
     $( 
    curl  
    -fsSlL  
    https://github.com/observiq/bindplane-agent/releases/latest/download/install_unix.sh ) 
     " 
      
    install_unix.sh 
    

Additional installation resources

For additional installation options, consult the installation guide .

Configure the Bindplane agent to ingest MISP log files and send to Google SecOps

  1. Access the configuration file:
    • Locate the config.yaml file. Typically, it's in the /etc/bindplane-agent/ directory on Linux.
    • Open the file using a text editor (for example, nano , vi , or Notepad).
  2. Edit the config.yaml file as follows:

      receivers 
     : 
      
     filelog 
     : 
      
     file_path 
     : 
      
     /opt/misp/ioc_export/*.log 
      
     log_type 
     : 
      
     'file' 
     exporters 
     : 
      
     chronicle/chronicle_w_labels 
     : 
      
     compression 
     : 
      
     gzip 
      
     # Adjust the path to the credentials file you downloaded in Step 1 
      
     creds_file_path 
     : 
      
     '/path/to/ingestion-authentication-file.json' 
      
     # Replace with your actual customer ID from Step 2 
      
     customer_id 
     : 
      
    < customer_id 
    >  
     endpoint 
     : 
      
     malachiteingestion-pa.googleapis.com 
      
     # Add optional ingestion labels for better organization 
      
     ingestion_labels 
     : 
      
     log_type 
     : 
      
     'MISP_IOC' 
      
     raw_log_field 
     : 
      
     body 
     service 
     : 
      
     pipelines 
     : 
      
     logs/source0__chronicle_w_labels-0 
     : 
      
     receivers 
     : 
      
     - 
      
     filelog 
      
     exporters 
     : 
      
     - 
      
     chronicle/chronicle_w_labels 
     
    
    • Replace the port and IP address as required in your infrastructure.
    • Replace <customer_id> with the actual Customer ID.
    • Update /path/to/ingestion-authentication-file.json to the path where the authentication file was saved in the Get Google SecOps ingestion authentication file section.

Restart the Bindplane agent to apply the changes

To restart the Bindplane agent in Linux, run the following command:

   
sudo  
systemctl  
restart  
bindplane-agent 

UDM mapping table

Log Field UDM Mapping Logic
Attribute.category
event.idm.entity.metadata.threat.category_details Directly mapped from Attribute.category in the nested JSON object within the "data" field. Used in the JSON parsing path.
Attribute.comment
event.idm.entity.metadata.threat.summary Directly mapped from Attribute.comment in the nested JSON object within the "data" field. Used in the JSON parsing path.
Attribute.deleted
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Attribute.deleted and added as a detection field with key "Attribute deleted". Used in the JSON parsing path.
Attribute.event_id
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Attribute.event_id and added as a detection field with key "Attribute event_id". Used in the JSON parsing path.
Attribute.first_seen
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Attribute.first_seen and added as a detection field with key "Attribute first_seen". Used in the JSON parsing path.
Attribute.id
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Attribute.id and added as a detection field with key "Attribute id" or "Attribute id $$" depending on the parsing path. Used in both CSV and JSON parsing paths.
Attribute.timestamp
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Attribute.timestamp and added as a detection field with key "Attribute timestamp". Used in the JSON parsing path.
Attribute.to_ids
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Attribute.to_ids and added as a detection field with key "Attribute to_ids". Used in the JSON parsing path.
Attribute.type
log_type Directly mapped from Attribute.type in the nested JSON object within the "data" field. Used as an interim field, and later used to populate other UDM fields. Used in the JSON parsing path.
Attribute.uuid
event.idm.entity.metadata.product_entity_id Directly mapped from Attribute.uuid in the nested JSON object within the "data" field. Used in the JSON parsing path.
Attribute.value
Multiple The value of this field is used to populate several UDM fields depending on the Attribute.type (or log_type if derived from Attribute.type ):
- event.idm.entity.entity.hostname if type is "domain".
- event.idm.entity.entity.file.md5 if type is "md5".
- event.idm.entity.entity.file.sha1 if type is "sha1".
- event.idm.entity.entity.file.sha256 if type is "sha256".
- event.idm.entity.entity.resource.name if type is "mutex".
- event.idm.entity.entity.registry.registry_key if type is "regkey".
- event.idm.entity.entity.user.email_addresses if type is "threat-actor".
- event.idm.entity.entity.url if type is uri or url .
- event.idm.entity.entity.file.full_path if type is "filename".
- Parsed for IP and port if type is "ip-dst|port", "ip-dst", or "ip-src". Used in the JSON parsing path.
column1
event.idm.entity.metadata.product_entity_id Directly mapped from column1 in the CSV parsing path.
column14
Part of event.idm.entity.metadata.threat.description Concatenated with description to form the final description in the threat metadata. Used in the CSV parsing path.
column16
event.idm.entity.metadata.threat.threat_feed_name , event.ioc.feed_name Directly mapped from column16 . Used in the CSV parsing path.
column18
event.idm.entity.metadata.threat.severity_details , event.ioc.raw_severity Directly mapped from column18 . Used in the CSV parsing path.
column21
Part of event.idm.entity.metadata.threat.description , event.ioc.description Used as the base for the description, later concatenated with event_info . Used in the CSV parsing path.
column3
Part of event.ioc.categorization Directly mapped from column3 and concatenated with " IOCs" to form the final categorization. Used in the CSV parsing path.
column4
event.idm.entity.metadata.description Directly mapped from column4 . Used in the CSV parsing path.
column5
Multiple The value of this field is used to populate several UDM fields depending on the column4 field (which maps to type ):
- event.idm.entity.entity.hostname if type is "domain".
- Parsed for IP and port if type is "ip-dst|port", "ip-dst", or "ip-src".
- event.idm.entity.entity.file.md5 if type is "md5".
- event.idm.entity.entity.file.sha1 if type is "sha1".
- event.idm.entity.entity.file.sha256 if type is "sha256".
- event.idm.entity.entity.resource.name if type is "mutex".
- event.idm.entity.entity.registry.registry_key if type is "regkey".
- event.idm.entity.entity.user.email_addresses if type is "threat-actor".
- event.idm.entity.entity.url if type is uri or url .
- event.idm.entity.entity.file.full_path if type is "filename". Used in the CSV parsing path.
column6
event.idm.entity.metadata.threat.summary Directly mapped from column6 . Used in the CSV parsing path.
column8
event.ioc.active_timerange.start , event.idm.entity.metadata.interval.start_time Parsed as a UNIX timestamp. Used in the CSV parsing path.
date description
event.idm.entity.metadata.threat.description Directly mapped from description in the nested JSON object within the "data" field. Used in the JSON parsing path.
event_creator_email
event.idm.entity.entity.labels.value Directly mapped from event_creator_email and added as a label with key "event_creator_email". Used in the JSON parsing path.
event_id Feed.publish
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Feed.publish and added as a detection field with key "Feed publish". Used in the JSON parsing path.
first_seen
event.ioc.active_timerange.start , event.idm.entity.metadata.interval.start_time Parsed as a timestamp in the format "yyyy-MM-ddTHH:mm:ssZZ". Used in the JSON parsing path.
id info
event.idm.entity.metadata.description Directly mapped from info in the nested JSON object within the "data" field. Used in the JSON parsing path.
last_seen
event.ioc.active_timerange.end Parsed as a timestamp in the format "yyyy-MM-ddTHH:mm:ssZZ". Used in the JSON parsing path.
Org.name
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Org.name and added as a detection field with key "Org name". Used in the JSON parsing path.
published
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from published and added as a detection field with key "published". Used in the JSON parsing path.
Tag.colour
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.colour and added as a detection field with key "tag colour". Used in the JSON parsing path.
Tag.exportable
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.exportable and added as a detection field with key "tag exportable". Used in the JSON parsing path.
Tag.hide_tag
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.hide_tag and added as a detection field with key "tag hide_tag". Used in the JSON parsing path.
Tag.id
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.id and added as a detection field with key "tag id". Used in the JSON parsing path.
Tag.is_custom_galaxy
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.is_custom_galaxy and added as a detection field with key "tag is_custom_galaxy". Used in the JSON parsing path.
Tag.is_galaxy
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.is_galaxy and added as a detection field with key "tag is_galaxy". Used in the JSON parsing path.
Tag.isinherited
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.isinherited and added as a detection field with key "tag isinherited". Used in the JSON parsing path.
Tag.name
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.name and added as a detection field with key "tag name". Used in the JSON parsing path.
Tag.numerical_value
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.numerical_value and added as a detection field with key "tag numerical_value". Used in the JSON parsing path.
Tag.user_id
event.idm.entity.metadata.threat.detection_fields.value Directly mapped from Tag.user_id and added as a detection field with key "tag user_id". Used in the JSON parsing path.
threat_level_id
event.idm.entity.entity.labels.value Directly mapped from threat_level_id and added as a label with key "threat_level_id". Used in the JSON parsing path.
timestamp
event.idm.entity.metadata.collected_timestamp , event.idm.entity.metadata.interval.start_time Parsed as a UNIX timestamp. Used in the CSV parsing path.
uuid
event.idm.entity.metadata.vendor_name Set to "MISP" by the parser. Set to "MISP" by the parser. Set to a default value far in the future (253402300799 seconds since epoch). Determined by the parser based on the type or log_type field. Can be "FILE", "DOMAIN_NAME", "IP_ADDRESS", "MUTEX", "RESOURCE", or "USER". Can be derived from Attribute.comment or Attribute.value depending on the Attribute.type . Parsed from Attribute.value or column5 if the type is IP related. Parsed from Attribute.value or column5 if the type is "ip-dst|port". Derived from column3 in CSV parsing or Attribute.category in JSON parsing. Derived from column21 and column14 in CSV parsing. Derived from column8 or first_seen . Derived from last_seen . Derived from description using a grok pattern. Derived from column16 or set to "MISP". Derived from column18 . Parsed from Attribute.value or column5 if the type is IP related. Parsed from Attribute.value or column5 if the type is "ip-dst|port". Derived from Attribute.value or column5 if the type is "domain". Derived from the confidence field, which is extracted from the description field. Values can be "HIGH_CONFIDENCE", "MEDIUM_CONFIDENCE", "LOW_CONFIDENCE", or "UNKNOWN_CONFIDENCE". Directly mapped from the confidence field, which is extracted from the description field. Directly mapped from the threat_level field, which is derived from column18 in the CSV parsing path. Directly mapped from the feed_name field, which is derived from column16 in the CSV parsing path. Derived from column21 and column14 in CSV parsing. Derived from column6 in CSV parsing or Attribute.comment in JSON parsing. Several fields are added as detection fields with their corresponding keys. Several fields are added as labels with their corresponding keys. Copied from the top-level timestamp field in the raw log.

Need more help? Get answers from Community members and Google SecOps professionals.

Create a Mobile Website
View Site in Mobile | Classic
Share by: