├── LICENSE ├── README.md ├── docs ├── mispalerts.md ├── mispfetch.md ├── mispgetevent.md ├── mispgetioc.md ├── misprest.md └── mispsearch.md ├── globalConfig.json ├── images ├── misp42_add_misp_instance.png ├── misp42_alert_action_create_event_dashboard.png ├── misp42_custom_command_mispgetioc_dashboard.png ├── misp42_custom_command_misprest_dashboard.png ├── misp42_custom_command_mispsearch_dashboard.png ├── misp42_ioc_retrosearch_kvstore_creation.png ├── misp42_ioc_retrosearch_lifecycle.png └── misp42_ioc_retrosearch_lookup_defintion.png ├── misp42splunk-5.0.0.tar.gz └── package ├── README.txt ├── README ├── misp_datatypes.csv.sample └── restmap.conf.spec ├── app.manifest ├── appserver └── static │ ├── alert_misp_alert_create_event.png │ ├── alert_misp_alert_sighting.png │ └── test_alert.png ├── bin ├── domain-ip_definition.json ├── email_definition.json ├── file_definition.json ├── misp42splunk │ ├── modalert_misp_alert_create_event_helper.py │ └── modalert_misp_alert_sighting_helper.py ├── misp42splunk_declare.py ├── misp_common.py ├── mispfetch.py ├── mispgetevent.py ├── mispgetioc.py ├── misprest.py └── mispsearch.py ├── default ├── alert_actions.conf ├── collections.conf ├── commands.conf ├── data │ └── ui │ │ ├── nav │ │ └── default.xml │ │ └── views │ │ ├── configuration.xml │ │ ├── inputs.xml │ │ ├── misp_alert_create_event.xml │ │ ├── misp_alert_logs.xml │ │ ├── misp_alert_sighting.xml │ │ ├── misp_overview.xml │ │ ├── mispfetch.xml │ │ ├── mispgetevent.xml │ │ ├── mispgetioc.xml │ │ ├── misprest.xml │ │ └── mispsearch.xml ├── props.conf ├── savedsearches.conf ├── searchbnf.conf ├── server.conf └── transforms.conf ├── lib └── requirements.txt ├── metadata └── default.meta └── static ├── README.md ├── appIcon.png ├── appIconAlt.png ├── appIconAlt_2x.png └── appIcon_2x.png /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Introduction 3 | This TA allows interaction between your [Splunk](www.splunk.com) search head (cluster) and one or several [MISP](http://www.misp-project.org/) instance(s). It is a versatile TA that acts as a wrapper of MISP API to either collect MISP information into Splunk (custom commands) or push information from Splunk to MISP (alert actions). 4 | The TA is designed to be easy to install, set up and maintain using the Splunk GUI. 5 | 6 | ## Quick Start 7 | **IMPORTANT** following first upgrade to version 4.1.0 or above, you need to configure the TA again (switch to new framework) 8 | 9 | 10 | This app is designed to run on **Splunk Search Head(s)** on Linux plateforms (not tested on Windows but it could work) 11 | 1. **Download TA from splunkbasew** [splunkbase](https://splunkbase.splunk.com/app/4335/) 12 | 2. Install the app on your Splunk Search Head(s): "Manage Apps" -> "Install app from file" and restart Splunk server 13 | 3. Launch the app (Manage Apps > misp42 > launch app) and go to Configuration menu 14 | 5. create at least one instance for example "default_misp". 15 | - provide a name for example default_misp to follow the examples provided in this doc 16 | - provide the url to your MISP instance (version > 2.4.117) 17 | - provide the authkey, 18 | - check (or not) the certificate of the MISP server, 19 | - use (or not) the proxy for this instance, 20 | - provide client certificate if required (and check the box to use it) 21 | ![inputs](images/misp42_add_misp_instance.png) 22 | 6. If you need **several instances**, create additional entries. 23 | 7. Important: Role(s)/user(s) using this app must have the capability to "list_storage_passwords" (as API KEYs and proxy password(s) are safely stored encrypted). 24 | 25 | ## Usage 26 | 1. MISP to SPLUNK (custom commands): 27 | **`| mispgetioc misp_instance=default_misp _params_ | ...`** gets MISP event attributes into Splunk search pipeline as lookup. 28 | **`| mispgetevent misp_instance=default_misp _params_ | ...`** gets MISP events into Splunk search pipeline as lookup. 29 | **`| mispcollect misp_instance=default_misp _params_ | ...`** gets MISP attributes or events into Splunk as events with complete mapping. 30 | **`search ... |mispsearch misp_instance=default_misp field=myvalue | ...`** searches for matching attributes in MISP. 31 | **`search ... |mispsight misp_instance=default_misp field=myvalue | ...`** gets sighting information for a specific value (note that if there is FP, only first hit is returned) 32 | 33 | 2. MISP for SPLUNK: 2 Splunk alert actions are available 34 | * one action to create new events or **edit** existing ones if you provide an eventid (or UUID). This allows to contribute to misp event(s) across several alert triggers. 35 | * one action to increment attribute sighting in a MISP instance. 36 | 37 | 38 | ## Use Cases 39 | ### Build a dashboard 40 | You may get fresh attributes from a MISP instance and save them under an index (for example index=misp). 41 | Then a dashboard can be build by using [this template](docs/dashboard_examples/misp_charts.xml). The result should be similar to [this video](https://www.youtube.com/watch?v=H2Z3gwJW7Fc&feature=youtu.be) 42 | Thanks to @ran2 for sharing! 43 | 44 | ### Hunting in Splunk logs 45 | Fresh IOC from MISP > saved searches in Splunk 46 | 47 | ### Creating (or editing) events based on automated sandboxing 48 | If you have output of analysis pushed to Splunk you may automate the creation of events 49 | Log on sandboxing output > saved search to qualify, sanitize (dedup remove top Alexa, etc.) and prepare the table (misp_*, fo_*, eo_* and no_*) > set a splunk alert to create event(s) in MISP 50 | * Only fields prefixed with misp_ (or fo_ for file objects, eo_ for email objects, no_ for domain-ip objects) are imported 51 | * **NEW in >=3.2.2** additional fields can be added to MISP event by editing lookup/misp_datatypes.csv see [this sample](README/misp_datatypes.csv.sample). This will improve compatibility with Enterprise Security Adaptative response 52 | * Advise: for objects, verify the name of the fields to be created [Object definitions](https://github.com/MISP/misp-objects/tree/master/objects) 53 | * If you provide an eventid, that event is updated with attributes and objects instead of creating a new one. **WARNING** apparently the **API does create duplicate objects** if you submit several time the same inputs. 54 | 55 | ### Sighting in MISP based on Splunk alerts 56 | Search for attributes values/uuids in Splunk > alert to increment sighting counters (standard,false positive,expiration) in MISP for those values/uuids 57 | 58 | NEW in >=3.2.2: you can record the source of sighting in alert configuration (static string or inline field) 59 | 60 | ### Saved searches and Enterprise Security App 61 | Several saved searches are provided to easily create KV store lookups which can be used later. The default behaviour is to append new event attributes to the KV store but you may switch to replace it. 62 | Based on those searches, you can easily created local CSV files and feed intel to Enterprise Security App. 63 | you can also use this example (thanks @xg-simon for sharing): 64 | 65 | ```conf 66 | | mispgetioc misp_instance=default_misp pipesplit=true add_description=true category="External analysis,Financial fraud,Internal reference,Network activity,Other,Payload delivery,Payload installation,Payload type,Persistence mechanism,Person,Social network,Support Tool,Targeting data" last=90d to_ids=true geteventtag=true warning_list=true not_tags="osint:source-type=\"block-or-filter-list\"" 67 | | eval ip=coalesce(misp_ip_dst, misp_ip_src, misp_ip) 68 | | eval domain=misp_domain 69 | | eval src_user=coalesce(misp_email_src, misp_email_src_display_name) 70 | | eval subject=misp_email_subject 71 | | eval file_name=misp_filename 72 | | eval file_hash=coalesce(misp_sha1, misp_sha256, misp_sha512, misp_md5, misp_ssdeep) 73 | | eval url=coalesce(misp_url,misp_hostname) 74 | | eval http_user_agent=misp_user_agent 75 | | eval registry_value_name=misp_regkey 76 | | eval registry_value_text=if(isnotnull(misp_regkey),misp_value,null) 77 | | eval description = misp_description 78 | | table domain,description,file_hash,file_name,http_user_agent,ip,registry_value_name,registry_value_text,src_user,subject,url,weight 79 | ``` 80 | 81 | ## Usage 82 | - custom commands 83 | * [mispgetioc](docs/mispgetioc.md) Generating command leveraging /attributes/restSearch endpoint 84 | * [mispgetevent](docs/mispgetevent.md) Generating command leveraging /events/restSearch endpoint 85 | * [mispcollect](docs/mispcollect.md) Generating command for events leveraging /attributes/restSearch or /events/restSearch endpoints 86 | * [misprest](docs/misprest.md) Generating command as a wrapper for MISP REST API. 87 | * [mispsearch](docs/mispsearch.md) streaming command 88 | * [mispsight](docs/mispsight.md) streaming command 89 | - Splunk alert actions to [update MISP](docs/mispalerts.md) 90 | * Alert to create MISP event(s) with an option to publish them at same time. 91 | * Alert for attribute sighting in MISP. 92 | 93 | - Each custome command and alert action comes with a dashboard to demonstrate how to use them. 94 | * mispgetioc for example for a generating command (first line of SPL) ![mispgetioc](images/misp42_custom_command_mispgetioc_dashboard.png) 95 | * mispsearch for example for a streaming command to enrich events ![mispsearch](images/misp42_custom_command_mispsearch_dashboard.png) 96 | * misprest for example for the more versdatile wrapper of MISP API ![misprest](images/misp42_custom_command_misprest_dashboard.png) 97 | * Create event for example for an alert action ![misp_alert_create_evennt](images/misp42_alert_action_create_event_dashboard.png) 98 | 99 | ## Credits 100 | The creation of this app started from work done by https://github.com/xme/splunk/tree/master/getmispioc and the associated blog https://blog.rootshell.be/2017/10/31/splunk-custom-search-command-searching-misp-iocs/ for MISP interactions. 101 | 102 | ## Licence 103 | This app misp42splunk is licensed under the GNU Lesser General Public License v3.0. 104 | -------------------------------------------------------------------------------- /docs/mispalerts.md: -------------------------------------------------------------------------------- 1 | # Alerts to interact with MISP 2 | ## Create MISP event(s) 3 | When you create an alert, you may add an alert action to directly create events (or edit events) in MISP based on search results. 4 | This version of the app requires MISP 2.4.97 or later. 5 | You may also send alerts from the search pipeline |sendalert 6 | ![mispgetioc](../images/misp42_alert_action_create_event_dashboard.png) for example for an alert action 7 | 8 | ### collect results in Splunk 9 | You may search and prepare the results as a table with the following command 10 | ``` 11 | | rename field1 AS misp_attribute_name (prefix misp_ is removed & '_' are replaced by '-' ) 12 | | rename field2 AS fo_object_attribute_name (for file objects) 13 | | rename field3 AS eo_object_attribute_name (for email objects) 14 | | rename field4 AS no_object_attribute_name (for network connection objects) 15 | | eval misp_time=round(_time,0) | eval misp_info= |eval misp_tag= 16 | | table eventkey misp_time misp_info misp_tag to_ids misp_category misp_* fo_* eo_* no_* (etc.) 17 | ``` 18 | CAUTION: Splunk syntax does not like field names containing '-'. 19 | Do not forget to check the [object attribute names](https://github.com/MISP/misp-objects/). To use objects, you need to use an object prefix (fo_, no_, eo_) followed by the object attribute name as splunk field name. 20 | For example eo_from for the sender address in an email object. 21 | 22 | * Optional fields: 23 | - misp_time: the timestamp will be converted to YYYY-MM-DD for event date. if not provided, it is set to localtime. for example | eval misp_time = round(_time,0) 24 | - to_ids: if not defined, set to False 25 | - misp_category: if not defined, set to None and populated in relation with the type of attribute 26 | - eventkey: This string/id is used to group several rows of the results belonging to the same event (e.g. attributes of type email-src, email-subject). The actual value is not pushed to MISP. If not specified by row, this value might be overall defined for the alert - see below 27 | - misp_info: This string will be set in the Info field of MISP event. This value might be overall defined for the alert - see below 28 | - misp_tag: a CSV string of additional event tags (some can be set in the alert action form) 29 | 30 | 31 | ### create the alert and add alert_action to create events (form) 32 | Save your search as alert. Select "Alert to create MISP event(s)" as action 33 | Fill in the form to tune your alert to your needs. 34 | 35 | * Alert overall description: this section is for Splunk documentation 36 | - **title**: The title of this alert. 37 | - Description: The description to send with the alert. 38 | - **misp_instance**: the misp instance name as defined in misp42splunk_instances.conf 39 | * Global event parameters: the parameters will apply for all events created by this alert unless overwritten (see above) 40 | - eventid: the event you want to add attrributes and objects. Leave blank or put 0 to create new events (default) 41 | - unique: indicate the field containing the unique id to group several rows under a single event. If not defined an default eventkey will be generated and all results will be added to the same event. 42 | - info: This string will be set in the Info field of MISP event. If not defined, the Info field will contain 'malspam'. By default, it takes a copy of the description (token $description$) 43 | - **distribution**: Change the Distribution. Defaults to Your organisation only 44 | 45 | 46 | 47 | 48 | 49 | - **threatlevel**: Change the Threat Level. Defaults to Undefined 50 | 51 | 52 | 53 | 54 | - **analysis**: Change Analysis status. Default to Initial 55 | 56 | 57 | 58 | - **tlp**: WHITE-GREEN-AMBER-RED Change the TLP of the created alert. Defaults to TLP-Amber 59 | - **pap**: WHITE-GREEN-AMBER-RED Permissible Action Protocol 60 | - tags: comma-separated list of tags 61 | 62 | ### example to create events (sendalert) 63 | .... | table mips_ip fo_filename, fo_md5, fo_sha256 |sendalert misp_alert_create_event param.title="my alert" param.misp_instance=default_misp param.distribution=3 param.threatlevel=3 param.analysis=1 param.tlp="TLP_GREEN" param.pap="TLP_AMBER" 64 | 65 | ### logging 66 | logs are in /opt/splunk/var/log/splunk/misp_alert_create_event_modalert.log 67 | 68 | ## Alert for sighting 69 | 70 | ### search results with one field for timestamp (recommended) 71 | Build your search with as many fields as you want. One field should contain a valid timestamp. 72 | 73 | ### create the alert and add alert_action for sighting 74 | Save your search as alert. Select "Alert for sighting MISP attribute(s)" as action 75 | Fill in the form to tune your alert to your needs. 76 | 77 | IMPORTANT: for mode **byuuid** , only first uuid is kept. If the field is a multivalue field other entries are lost. If needed you may prepare uuid list in Splunk using mvdedup and mvexpand commands. 78 | 79 | * Global event parameters: the parameters will apply for all events created by this alert unless overwritten (see above) 80 | - **title**: title of the alert 81 | - description: description 82 | - **misp_instance**: the misp instance name as defined in misp42splunk_instances.conf 83 | - unique: indicate the field containing timestamps. If not defined, defaults is now() 84 | - **mode**; indicate if sighting is by __value__ or __by attribute uuid__ 85 | - **type**; indicate if sighting type is 86 | * Sighting type 0, the default sighting type using the default STIX interpretation of a sighting. 87 | * Sighting type 1, a false-positive sighting which means this sighting has been interpreted as a false-positive by the organisation. 88 | * Sighting type 2, an expiration sighting which defines when the sighted attributes is to be expired. 89 | 90 | ### example to create events (sendalert) 91 | ...|table ip |sendalert misp_alert_sighting param.title="my sighting alert" param.misp_instance=default_misp param.mode=byvalue param.type=0 92 | 93 | ### logging 94 | logs are in /opt/splunk/var/log/splunk/misp_alert_sighting_modalert.log 95 | 96 | -------------------------------------------------------------------------------- /docs/mispfetch.md: -------------------------------------------------------------------------------- 1 | # mispfetch 2 | ## Description 3 | 4 | Use the `mispfetch` command to pull events or attributes from a [MISP](https://www.misp-project.org/) instance and **append** to the current set. 5 | 6 | `mispfetch` is a very versatile command (like a Swiss knife) to pull information from MISP. 7 | - All keys supported by MISP endpoint `/events/restSearch` or `/attributes/restSearch` can be used to build the HTTP body. 8 | - In other words, any request that works with MISP REST client will work with `mispfetch`. 9 | - there are arguments to further filter or format the results. 10 | 11 | The simplest way is to create fields with the same names as the expected keys in the HTTP body and use `tojson` command to create an output field **misp\_http\_body**. 12 | 13 | ### mispfetch vs mispgetioc or mispgetevent 14 | - `mispfetch` 15 | * is a **streaming** command that **cannot** be on the first line of a search (or a sub-search). 16 | * all `mispfetch` arguments can be prepared on the SPL as fields before calling the custom command (see examples). 17 | * or passed on the same ligne as `mispfetch` (fields in the SPL have priority over the arguments on the command line). 18 | * therefore arguments values may be calculated based on the fields of the main search. 19 | - `mispgetioc`and `mispgetevent` 20 | * are **generating** commands. 21 | * they must be on the first line of an SPL. 22 | * arguments must be on the same ligne. They may be prepared with a subsearch but this is complex and without link with main search. 23 | 24 | ## Syntax 25 | #### | mispfetch 26 | > **misp_instance**=string 27 | > **misp_restsearch**=(attributes|events) 28 | > **misp_http_body**=JSON object 29 | > attribute_limit=int 30 | > expand_object=bool 31 | > getioc=bool 32 | > keep_galaxy=bool 33 | > limit=int 34 | > misp\_output\_mode=(JSON|native) 35 | > not_tags=string (, comma-separated) 36 | > only_to_ids=bool 37 | > page=int 38 | > pipesplit=bool 39 | > tags=string (, comma-separated) 40 | 41 | #### Required arguments 42 | With `mispfetch`, all arguments are defined as optional **but misp_instance must be a valid account name**. All other arguments have default values or are really optional. 43 | 44 | They can be set as field names (e.g. using `eval`) before calling `mispfetch`. If not defined as field names, they can be passed as arguments on the command line. A field value has priority over an argument following `| mispfetch` 45 | 46 | - **misp_instance** 47 | - **Syntax:** `misp_instance=` 48 | - **Description:** Specifies the MISP instance to use. The configuration must be defined in `local/misp42splunk_instances.conf`. 49 | 50 | - **misp_restsearch** 51 | - **Syntax:** misp_restsearch= 52 | - **Description:** define the restSearch endpoint. Either `events` or `attributes`. Default is `events`. 53 | 54 | - **misp\_http\_body** 55 | - **Syntax:** misp\_http\_body= 56 | - **Description:** A valid JSON request (use `tojson` to build it easily) 57 | 58 | - **misp\_output\_mode** 59 | - **Syntax:** mmisp\_output\_mode=(fields|json) 60 | - **Description:** define how to render on Splunk either as native tabular view (`fields`)or JSON object (`json`). Default: is `fields`. 61 | 62 | #### Optional arguments to query MISP 63 | - **attribute_limit** 64 | - **Syntax:** `attribute_limit= 65 | - **Description:**define the attribute_limit for max count of returned attributes for each MISP default. ; 0 = no limit. Default is 0. 66 | 67 | - **expand_object** 68 | - **Syntax:** `expand_object=` 69 | - **Description:** Expands object attributes to one attribute per line. Default is `false`. 70 | 71 | - **getioc** 72 | - **Syntax:** `getioc=` 73 | - **Description:** Retrieves the list of attributes along with the event. Default is `false`. 74 | 75 | - **keep_galaxy** 76 | - **Syntax:** `keep_galaxy=` 77 | - **Description:** Retains galaxy information in the output. Default is `false`. 78 | 79 | - **keep_related** 80 | - **Syntax:** `keep_related=` 81 | - **Description:** Includes related events per attribute in the output. Default is `false`. 82 | 83 | - **limit** 84 | - **Syntax:** `limit=` 85 | - **Description:** Limits the number of events retrieved. Default is `1000`. 86 | 87 | - **not_tags** 88 | - **Syntax:** `not_tags=` 89 | - **Description:** Comma-separated list of tags to exclude from the search. Wildcard is `%`. 90 | 91 | - **page** 92 | - **Syntax:** `page=` 93 | - **Description:** Specifies the page number for paginated results. Default is `0` (fetches all pages). 94 | 95 | - **pipesplit** 96 | - **Syntax:** `pipesplit=` 97 | - **Description:** Splits multivalue attributes into separate rows. Default is `true`. 98 | 99 | - **prefix** 100 | - **Syntax:** `prefix=` 101 | - **Description:** A string prefix for MISP keys. 102 | 103 | - **tags** 104 | - **Syntax:** `tags=` 105 | - **Description:** Comma-separated list of tags to include in the search. Wildcard is `%`. 106 | 107 | ## Usage 108 | 109 | The `mispfetch` command is a dataset streaming command that appends data pulled from MISP instance to the current dataset. 110 | 111 | ### Basic examples 112 | 113 | The minimum code to run the `mispfetch` is to set the argument `misp_instance`. 114 | 115 | | makeresults 116 | | eval misp_instance="misp_instance_name" 117 | | mispfetch 118 | 119 | This will run the command will all default values. 120 | 121 | A second example shows how argument misp\_http\_body can be build to make a query MISP. 122 | 123 | Any argument supported by MISP REST API can be used. See MISP REST API documentation. 124 | This example introduces how arguments defined above can be set from the SPL or as argument of `mispfetch` command. 125 | The field in SPL has priority over the argument passed to the `mispfetch` command 126 | 127 | | makeresults 128 | | eval misp_instance="misp_instance_name", published_time="1d", published="True" 129 | | tojson misp_instance, published_time, published output_field=misp_http_body 130 | | mispfetch getioc=1 limit=100 attribute_limit=1000 131 | 132 | The query is done on the endpoint `/events/restSearch`. It will return a maximum of 100 events published in the last day. 133 | Event attributes are also returned with a limit per event of 1000 attributes. 134 | 135 | The third example uses the endpoint `/attributes/restSearch`. 136 | 137 | | makeresults 138 | | eval misp_instance="misp_instance_name", published_time="1d", published="True" 139 | | eval misp_restsearch="attributes", limit=1000 140 | | tojson misp_instance, published_time, published output_field=misp_http_body 141 | | mispfetch getioc=1 limit=100 attribute_limit=1000 142 | 143 | The query will returns all attributes of events published in last day. Attributes will be retrieved by chunks of 1000 (`limit=1000`) iterating through all pages (default `page=0`) 144 | 145 | -------------------------------------------------------------------------------- /docs/mispgetevent.md: -------------------------------------------------------------------------------- 1 | # mispgetevent 2 | 3 | ## Description 4 | The `mispgetevent` command retrieves event data from a MISP instance based on various parameters and filters. It supports 2 output formats for easy integration and analysis in Splunk searches. 5 | 6 | ## Syntax 7 | ``` 8 | | mispgetevent misp_instance= 9 | [json_request=] [date=] [eventid=] 10 | [last=d|h|m] [publish_timestamp=d|h|m] [timestamp=d|h|m] 11 | [category=] [exclude_local_tags=] [expand_object=] [getioc=] 12 | [include_sightings=] [keep_galaxy=] [keep_related=] 13 | [limit=] [not_tags=] [output=] [page=] 14 | [pipesplit=] [prefix=] [published=] [tags=] 15 | [threat_level_id=] [to_ids=] [type=] [warning_list=] 16 | ``` 17 | 18 | ## Parameters 19 | ### Required Parameters 20 | - **misp_instance** 21 | - **Syntax:** `misp_instance=` 22 | - **Description:** Specifies the MISP instance to use. The configuration must be defined in `local/misp42splunk_instances.conf`. 23 | 24 | ### Optional Parameters 25 | - **json_request** 26 | - **Syntax:** `json_request=` 27 | - **Description:** A valid JSON request payload as defined by the MISP REST API. 28 | 29 | - **date** 30 | - **Syntax:** `date=` 31 | - **Description:** Filters events by a specific date or a range of dates. 32 | 33 | - **eventid** 34 | - **Syntax:** `eventid=` 35 | - **Description:** A list of event IDs or UUIDs. Multiple values can be comma-separated. 36 | 37 | - **last** 38 | - **Syntax:** `last=d|h|m` 39 | - **Description:** Retrieves events published within the last specified time interval (e.g., `5d`, `12h`, or `30m`). 40 | 41 | - **publish_timestamp** 42 | - **Syntax:** `publish_timestamp=d|h|m` 43 | - **Description:** relative publication duration in day(s), hour(s) or minute(s). 44 | 45 | - **timestamp** 46 | - **Syntax:** `timestamp=d|h|m` 47 | - **Description:** event timestamp (last change). 48 | 49 | - **category** 50 | - **Syntax:** `category=` 51 | - **Description:** Comma-separated list of categories to filter events. Wildcard is `%`. 52 | 53 | - **exclude_local_tags** 54 | - **Syntax:** `exclude_local_tags=` 55 | - **Description:** excludeLocalTags. Default is `false`. 56 | 57 | - **expand_object** 58 | - **Syntax:** `expand_object=` 59 | - **Description:** Expands object attributes to one attribute per line. Default is `false`. 60 | 61 | - **getioc** 62 | - **Syntax:** `getioc=` 63 | - **Description:** Retrieves the list of attributes along with the event. Default is `false`. 64 | 65 | - **include_sightings** 66 | - **Syntax:** `include_sightings=` 67 | - **Description:** Extends the response with Sightings DB results if enabled. Default is `true`. 68 | 69 | - **keep_galaxy** 70 | - **Syntax:** `keep_galaxy=` 71 | - **Description:** Retains galaxy information in the output. Default is `false`. 72 | 73 | - **keep_related** 74 | - **Syntax:** `keep_related=` 75 | - **Description:** Includes related events per attribute in the output. Default is `false`. 76 | 77 | - **limit** 78 | - **Syntax:** `limit=` 79 | - **Description:** Limits the number of events retrieved. Default is `1000`. 80 | 81 | - **not_tags** 82 | - **Syntax:** `not_tags=` 83 | - **Description:** Comma-separated list of tags to exclude from the search. Wildcard is `%`. 84 | 85 | - **output** 86 | - **Syntax:** `output=` 87 | - **Description:** Determines the output format: `fields` (default tabular view) or `json`. 88 | 89 | - **page** 90 | - **Syntax:** `page=` 91 | - **Description:** Specifies the page number for paginated results. Default is `0` (fetches all pages). 92 | 93 | - **pipesplit** 94 | - **Syntax:** `pipesplit=` 95 | - **Description:** Splits multivalue attributes into separate rows. Default is `true`. 96 | 97 | - **prefix** 98 | - **Syntax:** `prefix=` 99 | - **Description:** A string prefix for MISP keys. 100 | 101 | - **published** 102 | - **Syntax:** `published=` 103 | - **Description:** Filters only published events. 104 | 105 | - **tags** 106 | - **Syntax:** `tags=` 107 | - **Description:** Comma-separated list of tags to include in the search. Wildcard is `%`. 108 | 109 | - **threat\_level\_id** 110 | - **Syntax:** `threat_level_id=` 111 | - **Description:** Filters events by threat level (1-High, 2-Medium, 3-Low, 4-Undefined). 112 | 113 | - **to_ids** 114 | - **Syntax:** `to_ids=` 115 | - **Description:** Filters attributes with the `to_ids` flag set to true. 116 | 117 | - **type** 118 | - **Syntax:** `type=` 119 | - **Description:** Comma-separated list of types to include in the search. Wildcard is `%`. 120 | 121 | - **warning_list** 122 | - **Syntax:** `warning_list=` 123 | - **Description:** Filters out known values. Default is `true`. 124 | 125 | ## Usage 126 | The `mispgetevent` command must be the first in a search or sub-search. It retrieves event data and optionally the attributes in the MISP events and transforms for further processing. 127 | 128 | ## Examples 129 | 130 | ### Retrieve events by event ID 131 | ``` 132 | | mispgetevent misp_instance=default_misp eventid=477 category="Payload delivery,Network activity" type="sha256,ip-dst" 133 | ``` 134 | 135 | ### Retrieve events published in the last 10 days 136 | ``` 137 | | mispgetevent misp_instance=test output=json last=10d 138 | ``` 139 | 140 | ### Retrieve events using a custom date filter 141 | ``` 142 | | mispgetevent misp_instance=default_misp date="2023-12-01" tags="malware" 143 | ``` 144 | 145 | ### Retrieve events with expanded object attributes 146 | ``` 147 | | mispgetevent misp_instance=default_misp expand_object=true 148 | ``` 149 | 150 | ## Notes 151 | - Boolean parameters accept values like `1`, `y`, `Y`, `t`, `true`, `0`, `n`, `N`, `f`, or `false`. 152 | - One and only one of the following parameters must be set: `json_request`, `date`, `eventid`, `last`, `publish_timestamp` or `timestamp`. 153 | - Parameters like `tags` and `not_tags` support wildcards using `%`. 154 | 155 | ## Logging 156 | Logs are written to `misp42splunk.log` and can be accessed via the Splunk job inspector. You can configure the logging level for detailed debugging information. 157 | 158 | ## Version 159 | - **Current Version:** 5.0.0 160 | - **Authors:** Remi Seguy 161 | - **License:** LGPLv3 162 | 163 | -------------------------------------------------------------------------------- /docs/mispgetioc.md: -------------------------------------------------------------------------------- 1 | 2 | # mispgetioc 3 | 4 | ## Description 5 | The `mispgetioc` command retrieves Indicators of Compromise (IOCs) from a configured MISP (Malware Information Sharing Platform) instance. This command must be the first in a Splunk search or subsearch pipeline. The results are displayed in a structured table, ready for further processing or analysis within Splunk. 6 | 7 | ### Features 8 | - Retrieves attributes from MISP events based on various filters. 9 | - Supports filtering by category, type, tags, and time-related parameters. 10 | - Can use all MISP OpenAPI parameters like the MISP REST client. 11 | - Optionally includes additional metadata such as event tags, decaying scores, and organizational details. 12 | - Outputs data in a format suitable for immediate reuse in Splunk searches or returns JSON events. 13 | 14 | ## Syntax 15 | ```spl 16 | | mispgetioc misp_instance= 17 | [json_request=] [date=] [eventid=] 18 | [last=d|h|m] [publish_timestamp=d|h|m] [timestamp=d|h|m] 19 | [category=] [decay_score_threshold=] [decaying_model=] [exclude_decayed=] 20 | [expand_object=] [geteventtag=] [include_decay_score=] [include_deleted=] 21 | [include_sightings=] [limit=] [not_tags=] 22 | [output=] [page=] [pipesplit=] [prefix=] 23 | [tags=] [threat_level_id=] [to_ids=] [type=] [warning_list=] 24 | ``` 25 | 26 | ## Parameters 27 | ### Required Parameters 28 | - **misp_instance** 29 | - **Syntax:** `misp_instance=` 30 | - **Description:** Specifies the MISP instance to use. The configuration must be defined in `local/misp42splunk_instances.conf`. 31 | 32 | ### Optional Parameters 33 | - **json_request** 34 | - **Syntax:** `json_request=` 35 | - **Description:** A valid JSON request payload as defined by the MISP REST API. 36 | 37 | - **date** 38 | - **Syntax:** `date=` 39 | - **Description:** Filters events by a specific date or a range of dates. 40 | 41 | - **eventid** 42 | - **Syntax:** `eventid=` 43 | - **Description:** A list of event IDs or UUIDs. Multiple values can be comma-separated. 44 | 45 | - **last** 46 | - **Syntax:** `last=d|h|m` 47 | - **Description:** Retrieves events published within the last specified time interval (e.g., `5d`, `12h`, or `30m`). 48 | 49 | - **publish_timestamp** 50 | - **Syntax:** `publish_timestamp=d|h|m` 51 | - **Description:** relative publication duration in day(s), hour(s) or minute(s). 52 | 53 | - **timestamp** 54 | - **Syntax:** `timestamp=d|h|m` 55 | - **Description:** event timestamp (last change). 56 | 57 | - **category** 58 | - **Syntax:** `category=` 59 | - **Description:** Filters attributes by MISP categories. Use a comma-separated string. 60 | 61 | - **decay\_score\_threshold** 62 | - **Syntax:** `decay_score_threshold=` 63 | - **Description:** Overrides the threshold of the decaying model. 64 | 65 | - **decaying_model** 66 | - **Syntax:** `decaying_model=` 67 | - **Description:** Specifies the decaying model to use by ID. 68 | 69 | - **exclude_decayed** 70 | - **Syntax:** `exclude_decayed=` 71 | - **Description:** Excludes decayed attributes. Default is `false`. 72 | 73 | - **expand_object** 74 | - **Syntax:** `expand_object=` 75 | - **Description:** Expands object attributes to one attribute per line. Default is `false`. 76 | 77 | - **geteventtag** 78 | - **Syntax:** `geteventtag=` 79 | - **Description:** boolean includeEventTags. Default is `true`: event tags are returned in addition of any attribute tags. 80 | 81 | - **include\_decay\_score** 82 | - **Syntax:** `include_decay_score=` 83 | - **Description:** Includes decay scores in the output. Default is `false`. 84 | 85 | - **include_deleted** 86 | - **Syntax:** `include_deleted=` 87 | - **Description:** Includes deleted attributes. Default is `false`. 88 | 89 | - **include_sightings** 90 | - **Syntax:** `include_sightings=` 91 | - **Description:** Boolean includeSightings. Extend response with Sightings DB results if the module is enabled. Default is `true` 92 | 93 | - **limit** 94 | - **Syntax:** `limit=` 95 | - **Description:** Specifies the maximum number of results to return. Default is `1000`. 96 | 97 | - **not_tags** 98 | - **Syntax:** `not_tags=` 99 | - **Description:** Excludes attributes with specified tags. 100 | 101 | - **output** 102 | - **Syntax:** `output=` 103 | - **Description:** Defines the output format. Options are `fields` (tabular) or `json`. Default is `fields`. 104 | 105 | - **page** 106 | - **Syntax:** `page=` 107 | - **Description:** define the page when limit is not 0. Default is `0`: get all pages. 108 | 109 | - **pipesplit** 110 | - **Syntax:** `pipesplit=` 111 | - **Description:** Splits multivalue attributes into separate fields. Default is `true`. 112 | 113 | - **prefix** 114 | - **Syntax:** `prefix=` 115 | - **Description:** Adds a prefix to all MISP keys in the output. 116 | 117 | - **tags** 118 | - **Syntax:** `tags=` 119 | - **Description:** Filters attributes by specified tags. 120 | 121 | - **threat\_level\_id** 122 | - **Syntax:** `threat_level_id=` 123 | - **Description:**define the threat level (1-High, 2-Medium, 3-Low, 4-Undefined). 124 | 125 | - **to_ids** 126 | - **Syntax:** `to_ids=` 127 | - **Description:** Filters attributes with the `to_ids` flag set to true or false. 128 | 129 | - **type** 130 | - **Syntax:** `type=` 131 | - **Description:** Filters attributes by MISP types. Use a comma-separated string. 132 | 133 | - **warning_list** 134 | - **Syntax:** `warning_list=` 135 | - **Description:** boolean to filter out well known values. Default is `true`. 136 | 137 | ## Examples 138 | ### Example 1: Retrieve attributes from the last 10 days 139 | ```spl 140 | | mispgetioc misp_instance=test last=10d 141 | ``` 142 | - Retrieves attributes of all events published in the last 10 days. 143 | 144 | ### Example 2: Retrieve attributes by category and type 145 | ```spl 146 | | mispgetioc misp_instance=test date="2023-01-01,2023-01-31" category="Payload delivery,Network%" type="ip-dst" to_ids=true 147 | ``` 148 | - Retrieves attributes of type `ip-dst` and categories `Payload delivery` or starting with `Network` from events between January 1 and January 31, 2023. 149 | 150 | ## Notes 151 | - Boolean parameters accept values like `1`, `y`, `Y`, `t`, `true`, `0`, `n`, `N`, `f`, or `false`. 152 | - One and only one of the following parameters must be set: `json_request`, `date`, `eventid`, `last`, `publish_timestamp` or `timestamp`. 153 | - Parameters like `tags` and `not_tags` support wildcards using `%`. 154 | 155 | ## Logging 156 | Logs are written to `misp42splunk.log` and can be accessed via the Splunk job inspector. You can configure the logging level for detailed debugging information. 157 | 158 | ## Version 159 | - **Current Version:** 5.0.0 160 | - **Authors:** Remi Seguy 161 | - **License:** LGPLv3 162 | -------------------------------------------------------------------------------- /docs/misprest.md: -------------------------------------------------------------------------------- 1 | # custom command misprest 2 | 3 | This custom command is a wrapper to call MISP API endpoints 4 | 5 | ## [misprest-command] 6 | syntax = |misprest 7 | shortdesc = MISP REST API wrapper: provide misp_instance, method, target and a valid JSON request. (don't forget to escape " like this \") 8 | usage = public 9 | example1 = | misprest misp_instance=test method=POST target="/attributes/restSearch" json_request="{\"returnFormat\": \"json\", \"last\": \"20d\"}" 10 | comment1 = retrieve attributes of all events published in last 20 days and display as events 11 | example: 12 | 13 | ![mispgetioc](../images/misp42_custom_command_misprest_dashboard.png) 14 | 15 | # All params 16 | ## MANDATORY MISP instance for this search 17 | misp_instance = Option( 18 | doc=''' 19 | **Syntax:** **misp_instance=instance_name* 20 | **Description:** MISP instance parameters 21 | as described in local/misp42splunk_instances.conf.''', 22 | require=True) 23 | method = Option( 24 | doc=''' 25 | **Syntax:** **method=**** 26 | **Description:** method to use for API target DELETE GET PATCH POST PUT.''', 27 | require=True, validate=validators.Match("method", r"^(DELETE|GET|POST|PUT)$")) 28 | target = Option( 29 | doc=''' 30 | **Syntax:** **target=api_target**** 31 | **Description:**target of MISP API.''', 32 | require=True, validate=validators.Match("target", r"^/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+$")) 33 | 34 | ## Other params 35 | json_request = Option( 36 | doc=''' 37 | **Syntax:** **json_request=***JSON request* 38 | **Description:** JSON-formatted json_request.''', 39 | require=False, validate=validators.Match("json_request", r"^{.+}$")) 40 | limit = Option( 41 | doc=''' 42 | **Syntax:** **limit=**** 43 | **Description:**define the limit for each MISP search; 44 | default 1000. 0 = no pagination.''', 45 | require=False, validate=validators.Match("limit", r"^[0-9]+$")) 46 | page = Option( 47 | doc=''' 48 | **Syntax:** **page=**** 49 | **Description:**define the page for each MISP search; default 1.''', 50 | require=False, validate=validators.Match("page", r"^[0-9]+$")) 51 | 52 | -------------------------------------------------------------------------------- /docs/mispsearch.md: -------------------------------------------------------------------------------- 1 | 2 | ## custom command mispsearch 3 | This custom command is a streaming command that applies to each event. 4 | It searches for the value of the field selected in the command. 5 | If there are matches, additional multi-value fields (starting by misp_) are added to the dataset. 6 | 7 | The command syntax is as follow: 8 | 9 | search something... | mispsearch field= 10 | misp_instance= 11 | [onlyids=y|n] 12 | [gettag=y|n] 13 | 14 | **WARNING**: if the field contains null value, you may get a server error 500. You can use the splunk command __|fillnull field__ to avoid those errors (Thanks @jlachesk for solving this #54). 15 | 16 | one simple example: 17 | 18 | ... | field clientip | mispsearch field=clientip misp_instance=prod | dedup misp_json 19 | 20 | another example 21 | ... | field ip | mispsearch field=ip misp_instance=ops json_request="{\"returnFormat\": \"json\", \"withAttachments\": \"false\", \"includeEventUuid\": \"true\", \"includeEventTags\": \"true\"}" 22 | 23 | will add following fields 24 | 25 | misp_type 26 | misp_value 27 | misp_to_ids 28 | misp_category 29 | misp_attribute_uuid 30 | misp_event_id 31 | misp_tag (if gettag is set to yes) 32 | 33 | see also for example for a streaming command to enrich events 34 | ![mispsearch](../images/misp42_custom_command_mispsearch_dashboard.png) 35 | 36 | - The other parameters are optional 37 | + you may filter the results using 38 | - onlyids (boolean), 39 | + you may set gettag=Y to get the attribute tags 40 | + you may provide a full JSON request body. returnFormat is forced to 'json' and withAttachments to False 41 | 42 | ## All params 43 | 44 | misp_instance = Option( 45 | doc=''' 46 | **Syntax:** **misp_instance=instance_name* 47 | **Description:**MISP instance parameters as \ 48 | described in local/misp42splunk_instances.conf''', 49 | require=True) 50 | field = Option( 51 | doc=''' 52 | **Syntax:** **field=**** 53 | **Description:**Name of the field containing \ 54 | the value to search for.''', 55 | require=True, validate=validators.Fieldname()) 56 | to_ids = Option( 57 | doc=''' 58 | **Syntax:** **to_ids=**** 59 | **Description:** Boolean to search only attributes with to_ids set''', 60 | require=False, validate=validators.Boolean()) 61 | includeEventUuid = Option( 62 | doc=''' 63 | **Syntax:** **includeEventUuid=***y|Y|1|true|True|n|N|0|false|False* 64 | **Description:**Boolean to include event UUID(s) to results.''', 65 | require=False, validate=validators.Boolean()) 66 | includeEventTags = Option( 67 | doc=''' 68 | **Syntax:** **includeEventTags=***y|Y|1|true|True|n|N|0|false|False* 69 | **Description:**Boolean to include Event Tags to results.''', 70 | require=False, validate=validators.Boolean()) 71 | last = Option( 72 | doc=''' 73 | **Syntax:** **last=***d|h|m* 74 | **Description:**Publication duration in day(s), hour(s) or minute(s) 75 | to limit search scope only to published events in last X timerange.''', 76 | require=False, validate=validators.Match("last", r"^[0-9]+[hdm]$")) 77 | limit = Option( 78 | doc=''' 79 | **Syntax:** **limit=**** 80 | **Description:**define the limit for each MISP search; \ 81 | default 1000. 0 = no pagination.''', 82 | require=False, validate=validators.Match("limit", r"^[0-9]+$")) 83 | page = Option( 84 | doc=''' 85 | **Syntax:** **page=**** 86 | **Description:**define the page for each MISP search; default 1.''', 87 | require=False, validate=validators.Match("limit", r"^[0-9]+$")) 88 | json_request = Option( 89 | doc=''' 90 | **Syntax:** **json_request=***valid JSON request* 91 | **Description:**Valid JSON request''', 92 | require=False) -------------------------------------------------------------------------------- /images/misp42_add_misp_instance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/images/misp42_add_misp_instance.png -------------------------------------------------------------------------------- /images/misp42_alert_action_create_event_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/images/misp42_alert_action_create_event_dashboard.png -------------------------------------------------------------------------------- /images/misp42_custom_command_mispgetioc_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/images/misp42_custom_command_mispgetioc_dashboard.png -------------------------------------------------------------------------------- /images/misp42_custom_command_misprest_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/images/misp42_custom_command_misprest_dashboard.png -------------------------------------------------------------------------------- /images/misp42_custom_command_mispsearch_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/images/misp42_custom_command_mispsearch_dashboard.png -------------------------------------------------------------------------------- /images/misp42_ioc_retrosearch_kvstore_creation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/images/misp42_ioc_retrosearch_kvstore_creation.png -------------------------------------------------------------------------------- /images/misp42_ioc_retrosearch_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/images/misp42_ioc_retrosearch_lifecycle.png -------------------------------------------------------------------------------- /images/misp42_ioc_retrosearch_lookup_defintion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/images/misp42_ioc_retrosearch_lookup_defintion.png -------------------------------------------------------------------------------- /misp42splunk-5.0.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/misp42splunk-5.0.0.tar.gz -------------------------------------------------------------------------------- /package/README.txt: -------------------------------------------------------------------------------- 1 | Introduction 2 | 3 | This TA alows interaction between your [Splunk](www.splunk.com) search head (cluster) and one or several [MISP](http://www.misp-project.org/) instance(s). It is a versatile TA that acts as a wrapper of MISP API to either collect MISP information into Splunk (custom commands) or push information from Splunk to MISP (alert actions). 4 | The TA is designed to be easy to install, set up and maintain using the Splunk GUI. 5 | 6 | For additional information, please go to [misp42splunk](https://github.com/remg427/misp42splunk) -------------------------------------------------------------------------------- /package/README/misp_datatypes.csv.sample: -------------------------------------------------------------------------------- 1 | field_name,field_type,datatype,regex,description 2 | dest,attribute,ip-dst,, 3 | dest_ip,attribute,ip-dst,, 4 | domain,attribute,domain,, 5 | src,attribute,ip-src,, 6 | src_ip,attribute,ip-src,, 7 | 8 | -------------------------------------------------------------------------------- /package/README/restmap.conf.spec: -------------------------------------------------------------------------------- 1 | ## 2 | ## SPDX-FileCopyrightText: 2021 Splunk, Inc. 3 | ## SPDX-License-Identifier: LicenseRef-Splunk-1-2020 4 | ## 5 | ## 6 | 7 | [admin_external:] 8 | python.version = {default|python|python2|python3} 9 | * For Splunk 8.0.x and Python scripts only, selects which Python version to use. 10 | * Either "default" or "python" select the system-wide default Python version. 11 | * Optional. 12 | * Default: not set; uses the system-wide Python version. -------------------------------------------------------------------------------- /package/app.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "2.0.0", 3 | "info": { 4 | "title": "MISP42", 5 | "id": { 6 | "group": null, 7 | "name": "misp42splunk", 8 | "version": "5.0.0" 9 | }, 10 | "author": [ 11 | { 12 | "name": "Remi Seguy", 13 | "email": "remg427@gmail.com", 14 | "company": null 15 | } 16 | ], 17 | "releaseDate": "2025-01-31", 18 | "description": "The Splunk TA MISP42 can pull data from MISP (custom commands) or push to MISP (alert actions & misprest).", 19 | "classification": { 20 | "intendedAudience": "Security", 21 | "categories": [ 22 | "Security, Fraud & Compliance" 23 | ], 24 | "developmentStatus": "Production/Stable" 25 | }, 26 | "commonInformationModels": null, 27 | "license": { 28 | "name": "GPLv3", 29 | "text": null, 30 | "uri": "https://www.gnu.org/licenses/gpl-3.0.en.html" 31 | }, 32 | "privacyPolicy": { 33 | "name": null, 34 | "text": null, 35 | "uri": null 36 | }, 37 | "releaseNotes": { 38 | "name": "README", 39 | "text": "README.txt", 40 | "uri": "https://github.com/remg427/misp42splunk" 41 | } 42 | }, 43 | "dependencies": null, 44 | "tasks": null, 45 | "inputGroups": null, 46 | "incompatibleApps": null, 47 | "platformRequirements": null, 48 | "supportedDeployments": [ 49 | "_standalone", 50 | "_distributed", 51 | "_search_head_clustering" 52 | ], 53 | "targetWorkloads": [ 54 | "_search_heads" 55 | ] 56 | } -------------------------------------------------------------------------------- /package/appserver/static/alert_misp_alert_create_event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/package/appserver/static/alert_misp_alert_create_event.png -------------------------------------------------------------------------------- /package/appserver/static/alert_misp_alert_sighting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/package/appserver/static/alert_misp_alert_sighting.png -------------------------------------------------------------------------------- /package/appserver/static/test_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/package/appserver/static/test_alert.png -------------------------------------------------------------------------------- /package/bin/domain-ip_definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes": { 3 | "domain": { 4 | "categories": [ 5 | "Network activity", 6 | "External analysis" 7 | ], 8 | "description": "Domain name", 9 | "misp-attribute": "domain", 10 | "multiple": true, 11 | "ui-priority": 1 12 | }, 13 | "first-seen": { 14 | "description": "First time the tuple has been seen", 15 | "disable_correlation": true, 16 | "misp-attribute": "datetime", 17 | "ui-priority": 0 18 | }, 19 | "hostname": { 20 | "description": "Hostname related to the IP", 21 | "misp-attribute": "hostname", 22 | "ui-priority": 1 23 | }, 24 | "ip": { 25 | "categories": [ 26 | "Network activity", 27 | "External analysis" 28 | ], 29 | "description": "IP Address", 30 | "misp-attribute": "ip-dst", 31 | "multiple": true, 32 | "ui-priority": 1 33 | }, 34 | "last-seen": { 35 | "description": "Last time the tuple has been seen", 36 | "disable_correlation": true, 37 | "misp-attribute": "datetime", 38 | "ui-priority": 0 39 | }, 40 | "port": { 41 | "categories": [ 42 | "Network activity", 43 | "External analysis" 44 | ], 45 | "description": "Associated TCP port with the domain", 46 | "misp-attribute": "port", 47 | "multiple": true, 48 | "ui-priority": 1 49 | }, 50 | "registration-date": { 51 | "description": "Registration date of domain", 52 | "disable_correlation": false, 53 | "misp-attribute": "datetime", 54 | "ui-priority": 0 55 | }, 56 | "text": { 57 | "description": "A description of the tuple", 58 | "disable_correlation": true, 59 | "misp-attribute": "text", 60 | "recommended": false, 61 | "ui-priority": 1 62 | } 63 | }, 64 | "description": "A domain/hostname and IP address seen as a tuple in a specific time frame.", 65 | "meta-category": "network", 66 | "name": "domain-ip", 67 | "requiredOneOf": [ 68 | "ip", 69 | "domain", 70 | "hostname" 71 | ], 72 | "uuid": "43b3b146-77eb-4931-b4cc-b66c60f28734", 73 | "version": 11 74 | } -------------------------------------------------------------------------------- /package/bin/email_definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes": { 3 | "attachment": { 4 | "categories": [ 5 | "Payload delivery" 6 | ], 7 | "description": "Attachment", 8 | "misp-attribute": "email-attachment", 9 | "multiple": true, 10 | "ui-priority": 0 11 | }, 12 | "bcc": { 13 | "categories": [ 14 | "Payload delivery" 15 | ], 16 | "description": "Blind carbon copy", 17 | "disable_correlation": true, 18 | "misp-attribute": "email-dst", 19 | "multiple": true, 20 | "ui-priority": 1 21 | }, 22 | "bcc-display-name": { 23 | "categories": [ 24 | "Payload delivery" 25 | ], 26 | "description": "Display name of the blind carbon copy", 27 | "misp-attribute": "email-dst-display-name", 28 | "multiple": true, 29 | "ui-priority": 1 30 | }, 31 | "cc": { 32 | "categories": [ 33 | "Payload delivery" 34 | ], 35 | "description": "Carbon copy", 36 | "disable_correlation": true, 37 | "misp-attribute": "email-dst", 38 | "multiple": true, 39 | "ui-priority": 1 40 | }, 41 | "cc-display-name": { 42 | "categories": [ 43 | "Payload delivery" 44 | ], 45 | "description": "Display name of the carbon copy", 46 | "misp-attribute": "email-dst-display-name", 47 | "multiple": true, 48 | "ui-priority": 1 49 | }, 50 | "email-body": { 51 | "categories": [ 52 | "Payload delivery" 53 | ], 54 | "description": "Body of the email", 55 | "disable_correlation": true, 56 | "misp-attribute": "email-body", 57 | "multiple": true, 58 | "ui-priority": 1 59 | }, 60 | "email-body-attachment": { 61 | "description": "Body of the email as an attachment", 62 | "disable_correlation": true, 63 | "misp-attribute": "attachment", 64 | "ui-priority": 1 65 | }, 66 | "eml": { 67 | "description": "Full EML", 68 | "disable_correlation": true, 69 | "misp-attribute": "attachment", 70 | "ui-priority": 1 71 | }, 72 | "from": { 73 | "categories": [ 74 | "Payload delivery" 75 | ], 76 | "description": "Sender email address", 77 | "misp-attribute": "email-src", 78 | "multiple": true, 79 | "ui-priority": 1 80 | }, 81 | "from-display-name": { 82 | "categories": [ 83 | "Payload delivery" 84 | ], 85 | "description": "Display name of the sender", 86 | "misp-attribute": "email-src-display-name", 87 | "multiple": true, 88 | "ui-priority": 1 89 | }, 90 | "from-domain": { 91 | "categories": [ 92 | "Payload delivery" 93 | ], 94 | "description": "Sender domain address (when only the source domain is known)", 95 | "misp-attribute": "domain", 96 | "multiple": true, 97 | "ui-priority": 1 98 | }, 99 | "header": { 100 | "categories": [ 101 | "Payload delivery" 102 | ], 103 | "description": "Full headers", 104 | "disable_correlation": true, 105 | "misp-attribute": "email-header", 106 | "multiple": true, 107 | "ui-priority": 0 108 | }, 109 | "ip-src": { 110 | "description": "Source IP address of the email sender", 111 | "misp-attribute": "ip-src", 112 | "multiple": true, 113 | "ui-priority": 0 114 | }, 115 | "message-id": { 116 | "categories": [ 117 | "Payload delivery" 118 | ], 119 | "description": "Message ID", 120 | "disable_correlation": true, 121 | "misp-attribute": "email-message-id", 122 | "ui-priority": 0 123 | }, 124 | "mime-boundary": { 125 | "categories": [ 126 | "Payload delivery" 127 | ], 128 | "description": "MIME Boundary", 129 | "disable_correlation": true, 130 | "misp-attribute": "email-mime-boundary", 131 | "ui-priority": 0 132 | }, 133 | "msg": { 134 | "description": "Full MSG", 135 | "disable_correlation": true, 136 | "misp-attribute": "attachment", 137 | "ui-priority": 1 138 | }, 139 | "received-header-hostname": { 140 | "description": "Extracted hostname from parsed headers", 141 | "misp-attribute": "hostname", 142 | "multiple": true, 143 | "ui-priority": 0 144 | }, 145 | "received-header-ip": { 146 | "description": "Extracted IP address from parsed headers", 147 | "misp-attribute": "ip-src", 148 | "multiple": true, 149 | "ui-priority": 0 150 | }, 151 | "reply-to": { 152 | "categories": [ 153 | "Payload delivery" 154 | ], 155 | "description": "Email address the reply will be sent to", 156 | "misp-attribute": "email-reply-to", 157 | "multiple": true, 158 | "ui-priority": 1 159 | }, 160 | "reply-to-display-name": { 161 | "categories": [ 162 | "Payload delivery" 163 | ], 164 | "description": "Display name of the email address the reply will be sent to", 165 | "misp-attribute": "email-dst-display-name", 166 | "multiple": true, 167 | "ui-priority": 1 168 | }, 169 | "return-path": { 170 | "categories": [ 171 | "Payload delivery" 172 | ], 173 | "description": "Message return path", 174 | "misp-attribute": "email-src", 175 | "ui-priority": 1 176 | }, 177 | "screenshot": { 178 | "categories": [ 179 | "External analysis" 180 | ], 181 | "description": "Screenshot of email", 182 | "disable_correlation": true, 183 | "misp-attribute": "attachment", 184 | "ui-priority": 1 185 | }, 186 | "send-date": { 187 | "categories": [ 188 | "Other" 189 | ], 190 | "description": "Date the email has been sent", 191 | "disable_correlation": true, 192 | "misp-attribute": "datetime", 193 | "ui-priority": 0 194 | }, 195 | "subject": { 196 | "categories": [ 197 | "Payload delivery" 198 | ], 199 | "description": "Subject", 200 | "misp-attribute": "email-subject", 201 | "multiple": true, 202 | "ui-priority": 1 203 | }, 204 | "thread-index": { 205 | "categories": [ 206 | "Payload delivery" 207 | ], 208 | "description": "Identifies a particular conversation thread", 209 | "disable_correlation": true, 210 | "misp-attribute": "email-thread-index", 211 | "ui-priority": 0 212 | }, 213 | "to": { 214 | "categories": [ 215 | "Payload delivery" 216 | ], 217 | "description": "Destination email address", 218 | "disable_correlation": true, 219 | "misp-attribute": "email-dst", 220 | "multiple": true, 221 | "ui-priority": 1 222 | }, 223 | "to-display-name": { 224 | "categories": [ 225 | "Payload delivery" 226 | ], 227 | "description": "Display name of the receiver", 228 | "misp-attribute": "email-dst-display-name", 229 | "multiple": true, 230 | "ui-priority": 1 231 | }, 232 | "user-agent": { 233 | "description": "User Agent of the sender", 234 | "disable_correlation": true, 235 | "misp-attribute": "text", 236 | "ui-priority": 0 237 | }, 238 | "x-mailer": { 239 | "categories": [ 240 | "Payload delivery" 241 | ], 242 | "description": "X-Mailer generally tells the program that was used to draft and send the original email", 243 | "disable_correlation": true, 244 | "misp-attribute": "email-x-mailer", 245 | "ui-priority": 0 246 | } 247 | }, 248 | "description": "Email object describing an email with meta-information", 249 | "meta-category": "network", 250 | "name": "email", 251 | "requiredOneOf": [ 252 | "from", 253 | "from-display-name", 254 | "to", 255 | "to-display-name", 256 | "subject", 257 | "attachment", 258 | "message-id", 259 | "reply-to", 260 | "send-date", 261 | "mime-boundary", 262 | "thread-index", 263 | "header", 264 | "x-mailer", 265 | "return-path", 266 | "email-body", 267 | "email-body-attachment", 268 | "eml", 269 | "msg" 270 | ], 271 | "uuid": "a0c666e0-fc65-4be8-b48f-3423d788b552", 272 | "version": 19 273 | } -------------------------------------------------------------------------------- /package/bin/file_definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes": { 3 | "access-time": { 4 | "description": "The last time the file was accessed", 5 | "misp-attribute": "datetime", 6 | "ui-priority": 0 7 | }, 8 | "attachment": { 9 | "description": "A non-malicious file.", 10 | "misp-attribute": "attachment", 11 | "ui-priority": 1 12 | }, 13 | "authentihash": { 14 | "description": "Authenticode executable signature hash", 15 | "misp-attribute": "authentihash", 16 | "recommended": false, 17 | "ui-priority": 0 18 | }, 19 | "certificate": { 20 | "description": "Certificate value if the binary is signed with another authentication scheme than authenticode", 21 | "misp-attribute": "x509-fingerprint-sha1", 22 | "ui-priority": 0 23 | }, 24 | "compilation-timestamp": { 25 | "description": "Compilation timestamp", 26 | "misp-attribute": "datetime", 27 | "ui-priority": 0 28 | }, 29 | "creation-time": { 30 | "description": "Creation time of the file", 31 | "misp-attribute": "datetime", 32 | "ui-priority": 0 33 | }, 34 | "dom-hash": { 35 | "description": "Dom-hash of the file", 36 | "misp-attribute": "dom-hash", 37 | "ui-priority": 0 38 | }, 39 | "entropy": { 40 | "description": "Entropy of the whole file", 41 | "disable_correlation": true, 42 | "misp-attribute": "float", 43 | "ui-priority": 1 44 | }, 45 | "file-encoding": { 46 | "description": "Encoding format of the file", 47 | "disable_correlation": true, 48 | "misp-attribute": "text", 49 | "sane_default": [ 50 | "Adobe-Standard-Encoding", 51 | "Adobe-Symbol-Encoding", 52 | "Amiga-1251", 53 | "ANSI_X3.110-1983", 54 | "ASMO_449", 55 | "Big5", 56 | "Big5-HKSCS", 57 | "BOCU-1", 58 | "BRF", 59 | "BS_4730", 60 | "BS_viewdata", 61 | "CESU-8", 62 | "CP50220", 63 | "CP51932", 64 | "CSA_Z243.4-1985-1", 65 | "CSA_Z243.4-1985-2", 66 | "CSA_Z243.4-1985-gr", 67 | "CSN_369103", 68 | "DEC-MCS", 69 | "DIN_66003", 70 | "dk-us", 71 | "DS_2089", 72 | "EBCDIC-AT-DE", 73 | "EBCDIC-AT-DE-A", 74 | "EBCDIC-CA-FR", 75 | "EBCDIC-DK-NO", 76 | "EBCDIC-DK-NO-A", 77 | "EBCDIC-ES", 78 | "EBCDIC-ES-A", 79 | "EBCDIC-ES-S", 80 | "EBCDIC-FI-SE", 81 | "EBCDIC-FI-SE-A", 82 | "EBCDIC-FR", 83 | "EBCDIC-IT", 84 | "EBCDIC-PT", 85 | "EBCDIC-UK", 86 | "EBCDIC-US", 87 | "ECMA-cyrillic", 88 | "ES", 89 | "ES2", 90 | "EUC-KR", 91 | "Extended_UNIX_Code_Fixed_Width_for_Japanese", 92 | "Extended_UNIX_Code_Packed_Format_for_Japanese", 93 | "GB18030", 94 | "GB_1988-80", 95 | "GB2312", 96 | "GB_2312-80", 97 | "GBK", 98 | "GOST_19768-74", 99 | "greek7", 100 | "greek7-old", 101 | "greek-ccitt", 102 | "HP-DeskTop", 103 | "HP-Legal", 104 | "HP-Math8", 105 | "HP-Pi-font", 106 | "hp-roman8", 107 | "HZ-GB-2312", 108 | "IBM00858", 109 | "IBM00924", 110 | "IBM01140", 111 | "IBM01141", 112 | "IBM01142", 113 | "IBM01143", 114 | "IBM01144", 115 | "IBM01145", 116 | "IBM01146", 117 | "IBM01147", 118 | "IBM01148", 119 | "IBM01149", 120 | "IBM037", 121 | "IBM038", 122 | "IBM1026", 123 | "IBM1047", 124 | "IBM273", 125 | "IBM274", 126 | "IBM275", 127 | "IBM277", 128 | "IBM278", 129 | "IBM280", 130 | "IBM281", 131 | "IBM284", 132 | "IBM285", 133 | "IBM290", 134 | "IBM297", 135 | "IBM420", 136 | "IBM423", 137 | "IBM424", 138 | "IBM437", 139 | "IBM500", 140 | "IBM775", 141 | "IBM850", 142 | "IBM851", 143 | "IBM852", 144 | "IBM855", 145 | "IBM857", 146 | "IBM860", 147 | "IBM861", 148 | "IBM862", 149 | "IBM863", 150 | "IBM864", 151 | "IBM865", 152 | "IBM866", 153 | "IBM868", 154 | "IBM869", 155 | "IBM870", 156 | "IBM871", 157 | "IBM880", 158 | "IBM891", 159 | "IBM903", 160 | "IBM904", 161 | "IBM905", 162 | "IBM918", 163 | "IBM-Symbols", 164 | "IBM-Thai", 165 | "IEC_P27-1", 166 | "INIS", 167 | "INIS-8", 168 | "INIS-cyrillic", 169 | "INVARIANT", 170 | "ISO_10367-box", 171 | "ISO-10646-J-1", 172 | "ISO-10646-UCS-2", 173 | "ISO-10646-UCS-4", 174 | "ISO-10646-UCS-Basic", 175 | "ISO-10646-Unicode-Latin1", 176 | "ISO-10646-UTF-1", 177 | "ISO-11548-1", 178 | "ISO-2022-CN", 179 | "ISO-2022-CN-EXT", 180 | "ISO-2022-JP", 181 | "ISO-2022-JP-2", 182 | "ISO-2022-KR", 183 | "ISO_2033-1983", 184 | "ISO_5427", 185 | "ISO_5427:1981", 186 | "ISO_5428:1980", 187 | "ISO_646.basic:1983", 188 | "ISO_646.irv:1983", 189 | "ISO_6937-2-25", 190 | "ISO_6937-2-add", 191 | "ISO-8859-10", 192 | "ISO_8859-1:1987", 193 | "ISO-8859-13", 194 | "ISO-8859-14", 195 | "ISO-8859-15", 196 | "ISO-8859-16", 197 | "ISO-8859-1-Windows-3.0-Latin-1", 198 | "ISO-8859-1-Windows-3.1-Latin-1", 199 | "ISO_8859-2:1987", 200 | "ISO-8859-2-Windows-Latin-2", 201 | "ISO_8859-3:1988", 202 | "ISO_8859-4:1988", 203 | "ISO_8859-5:1988", 204 | "ISO_8859-6:1987", 205 | "ISO_8859-6-E", 206 | "ISO_8859-6-I", 207 | "ISO_8859-7:1987", 208 | "ISO_8859-8:1988", 209 | "ISO_8859-8-E", 210 | "ISO_8859-8-I", 211 | "ISO_8859-9:1989", 212 | "ISO-8859-9-Windows-Latin-5", 213 | "ISO_8859-supp", 214 | "iso-ir-90", 215 | "ISO-Unicode-IBM-1261", 216 | "ISO-Unicode-IBM-1264", 217 | "ISO-Unicode-IBM-1265", 218 | "ISO-Unicode-IBM-1268", 219 | "ISO-Unicode-IBM-1276", 220 | "IT", 221 | "JIS_C6220-1969-jp", 222 | "JIS_C6220-1969-ro", 223 | "JIS_C6226-1978", 224 | "JIS_C6226-1983", 225 | "JIS_C6229-1984-a", 226 | "JIS_C6229-1984-b", 227 | "JIS_C6229-1984-b-add", 228 | "JIS_C6229-1984-hand", 229 | "JIS_C6229-1984-hand-add", 230 | "JIS_C6229-1984-kana", 231 | "JIS_Encoding", 232 | "JIS_X0201", 233 | "JIS_X0212-1990", 234 | "JUS_I.B1.002", 235 | "JUS_I.B1.003-mac", 236 | "JUS_I.B1.003-serb", 237 | "KOI7-switched", 238 | "KOI8-R", 239 | "KOI8-U", 240 | "KS_C_5601-1987", 241 | "KSC5636", 242 | "KZ-1048", 243 | "latin-greek", 244 | "Latin-greek-1", 245 | "latin-lap", 246 | "macintosh", 247 | "Microsoft-Publishing", 248 | "MNEM", 249 | "MNEMONIC", 250 | "MSZ_7795.3", 251 | "Name", 252 | "NATS-DANO", 253 | "NATS-DANO-ADD", 254 | "NATS-SEFI", 255 | "NATS-SEFI-ADD", 256 | "NC_NC00-10:81", 257 | "NF_Z_62-010", 258 | "NF_Z_62-010_(1973)", 259 | "NS_4551-1", 260 | "NS_4551-2", 261 | "OSD_EBCDIC_DF03_IRV", 262 | "OSD_EBCDIC_DF04_1", 263 | "OSD_EBCDIC_DF04_15", 264 | "PC8-Danish-Norwegian", 265 | "PC8-Turkish", 266 | "PT", 267 | "PT2", 268 | "PTCP154", 269 | "SCSU", 270 | "SEN_850200_B", 271 | "SEN_850200_C", 272 | "Shift_JIS", 273 | "T.101-G2", 274 | "T.61-7bit", 275 | "T.61-8bit", 276 | "TIS-620", 277 | "TSCII", 278 | "UNICODE-1-1", 279 | "UNICODE-1-1-UTF-7", 280 | "UNKNOWN-8BIT", 281 | "US-ASCII", 282 | "us-dk", 283 | "UTF-16", 284 | "UTF-16BE", 285 | "UTF-16LE", 286 | "UTF-32", 287 | "UTF-32BE", 288 | "UTF-32LE", 289 | "UTF-7", 290 | "UTF-8", 291 | "Ventura-International", 292 | "Ventura-Math", 293 | "Ventura-US", 294 | "videotex-suppl", 295 | "VIQR", 296 | "VISCII", 297 | "windows-1250", 298 | "windows-1251", 299 | "windows-1252", 300 | "windows-1253", 301 | "windows-1254", 302 | "windows-1255", 303 | "windows-1256", 304 | "windows-1257", 305 | "windows-1258", 306 | "Windows-31J", 307 | "windows-874" 308 | ], 309 | "ui-priority": 0 310 | }, 311 | "filename": { 312 | "categories": [ 313 | "Payload delivery", 314 | "Artifacts dropped", 315 | "Payload installation", 316 | "External analysis" 317 | ], 318 | "description": "Filename on disk", 319 | "disable_correlation": true, 320 | "misp-attribute": "filename", 321 | "multiple": true, 322 | "ui-priority": 1 323 | }, 324 | "fullpath": { 325 | "description": "Complete path of the filename including the filename", 326 | "misp-attribute": "text", 327 | "multiple": true, 328 | "ui-priority": 0 329 | }, 330 | "imphash": { 331 | "description": "Hash (md5) calculated from the PE import table", 332 | "misp-attribute": "imphash", 333 | "ui-priority": 0 334 | }, 335 | "malware-sample": { 336 | "description": "The file itself (binary)", 337 | "misp-attribute": "malware-sample", 338 | "ui-priority": 1 339 | }, 340 | "md5": { 341 | "description": "[Insecure] MD5 hash (128 bits)", 342 | "misp-attribute": "md5", 343 | "recommended": false, 344 | "ui-priority": 1 345 | }, 346 | "mimetype": { 347 | "description": "Mime type", 348 | "disable_correlation": true, 349 | "misp-attribute": "mime-type", 350 | "ui-priority": 0 351 | }, 352 | "modification-time": { 353 | "description": "Last time the file was modified", 354 | "misp-attribute": "datetime", 355 | "ui-priority": 0 356 | }, 357 | "path": { 358 | "description": "Path of the filename complete or partial", 359 | "disable_correlation": true, 360 | "misp-attribute": "text", 361 | "multiple": true, 362 | "ui-priority": 0 363 | }, 364 | "pattern-in-file": { 365 | "categories": [ 366 | "Artifacts dropped", 367 | "Payload installation", 368 | "External analysis" 369 | ], 370 | "description": "Pattern that can be found in the file", 371 | "misp-attribute": "pattern-in-file", 372 | "multiple": true, 373 | "ui-priority": 1 374 | }, 375 | "sha1": { 376 | "description": "[Insecure] Secure Hash Algorithm 1 (160 bits)", 377 | "misp-attribute": "sha1", 378 | "recommended": false, 379 | "ui-priority": 1 380 | }, 381 | "sha224": { 382 | "description": "Secure Hash Algorithm 2 (224 bits)", 383 | "misp-attribute": "sha224", 384 | "recommended": false, 385 | "ui-priority": 0 386 | }, 387 | "sha256": { 388 | "description": "Secure Hash Algorithm 2 (256 bits)", 389 | "misp-attribute": "sha256", 390 | "ui-priority": 1 391 | }, 392 | "sha3-224": { 393 | "description": "Secure Hash Algorithm 3 (224 bits)", 394 | "misp-attribute": "sha3-224", 395 | "recommended": false, 396 | "ui-priority": 0 397 | }, 398 | "sha3-256": { 399 | "description": "Secure Hash Algorithm 3 (256 bits)", 400 | "misp-attribute": "sha3-256", 401 | "recommended": false, 402 | "ui-priority": 0 403 | }, 404 | "sha3-384": { 405 | "description": "Secure Hash Algorithm 3 (384 bits)", 406 | "misp-attribute": "sha3-384", 407 | "recommended": false, 408 | "ui-priority": 0 409 | }, 410 | "sha3-512": { 411 | "description": "Secure Hash Algorithm 3 (512 bits)", 412 | "misp-attribute": "sha3-512", 413 | "recommended": false, 414 | "ui-priority": 0 415 | }, 416 | "sha384": { 417 | "description": "Secure Hash Algorithm 2 (384 bits)", 418 | "misp-attribute": "sha384", 419 | "recommended": false, 420 | "ui-priority": 0 421 | }, 422 | "sha512": { 423 | "description": "Secure Hash Algorithm 2 (512 bits)", 424 | "misp-attribute": "sha512", 425 | "ui-priority": 1 426 | }, 427 | "sha512/224": { 428 | "description": "Secure Hash Algorithm 2 (224 bits)", 429 | "misp-attribute": "sha512/224", 430 | "recommended": false, 431 | "ui-priority": 0 432 | }, 433 | "sha512/256": { 434 | "description": "Secure Hash Algorithm 2 (256 bits)", 435 | "misp-attribute": "sha512/256", 436 | "recommended": false, 437 | "ui-priority": 0 438 | }, 439 | "size-in-bytes": { 440 | "description": "Size of the file, in bytes", 441 | "disable_correlation": true, 442 | "misp-attribute": "size-in-bytes", 443 | "ui-priority": 0 444 | }, 445 | "ssdeep": { 446 | "description": "Fuzzy hash using context triggered piecewise hashes (CTPH)", 447 | "misp-attribute": "ssdeep", 448 | "ui-priority": 0 449 | }, 450 | "state": { 451 | "description": "State of the file", 452 | "disable_correlation": true, 453 | "misp-attribute": "text", 454 | "multiple": true, 455 | "ui-priority": 0, 456 | "values_list": [ 457 | "Malicious", 458 | "Harmless", 459 | "Signed", 460 | "Revoked", 461 | "Expired", 462 | "Trusted" 463 | ] 464 | }, 465 | "telfhash": { 466 | "description": "telfhash - Symbol hash for ELF files.", 467 | "misp-attribute": "telfhash", 468 | "ui-priority": 0 469 | }, 470 | "text": { 471 | "description": "Free text value to attach to the file", 472 | "disable_correlation": true, 473 | "misp-attribute": "text", 474 | "multiple": true, 475 | "recommended": false, 476 | "ui-priority": 1 477 | }, 478 | "tlsh": { 479 | "description": "Fuzzy hash by Trend Micro: Locality Sensitive Hash", 480 | "misp-attribute": "tlsh", 481 | "ui-priority": 0 482 | }, 483 | "vhash": { 484 | "description": "vhash by VirusTotal", 485 | "misp-attribute": "vhash", 486 | "ui-priority": 0 487 | } 488 | }, 489 | "description": "File object describing a file with meta-information", 490 | "meta-category": "file", 491 | "name": "file", 492 | "requiredOneOf": [ 493 | "filename", 494 | "size-in-bytes", 495 | "authentihash", 496 | "ssdeep", 497 | "md5", 498 | "sha1", 499 | "sha224", 500 | "sha256", 501 | "sha384", 502 | "sha512", 503 | "sha512/224", 504 | "sha512/256", 505 | "sha3-224", 506 | "sha3-256", 507 | "sha3-384", 508 | "sha3-512", 509 | "tlsh", 510 | "telfhash", 511 | "imphash", 512 | "pattern-in-file", 513 | "certificate", 514 | "malware-sample", 515 | "attachment", 516 | "path", 517 | "fullpath" 518 | ], 519 | "uuid": "688c46fb-5edb-40a3-8273-1af7923e2215", 520 | "version": 25 521 | } -------------------------------------------------------------------------------- /package/bin/misp42splunk/modalert_misp_alert_create_event_helper.py: -------------------------------------------------------------------------------- 1 | 2 | # coding=utf-8 3 | 4 | # 5 | # Create Events in MISP from results of alerts 6 | # 7 | # Author: Remi Seguy 8 | # 9 | # Copyright: LGPLv3 (https://www.gnu.org/licenses/lgpl-3.0.txt) 10 | # Feel free to use the code, but please share the changes you've made 11 | from __future__ import print_function 12 | from misp_common import prepare_config, urllib_init_pool, urllib_request 13 | import csv 14 | import datetime 15 | import gzip 16 | import json 17 | import os 18 | import re 19 | import time 20 | import splunklib.client as client 21 | from io import open 22 | 23 | __author__ = "Remi Seguy" 24 | __license__ = "LGPLv3" 25 | __version__ = "5.0.0" 26 | __maintainer__ = "Remi Seguy" 27 | __email__ = "remg427@gmail.com" 28 | 29 | # encoding = utf-8 30 | 31 | def is_uuid_v4(field): 32 | uuid_v4_pattern = re.compile( 33 | r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', 34 | re.IGNORECASE 35 | ) 36 | return bool(uuid_v4_pattern.match(field)) 37 | 38 | def get_datatype_dict(helper, config, app_name): 39 | datatype_dict = dict() 40 | _SPLUNK_PATH = os.environ['SPLUNK_HOME'] 41 | directory = os.path.join( 42 | _SPLUNK_PATH, 'etc', 'apps', app_name, 'lookups' 43 | ) 44 | dt_filename = os.path.join(directory, 'misp_datatypes.csv') 45 | if os.path.exists(dt_filename): 46 | try: 47 | # open the file with gzip lib, start making alerts 48 | # can with statements fail gracefully?? 49 | fh = open(dt_filename, "rt") 50 | except ValueError: 51 | # Workaround for Python 2.7 under Windows 52 | fh = gzip.open(dt_filename, "r") 53 | if fh is not None: 54 | try: 55 | csv_reader = csv.DictReader(fh) 56 | for row in csv_reader: 57 | if 'field_name' in row and 'field_type' in row: 58 | if row['field_type'] == 'attribute': 59 | datatype_dict[row['field_name']] = row['datatype'] 60 | helper.log_info("[HC304] datatype_dict built from misp_datatypes.csv") 61 | except IOError: # file misp_datatypes.csv not readable 62 | helper.log_error('[HC305] file {} empty, malformed or not readable'.format( 63 | dt_filename 64 | )) 65 | return datatype_dict 66 | 67 | 68 | def prepare_alert(helper, app_name): 69 | # Get parameters for calling prepare_config 70 | instance = helper.get_param("misp_instance") 71 | sessionKey = helper.settings['session_key'] 72 | splunkService = client.connect(token=sessionKey) 73 | storage = splunkService.storage_passwords 74 | helper.log_debug("[AL-PA-D01] successfully retrieved storage object") 75 | config_args = prepare_config(helper, app_name, instance, storage, sessionKey) 76 | if config_args is None: 77 | return None 78 | 79 | alert_args = { 80 | 'tlp': str(helper.get_param("tlp").replace('_', ':')), 81 | 'pap': str(helper.get_param("pap").replace('_', ':')), 82 | 'eventid': str(helper.get_param("eventid") or "0"), 83 | 'eventkey': str(helper.get_param("unique") or "singleEvent"), 84 | 'info': str(helper.get_param("info") or "notable event"), 85 | 'published': helper.get_param("publish_on_creation") == "1", 86 | 'tags': str(helper.get_param("tags")) if helper.get_param("tags") else None, 87 | 'analysis': int(helper.get_param("analysis")), 88 | 'threatlevel': int(helper.get_param("threatlevel")), 89 | 'distribution': int(helper.get_param("distribution")) 90 | } 91 | config_args.update(alert_args) 92 | 93 | return config_args 94 | 95 | 96 | def init_object_template(helper, ot): 97 | try: 98 | # open object definition.json 99 | _SPLUNK_PATH = os.environ['SPLUNK_HOME'] 100 | # open misp.conf 101 | object_definition = os.path.join( 102 | _SPLUNK_PATH, 'etc', 'apps', 'misp42splunk', 103 | 'bin', ot + '_definition.json') 104 | with open(object_definition) as json_object: 105 | od = json.load(json_object) 106 | return od 107 | except IOError: 108 | helper.log_error("[AL-IOT-E01] file not found {}".format(object_definition)) 109 | return None 110 | 111 | 112 | def store_object_attribute(object_attributes, t, v, metadata=None): 113 | Attribute = metadata 114 | if t in object_attributes: 115 | Attribute['type'] = object_attributes[t]['misp-attribute'] 116 | Attribute['object_relation'] = t 117 | Attribute['value'] = v 118 | return Attribute 119 | 120 | 121 | def prepare_misp_events(helper, config, event_list): 122 | events = {} 123 | event_baseline = { 124 | 'threat_level_id': config['threatlevel'], 125 | 'analysis': config['analysis'], 126 | 'distribution': config['distribution'], 127 | 'published': config['published'], 128 | 'Attribute': [], 129 | 'Object': [] 130 | } 131 | 132 | data_type = get_datatype_dict(helper, config, 'misp42splunk') 133 | tags = [{'name': config['tlp']}, {'name': config['pap']}] 134 | if config['tags']: 135 | tags.extend({'name': tag} for tag in config['tags'].split(',') if tag not in tags) 136 | event_baseline['Tag'] = tags 137 | 138 | results = helper.get_events() 139 | for row in results: 140 | row = {key: value for key, value in row.items() if not key.startswith("__mv_")} 141 | # Get the specific eventkey if defined in Splunk search. 142 | eventkey = str(row.get(config['eventkey'], config['eventkey'])) 143 | # Get the specific eventid if defined in Splunk search. 144 | eventid = str(row.pop(config['eventid'], config['eventid'])) 145 | # Check if eventid contains only digits or a uuid v4 146 | if not (re.match(r'^\d+$', eventid) or is_uuid_v4(eventid)): 147 | eventid = "0" 148 | helper.log_info(f"[AL-PPE-I01] eventkey is {eventkey} and eventid is {eventid}") 149 | 150 | if eventkey in events: 151 | event = events[eventkey] 152 | row.pop('misp_date', None) 153 | row.pop('misp_info', None) 154 | else: 155 | event_list[eventkey] = eventid 156 | event = event_baseline.copy() 157 | event['date'] = datetime.datetime.fromtimestamp( 158 | int(row.pop('misp_date', time.time()))).strftime('%Y-%m-%d') 159 | event['info'] = row.pop('misp_info', config['info']) 160 | 161 | if config['distribution'] == 4: 162 | if 'misp_sg_id' in row: 163 | event['sharing_group_id'] = int(row.pop('misp_sg_id', 0)) 164 | else: 165 | helper.log_error("[AL-PPE-E01] Distribution is set to Sharing Group but no field misp_sg_id is provided") 166 | 167 | attributes = list(event['Attribute']) 168 | objects = list(event['Object']) 169 | 170 | tags = list(event['Tag']) 171 | if 'misp_tag' in row: 172 | tags.extend({'name': tag} for tag in row.pop('misp_tag').split(',') if tag not in tags) 173 | event['Tag'] = tags 174 | 175 | event['published'] = row.pop('misp_publish_on_creation', "0") == "1" 176 | 177 | attribute_baseline = dict() 178 | # collect attribute value and build type=value entry 179 | if 'misp_attribute_tag' in row: 180 | attribute_baseline['Tag'] = [{'name': tag} for tag in row.pop('misp_attribute_tag', '').split(',') if tag] 181 | 182 | if 'misp_category' in row: 183 | attribute_baseline['category'] = str(row.pop('misp_category')) 184 | 185 | if 'misp_comment' in row: 186 | attribute_baseline['comment'] = str(row.pop('misp_comment')) 187 | 188 | if 'misp_to_ids' in row: 189 | attribute_baseline['to_ids'] = row.pop('misp_to_ids', 'False') == 'True', 190 | 191 | if 'misp_first_seen' in row: # must be EPOCH UNIX 192 | attribute_baseline['first_seen'] = datetime.datetime.fromtimestamp( 193 | int(row.pop('misp_first_seen'))).strftime('%Y-%m-%dT%H:%M:%S.%f%z') 194 | 195 | if 'misp_last_seen' in row: # must be EPOCH UNIX 196 | attribute_baseline['last_seen'] = datetime.datetime.fromtimestamp( 197 | int(row.pop('misp_last_seen'))).strftime('%Y-%m-%dT%H:%M:%S.%f%z') 198 | 199 | # now we take KV pairs starting by misp_ 200 | # to add to event as attributes(s) of an object 201 | # or as single attribute(s) 202 | # 203 | 204 | fo_template = init_object_template(helper, 'file') 205 | eo_template = init_object_template(helper, 'email') 206 | no_template = init_object_template(helper, 'domain-ip') 207 | fo_attribute, eo_attribute, no_attribute = [], [], [] 208 | 209 | for key, value in row.items(): 210 | for single in str(value).split("\n"): 211 | if single.strip() and single != '0': 212 | attribute_metadata = attribute_baseline.copy() 213 | if key.startswith("misp_"): 214 | attribute_metadata['type'] = key.replace('misp_', '').replace('_', '-') 215 | attribute_metadata['value'] = single 216 | attributes.append(attribute_metadata) 217 | elif key.startswith("fo_"): 218 | object_attribute = store_object_attribute( 219 | fo_template['attributes'], key.replace('fo_', '').replace('_', '-'), single, metadata=attribute_metadata) 220 | if object_attribute: 221 | fo_attribute.append(object_attribute) 222 | elif key.startswith("eo_"): 223 | object_attribute = store_object_attribute( 224 | eo_template['attributes'], key.replace('eo_', '').replace('_', '-'), single, metadata=attribute_metadata) 225 | if object_attribute: 226 | eo_attribute.append(object_attribute) 227 | elif key.startswith("no_"): 228 | object_attribute = store_object_attribute( 229 | no_template['attributes'], key.replace('no_', '').replace('_', '-'), single, metadata=attribute_metadata) 230 | if object_attribute: 231 | no_attribute.append(object_attribute) 232 | elif key in data_type: 233 | attribute_metadata['type'] = data_type[key] 234 | attribute_metadata['value'] = single 235 | attributes.append(attribute_metadata) 236 | 237 | event['Attribute'] = attributes 238 | 239 | def add_misp_object(object_template, object_attributes): 240 | if object_attributes: 241 | objects.append({ 242 | 'template_version': object_template['version'], 243 | 'description': object_template['description'], 244 | 'meta-category': object_template['meta-category'], 245 | 'template_uuid': object_template['uuid'], 246 | 'name': object_template['name'], 247 | 'distribution': 5, 248 | 'Attribute': object_attributes 249 | }) 250 | 251 | add_misp_object(fo_template, fo_attribute) 252 | add_misp_object(eo_template, eo_attribute) 253 | add_misp_object(no_template, no_attribute) 254 | 255 | event['Object'] = objects 256 | events[eventkey] = event 257 | 258 | return events 259 | 260 | 261 | def process_misp_events(helper, config, results, event_list): 262 | connection, connection_status = urllib_init_pool(helper, config) 263 | 264 | def handle_response(response, success_msg, error_msg): 265 | if '_raw' not in response: 266 | helper.log_info(success_msg) 267 | else: 268 | helper.log_error(error_msg.format(response)) 269 | 270 | for eventkey, event in results.items(): 271 | helper.log_debug(f"[AL-PME-D01] payload is {event}") 272 | if event_list[eventkey] == "0": # create new event 273 | misp_url_create = f"{config['misp_url']}/events/add" 274 | response = urllib_request( 275 | helper, 276 | connection, 277 | 'POST', 278 | misp_url_create, 279 | event, 280 | config 281 | ) if connection else connection_status 282 | handle_response(response, 283 | f"[AL-PME-I02] INFO MISP event is successfully created. url={misp_url_create}", 284 | f"[AL-PME-E01] ERROR MISP event creation has failed. url={misp_url_create}, response={{}}" 285 | ) 286 | else: # edit existing eventid with Attribute and Object 287 | misp_url_edit = f"{config['misp_url']}/events/edit/{event_list[eventkey]}" 288 | edit_body = {'Attribute': event['Attribute'], 'Object': event['Object']} 289 | response = urllib_request( 290 | helper, 291 | connection, 292 | 'POST', 293 | misp_url_edit, 294 | edit_body, 295 | config 296 | ) if connection else connection_status 297 | handle_response(response, 298 | f"[AL-PME-I04] INFO MISP event is successfully edited. url={misp_url_edit}", 299 | f"[AL-PME-E02] ERROR MISP event edition has failed. url={misp_url_edit}, response={{}}" 300 | ) 301 | 302 | 303 | def process_event(helper, *args, **kwargs): 304 | """ 305 | # IMPORTANT 306 | # Do not remove the anchor macro:start and macro:end lines. 307 | # These lines are used to generate sample code. If they are 308 | # removed, the sample code will not be updated when configurations 309 | # are updated. 310 | 311 | [sample_code_macro:start] 312 | 313 | # The following example gets and sets the log level 314 | helper.set_log_level(helper.log_level) 315 | 316 | # The following example gets the alert action parameters 317 | # and prints them to the log 318 | title = helper.get_param("title") 319 | helper.log_info("title={}".format(title)) 320 | 321 | description = helper.get_param("description") 322 | helper.log_info("description={}".format(description)) 323 | 324 | eventid = helper.get_param("eventid") 325 | helper.log_info("eventid={}".format(eventid)) 326 | 327 | unique = helper.get_param("unique") 328 | helper.log_info("unique={}".format(unique)) 329 | 330 | info = helper.get_param("info") 331 | helper.log_info("info={}".format(info)) 332 | 333 | distribution = helper.get_param("distribution") 334 | helper.log_info("distribution={}".format(distribution)) 335 | 336 | threatlevel = helper.get_param("threatlevel") 337 | helper.log_info("threatlevel={}".format(threatlevel)) 338 | 339 | analysis = helper.get_param("analysis") 340 | helper.log_info("analysis={}".format(analysis)) 341 | 342 | tlp = helper.get_param("tlp") 343 | helper.log_info("tlp={}".format(tlp)) 344 | 345 | pap = helper.get_param("pap") 346 | helper.log_info("pap={}".format(pap)) 347 | 348 | tags = helper.get_param("tags") 349 | helper.log_info("tags={}".format(tags)) 350 | 351 | misp_instance = helper.get_param("misp_instance") 352 | helper.log_info("misp_instance={}".format(misp_instance)) 353 | 354 | 355 | # The following example adds two sample events ("hello", "world") 356 | # and writes them to Splunk 357 | # NOTE: Call helper.writeevents() only once after all events 358 | # have been added 359 | helper.addevent("hello", sourcetype="sample_sourcetype") 360 | helper.addevent("world", sourcetype="sample_sourcetype") 361 | helper.writeevents(index="summary", host="localhost", source="localhost") 362 | 363 | # The following example gets the events that trigger the alert 364 | events = helper.get_events() 365 | for event in events: 366 | helper.log_info("event={}".format(event)) 367 | 368 | # helper.settings is a dict that includes environment configuration 369 | # Example usage: helper.settings["server_uri"] 370 | helper.log_info("server_uri={}".format(helper.settings["server_uri"])) 371 | [sample_code_macro:end] 372 | """ 373 | 374 | helper.set_log_level(helper.log_level) 375 | helper.log_info("[AL-PE-I01] Alert action misp_alert_create_event started.") 376 | 377 | misp_app_name = "misp42splunk" 378 | misp_config = prepare_alert(helper, misp_app_name) 379 | if misp_config is None: 380 | helper.log_error("[AL-PE-E01] FATAL config dict not initialised") 381 | # data = { 382 | # '_time': time.time(), 383 | # '_raw': json.loads("AL-PE-E01] FATAL config dict not initialised") 384 | # } 385 | # yield data 386 | return 1 387 | else: 388 | helper.log_info("[AL-PE-I02] config dict is ready to use") 389 | event_list = {} 390 | events = prepare_misp_events(helper, misp_config, event_list) 391 | if events is not None: 392 | helper.log_info("[AL-PE-I03] Events dict is ready to use") 393 | process_misp_events(helper, misp_config, events, event_list) 394 | helper.log_info("[AL-PE-I04] Alert action misp_alert_create_event completed") 395 | else: 396 | helper.log_error("[AL-PE-E02] FATAL no event to process") 397 | return 2 398 | return 0 399 | -------------------------------------------------------------------------------- /package/bin/misp42splunk/modalert_misp_alert_sighting_helper.py: -------------------------------------------------------------------------------- 1 | 2 | # coding=utf-8 3 | 4 | # 5 | # Create Events in MISP from results of alerts 6 | # 7 | # Author: Remi Seguy 8 | # 9 | # Copyright: LGPLv3 (https://www.gnu.org/licenses/lgpl-3.0.txt) 10 | # Feel free to use the code, but please share the changes you've made 11 | 12 | """ 13 | { 14 | "values": "mandatory", 15 | "id": "mandatory", 16 | "type": "optional", 17 | "source": "optional", 18 | "timestamp": "optional", 19 | "date": "optional", 20 | "time": "optional" 21 | } 22 | """ 23 | 24 | from misp_common import prepare_config, urllib_init_pool, urllib_request 25 | import time 26 | import splunklib.client as client 27 | 28 | __author__ = "Remi Seguy" 29 | __license__ = "LGPLv3" 30 | __version__ = "5.0.0" 31 | __maintainer__ = "Remi Seguy" 32 | __email__ = "remg427@gmail.com" 33 | 34 | 35 | # encoding = utf-8 36 | def prepare_alert(helper, app_name): 37 | instance = helper.get_param("misp_instance") 38 | session_key = helper.settings['session_key'] 39 | splunk_service = client.connect(token=session_key) 40 | storage = splunk_service.storage_passwords 41 | 42 | config_args = prepare_config(helper, app_name, instance, storage, session_key) 43 | if config_args is None: 44 | return None 45 | 46 | alert_args = { 47 | 'mode': str(helper.get_param("mode")), 48 | 'type': int(helper.get_param("type")), 49 | 'source': str(helper.get_param("source")), 50 | 'timestamp': str(helper.get_param("timestamp") or "no_timestamp_field") 51 | } 52 | 53 | config_args.update(alert_args) 54 | return config_args 55 | 56 | 57 | def group_values(helper, rows, tslabel, default_ts, source, sighting_type): 58 | data_collection = dict() 59 | source_collection = dict() 60 | 61 | def get_timestamp(row): 62 | try: 63 | return str(int(row.pop(tslabel, default_ts))) 64 | except ValueError: 65 | return str(default_ts) 66 | 67 | def update_source(row, current_source): 68 | new_source = str(row.pop(source, current_source)) 69 | return new_source if new_source else current_source 70 | 71 | for row in rows: 72 | row = {key: value for key, value in row.items() if not key.startswith("__mv_")} 73 | 74 | timestamp = get_timestamp(row) 75 | source_collection[timestamp] = str(row.pop(source, source)) 76 | data = data_collection.get(timestamp, []) 77 | for key, value in row.items(): 78 | for single in str(value).split("\n"): 79 | if single.strip() and single != '0' and single not in data: 80 | data.append(single) 81 | 82 | data_collection[timestamp] = data 83 | 84 | sightings = [ 85 | { 86 | 'timestamp': int(ts), 87 | 'values': data, 88 | 'source': source_collection[ts], 89 | 'type': sighting_type 90 | } 91 | for ts, data in data_collection.items() 92 | ] 93 | 94 | return sightings 95 | 96 | 97 | def create_alert(helper, config): 98 | # get specific misp url and key if any (from alert configuration) 99 | misp_url = f"{config['misp_url']}/sightings/add" 100 | mode = config['mode'] 101 | sighting_type = config['type'] # 0, 1, 2 102 | default_ts = int(time.time()) 103 | results = helper.get_events() 104 | helper.log_info(f"[AS302] sighting mode is {mode}") 105 | 106 | if mode == 'byvalue': 107 | sightings = group_values(helper, results, config['timestamp'], default_ts, config['source'], sighting_type) 108 | else: 109 | sightings = [] 110 | for row in results: 111 | if 'misp_attribute_uuid' in row: 112 | timestamp = int(row.pop(config['timestamp'], default_ts)) 113 | source = str(row.pop(config['source'], config['source'])) 114 | value = row['misp_attribute_uuid'] 115 | helper.log_info(f"[AS303] sighting misp attribute uuid {value}") 116 | if value.strip() and value != '0': 117 | value = value.splitlines() 118 | for uuid in list(value): 119 | sighting = { 120 | 'id': uuid, 121 | 'source': source, 122 | 'timestamp': timestamp, 123 | 'type': sighting_type 124 | } 125 | sightings.append(sighting) 126 | 127 | connection, connection_status = urllib_init_pool(helper, config) 128 | for sighting in sightings: 129 | response = urllib_request(helper, connection, 'POST', misp_url, sighting, config) if connection else connection_status 130 | if '_raw' not in response: 131 | helper.log_info("[AL303] INFO MISP event is successfully edited.") 132 | else: 133 | helper.log_error(f"[AL304] ERROR MISP event edition has failed. url={misp_url}, data={response}") 134 | 135 | 136 | def process_event(helper, *args, **kwargs): 137 | """ 138 | # IMPORTANT 139 | # Do not remove the anchor macro:start and macro:end lines. 140 | # These lines are used to generate sample code. If they are 141 | # removed, the sample code will not be updated when configurations 142 | # are updated. 143 | 144 | [sample_code_macro:start] 145 | 146 | # The following example gets and sets the log level 147 | helper.set_log_level(helper.log_level) 148 | 149 | # The following example gets the alert action parameters 150 | # and prints them to the log 151 | title = helper.get_param("title") 152 | helper.log_info("title={}".format(title)) 153 | 154 | description = helper.get_param("description") 155 | helper.log_info("description={}".format(description)) 156 | 157 | timestamp = helper.get_param("timestamp") 158 | helper.log_info("timestamp={}".format(timestamp)) 159 | 160 | mode = helper.get_param("mode") 161 | helper.log_info("mode={}".format(mode)) 162 | 163 | type = helper.get_param("type") 164 | helper.log_info("type={}".format(type)) 165 | 166 | misp_instance = helper.get_param("misp_instance") 167 | helper.log_info("misp_instance={}".format(misp_instance)) 168 | 169 | 170 | # The following example adds two sample events ("hello", "world") 171 | # and writes them to Splunk 172 | # NOTE: Call helper.writeevents() only once after all events 173 | # have been added 174 | helper.addevent("hello", sourcetype="sample_sourcetype") 175 | helper.addevent("world", sourcetype="sample_sourcetype") 176 | helper.writeevents(index="summary", host="localhost", source="localhost") 177 | 178 | # The following example gets the events that trigger the alert 179 | events = helper.get_events() 180 | for event in events: 181 | helper.log_info("event={}".format(event)) 182 | 183 | # helper.settings is a dict that includes environment configuration 184 | # Example usage: helper.settings["server_uri"] 185 | helper.log_info("server_uri={}".format(helper.settings["server_uri"])) 186 | [sample_code_macro:end] 187 | """ 188 | 189 | helper.set_log_level(helper.log_level) 190 | helper.log_info("[AS101] Alert action misp_alert_sighting started.") 191 | 192 | # TODO: Implement your alert action logic here 193 | misp_app_name = "misp42splunk" 194 | misp_config = prepare_alert(helper, misp_app_name) 195 | if misp_config is None: 196 | helper.log_error("[AS102] FATAL config dict not initialised") 197 | return 1 198 | else: 199 | helper.log_info("[AS103] config dict is ready to use") 200 | create_alert(helper, misp_config) 201 | return 0 202 | -------------------------------------------------------------------------------- /package/bin/misp42splunk_declare.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import sys 4 | import re 5 | from os.path import dirname 6 | 7 | ta_name = 'misp42splunk' 8 | pattern = re.compile(r'[\\/]etc[\\/]apps[\\/][^\\/]+[\\/]bin[\\/]?$') 9 | new_paths = [path for path in sys.path if not pattern.search(path) or ta_name in path] 10 | new_paths.append(os.path.join(dirname(dirname(__file__)), "lib")) 11 | new_paths.insert(0, os.path.sep.join([os.path.dirname(__file__), ta_name])) 12 | sys.path = new_paths 13 | -------------------------------------------------------------------------------- /package/bin/misprest.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # 3 | # collect attributes or events as events in Splunk 4 | # Author: Remi Seguy 5 | # 6 | # Copyright: LGPLv3 (https://www.gnu.org/licenses/lgpl-3.0.txt) 7 | # Feel free to use the code, but please share the changes you've made 8 | 9 | from __future__ import absolute_import, division, print_function, unicode_literals 10 | import misp42splunk_declare 11 | 12 | import json 13 | import logging 14 | from misp_common import prepare_config, logging_level, urllib_init_pool, urllib_request 15 | from splunklib.searchcommands import dispatch, GeneratingCommand, Configuration, Option, validators 16 | import time 17 | import sys 18 | 19 | __author__ = "Remi Seguy" 20 | __license__ = "LGPLv3" 21 | __version__ = "5.0.0" 22 | __maintainer__ = "Remi Seguy" 23 | __email__ = "remg427@gmail.com" 24 | 25 | 26 | @Configuration(distributed=False) 27 | class MispRestCommand(GeneratingCommand): 28 | # MANDATORY MISP instance 29 | misp_instance = Option( 30 | doc=''' 31 | **Syntax:** **misp_instance=** *instance_name* 32 | **Description:** MISP instance parameters as described in local/misp42splunk_instances.conf. 33 | ''', 34 | require=True 35 | ) 36 | method = Option( 37 | doc=''' 38 | **Syntax:** **method=**** 39 | **Description:** method to use for API target DELETE GET POST PUT. 40 | **Default:** GET. 41 | ''', 42 | require=False, 43 | default="GET", 44 | validate=validators.Match("method", r"^(DELETE|GET|POST|PUT)$") 45 | ) 46 | json_request = Option( 47 | doc=''' 48 | **Syntax:** **json_request=***JSON request* 49 | **Description:** JSON-formatted json_request. 50 | ''', 51 | require=False, 52 | validate=validators.Match("json_request", r"^{.+}$") 53 | ) 54 | target = Option( 55 | doc=''' 56 | **Syntax:** **target=api_target**** 57 | **Description:** target of MISP API. 58 | **Default:** /servers/serverSettings 59 | ''', 60 | require=False, 61 | default="/servers/serverSettings", 62 | validate=validators.Match("target", r"^/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+$") 63 | ) 64 | 65 | def log_error(self, msg): 66 | logging.error(msg) 67 | 68 | def log_info(self, msg): 69 | logging.info(msg) 70 | 71 | def log_debug(self, msg): 72 | logging.debug(msg) 73 | 74 | def log_warn(self, msg): 75 | logging.warning(msg) 76 | 77 | def set_log_level(self): 78 | logging.root 79 | loglevel = logging_level('misp42splunk') 80 | logging.root.setLevel(loglevel) 81 | logging.error('[MR-201] logging level is set to %s', loglevel) 82 | logging.error('[MR-202] PYTHON VERSION: ' + sys.version) 83 | 84 | def generate(self): 85 | # loggging 86 | self.set_log_level() 87 | # Phase 1: Preparation 88 | misp_instance = self.misp_instance 89 | storage = self.service.storage_passwords 90 | config = prepare_config(self, 'misp42splunk', misp_instance, storage) 91 | if config is None: 92 | raise Exception("[MR-101] Sorry, no configuration for misp_instance={}".format(misp_instance)) 93 | 94 | try: 95 | body_dict = json.loads(self.json_request) 96 | except Exception: 97 | body_dict = {} 98 | config['method'] = self.method 99 | config['misp_url'] = config['misp_url'] + self.target 100 | 101 | connection, connection_status = urllib_init_pool(self, config) 102 | if connection is None: 103 | response = connection_status 104 | self.log_info('[MR-102] connection for {} failed'.format(config['misp_url'])) 105 | yield response 106 | else: 107 | response = urllib_request(self, connection, config['method'], config['misp_url'], body_dict, config) 108 | # response is 200 by this point or we would have thrown an exception 109 | data = {'_time': time.time(), '_raw': json.dumps(response)} 110 | yield data 111 | 112 | 113 | if __name__ == "__main__": 114 | dispatch(MispRestCommand, sys.argv, sys.stdin, sys.stdout, __name__) 115 | -------------------------------------------------------------------------------- /package/bin/mispsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | # 4 | # search for value in MISP and add some fields to the pipeline 5 | # 6 | # Author: Remi Seguy 7 | # 8 | # Copyright: LGPLv3 (https://www.gnu.org/licenses/lgpl-3.0.txt) 9 | # Feel free to use the code, but please share the changes you've made 10 | # 11 | from __future__ import absolute_import, division, print_function, unicode_literals 12 | import misp42splunk_declare 13 | 14 | from itertools import chain 15 | from splunklib.searchcommands import dispatch, StreamingCommand, Configuration, Option, validators 16 | from misp_common import prepare_config, logging_level, urllib_init_pool, get_attributes, map_attribute_table 17 | import json 18 | import logging 19 | import sys 20 | 21 | """ 22 | splunkhome = os.environ['SPLUNK_HOME'] 23 | 24 | # set logging 25 | filehandler = logging.FileHandler(splunkhome 26 | + "/var/log/splunk/misp42splunk.log", 'a') 27 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(filename)s \ 28 | %(funcName)s %(lineno)d %(message)s') 29 | filehandler.setFormatter(formatter) 30 | log = logging.getLogger() # root logger - Good to get it only once. 31 | for hdlr in log.handlers[:]: # remove the existing file handlers 32 | if isinstance(hdlr, logging.FileHandler): 33 | log.removeHandler(hdlr) 34 | log.addHandler(filehandler) # set the new handler 35 | # set the log level to INFO, DEBUG as the default is ERROR 36 | log.setLevel(logging.INFO) 37 | """ 38 | 39 | MISPSEARCH_INIT_PARAMS = { 40 | # optional parameters for request 41 | 'misp_http_body': None, 42 | 'limit': 10, 43 | 'page': 1, 44 | 'not_tags': None, 45 | 'tags': None, 46 | # optional parameters to format results 47 | 'pipesplit': True, 48 | 'prefix': 'misp_' 49 | } 50 | 51 | __author__ = "Remi Seguy" 52 | __license__ = "LGPLv3" 53 | __version__ = "5.0.0" 54 | __maintainer__ = "Remi Seguy" 55 | __email__ = "remg427@gmail.com" 56 | 57 | 58 | @Configuration(distributed=False) 59 | class MispSearchCommand(StreamingCommand): 60 | """ 61 | search in MISP for attributes matching the value of field. 62 | 63 | ##Syntax 64 | 65 | code-block:: 66 | mispsearch field= to_ids=y|n 67 | 68 | ##Description 69 | 70 | body = { 71 | "returnFormat": "mandatory", 72 | "page": "optional", 73 | "limit": "optional", 74 | "value": "optional", 75 | "type": "optional", 76 | "category": "optional", 77 | "org": "optional", 78 | "tags": "optional", 79 | "from": "optional", 80 | "to": "optional", 81 | "last": "optional", 82 | "eventid": "optional", 83 | "withAttachments": "optional", 84 | "uuid": "optional", 85 | "publish_timestamp": "optional", 86 | "timestamp": "optional", 87 | "enforceWarninglist": "optional", 88 | "to_ids": "optional", 89 | "deleted": "optional", 90 | "includeEventUuid": "optional", 91 | "includeEventTags": "optional", 92 | "event_timestamp": "optional", 93 | "threat_level_id": "optional", 94 | "eventinfo": "optional" 95 | } 96 | 97 | ##Example 98 | 99 | Search in MISP for value of fieldname r_ip (remote IP in proxy logs). 100 | 101 | code-block:: 102 | * | mispsearch field=r_ip 103 | 104 | """ 105 | 106 | misp_instance = Option( 107 | doc=''' 108 | **Syntax:** **misp_instance=instance_name* 109 | **Description:**MISP instance parameters as described in local/misp42splunk_instances.conf 110 | ''', 111 | require=True) 112 | field = Option( 113 | doc=''' 114 | **Syntax:** **field=**** 115 | **Description:**Name of the field containing the value to search for. 116 | ''', 117 | require=True, 118 | validate=validators.Fieldname()) 119 | misp_http_body = Option( 120 | doc=''' 121 | **Syntax:** misp_http_body= 122 | **Description:**Valid JSON request 123 | ''', 124 | require=False 125 | ) 126 | limit = Option( 127 | doc=''' 128 | **Syntax:** limit= 129 | **Description:**define the limit for each request to MISP. 0 = no pagination. 130 | **Default:** 1000 131 | ''', 132 | require=False, 133 | default=10, 134 | validate=validators.Integer() 135 | ) 136 | not_tags = Option( 137 | doc=''' 138 | **Syntax:** not_tags=,* 139 | **Description:**Comma(,)-separated string of tags to exclude. Wildcard is %. 140 | ''', 141 | require=False 142 | ) 143 | page = Option( 144 | doc=''' 145 | **Syntax:** **page=** ** 146 | **Description:** define the page when limit is not 0. 147 | **Default:** 0 - get all pages 148 | ''', 149 | require=False, 150 | default=1, 151 | validate=validators.Integer() 152 | ) 153 | pipesplit = Option( 154 | doc=''' 155 | **Syntax:** pipesplit=<1|y|Y|t|true|True|0|n|N|f|false|False> 156 | **Description:**Boolean to split multivalue attributes. 157 | **Default:** False 158 | ''', 159 | require=False, 160 | default=True, 161 | validate=validators.Boolean() 162 | ) 163 | prefix = Option( 164 | doc=''' 165 | **Syntax:** **prefix=** ** 166 | **Description:** string to use as prefix for misp keys 167 | **Default:** misp_ 168 | ''', 169 | require=False, 170 | default="misp_", 171 | validate=validators.Match("prefix", r"^[a-zA-Z][a-zA-Z0-9_]+$") 172 | ) 173 | tags = Option( 174 | doc=''' 175 | **Syntax:** tags=, 176 | **Description:**Comma(,)-separated string of tags to search for. Wildcard is %. 177 | ''', 178 | require=False 179 | ) 180 | 181 | def log_error(self, msg): 182 | logging.error(msg) 183 | 184 | def log_info(self, msg): 185 | logging.info(msg) 186 | 187 | def log_debug(self, msg): 188 | logging.debug(msg) 189 | 190 | def log_warn(self, msg): 191 | logging.warning(msg) 192 | 193 | def set_log_level(self): 194 | logging.root 195 | loglevel = logging_level('misp42splunk') 196 | logging.root.setLevel(loglevel) 197 | logging.error('[SE-101] logging level is set to %s', loglevel) 198 | logging.error('[SE-102] PYTHON VERSION: ' + sys.version) 199 | 200 | # get parameters from record or command line 201 | def get_parameter(self, obj, key, default=False): 202 | if key in obj: 203 | return obj[key] 204 | else: 205 | key_param = getattr(self, key) 206 | if key_param is not None: 207 | return key_param 208 | else: 209 | return default 210 | 211 | def check_true_bool(self, field): 212 | if field is True or str(field).lower() in ["1", "y", "t", "true"]: 213 | return True 214 | else: 215 | return False 216 | 217 | def create_se_params(self, last_record): 218 | field_values = dict( 219 | chain( 220 | map( 221 | lambda name: ( 222 | name, 223 | self.get_parameter( 224 | last_record, 225 | name, 226 | default=MISPSEARCH_INIT_PARAMS[name])), 227 | list(MISPSEARCH_INIT_PARAMS.keys()) 228 | ))) 229 | 230 | for field in list(MISPSEARCH_INIT_PARAMS.keys()): 231 | if isinstance(MISPSEARCH_INIT_PARAMS[field], bool): 232 | if field in field_values: 233 | field_values[field] = self.check_true_bool( 234 | field_values[field]) 235 | 236 | return field_values 237 | 238 | def stream(self, records): 239 | # loggging 240 | self.set_log_level() 241 | 242 | config = dict() 243 | # Phase 1: Preparation 244 | # extract parameters from last record from input set 245 | # Phase 1: Preparation 246 | try: 247 | dummy_record = {"misp_app": "misp42"} 248 | se_params = self.create_se_params(dummy_record) 249 | self.log_info('[SE-201] ms_params {}'.format(se_params)) 250 | except Exception as e: 251 | raise Exception("[SE-202] Sorry, ms_params failed {}".format(e)) 252 | 253 | misp_instance = self.misp_instance 254 | storage = self.service.storage_passwords 255 | config = prepare_config(self, 'misp42splunk', misp_instance, storage) 256 | if config is None: 257 | raise Exception("[SE-203] Sorry, no configuration for misp_instance={}".format(misp_instance)) 258 | config.update(se_params) 259 | config['misp_url'] = config['misp_url'] + '/attributes/restSearch' 260 | 261 | connection, connection_status = urllib_init_pool(self, config) 262 | 263 | config['fieldname'] = str(self.field) 264 | if self.prefix: 265 | config['prefix'] = self.prefix 266 | prefix = config['prefix'] 267 | for record in records: 268 | if config['fieldname'] in record: 269 | try: 270 | se_params = self.create_se_params(record) 271 | except Exception: 272 | pass 273 | config.update(se_params) 274 | 275 | if config['misp_http_body'] is None: 276 | # Force some values on JSON request 277 | body_dict = dict() 278 | else: 279 | body_dict = dict(json.loads(config['misp_http_body'])) 280 | # enforce returnFormat to JSON 281 | body_dict['returnFormat'] = 'json' 282 | body_dict['withAttachments'] = False 283 | body_dict['includeEventTags'] = True 284 | body_dict['includeEventUuid'] = True 285 | body_dict['includeSightings'] = body_dict.get('includeSightings', True) 286 | 287 | if 'tags' not in body_dict: 288 | if config['tags'] is not None or\ 289 | config['not_tags'] is not None: 290 | tags_criteria = {} 291 | if config['tags'] is not None: 292 | tags_criteria['OR'] = config['tags'].split(",") 293 | if config['not_tags'] is not None: 294 | tags_criteria['NOT'] = config['not_tags'].split(",") 295 | if tags_criteria is not None: 296 | body_dict['tags'] = tags_criteria 297 | 298 | config['limit'] = body_dict.get('limit', config['limit']) 299 | config['page'] = body_dict.get('page', config['page']) 300 | config['include_sightings'] = body_dict.get('includeSightings', True) # default true whithout additional param 301 | 302 | self.log_info('[SE-204] actual http body: {} '.format(json.dumps(body_dict))) 303 | 304 | value = record.get(config['fieldname'], None) 305 | if value not in [None, '', 0, "0", "%"]: 306 | body_dict['value'] = str(value) 307 | # search 308 | if connection: 309 | response_list = get_attributes(self, connection, config, body_dict) 310 | attribute_list = map_attribute_table(self, response_list, config) 311 | merged_list = dict() 312 | for attribute in attribute_list: 313 | for a_key, a_value in attribute.items(): 314 | if a_key not in merged_list: 315 | merged_list[a_key] = [] 316 | if a_value not in [None, '']: 317 | if isinstance(a_value, list): 318 | for a_value_item in a_value: 319 | if a_value_item not in merged_list[a_key]: 320 | merged_list[a_key].append(a_value_item) 321 | else: 322 | if a_value not in merged_list[a_key]: 323 | merged_list[a_key].append(a_value) 324 | 325 | attribute_key = prefix + 'attributes' 326 | if attribute_key not in merged_list: 327 | merged_list[attribute_key] = [] 328 | merged_list[attribute_key].append(attribute) 329 | record.update(merged_list) 330 | # return enriched record 331 | yield record 332 | 333 | 334 | if __name__ == "__main__": 335 | dispatch(MispSearchCommand, sys.argv, sys.stdin, sys.stdout, __name__) 336 | -------------------------------------------------------------------------------- /package/default/alert_actions.conf: -------------------------------------------------------------------------------- 1 | [misp_alert_create_event] 2 | python.version = python3 3 | label = MISP Create Event 4 | description = Create events in MISP (alert action) 5 | param._cam = {"task": ["Create", "Update"], "subject": ["endpoint"], "category": ["Cyber Security", "Threat Intelligence"], "technology": [{"version": ["2.4.119"], "product": "MISP", "vendor": "MISP"}], "supports_adhoc": true, "drilldown_uri": "search?q=search%20index%3D\"_internal\"&earliest=0&latest="} 6 | is_custom = 1 7 | payload_format = json 8 | icon_path = alert_misp_alert_create_event.png 9 | param.misp_instance = 10 | param.title = * 11 | param.description = * 12 | param.eventid = 13 | param.unique = 14 | param.info = 15 | param.distribution = 0 16 | param.threatlevel = 4 17 | param.analysis = 0 18 | param.tlp = TLP_AMBER 19 | param.pap = PAP_RED 20 | param.publish_on_creation = 0 21 | param.tags = 22 | 23 | [misp_alert_sighting] 24 | python.version = python3 25 | label = Alert for sighting MISP attribute(s) 26 | description = Increment sighting counters for attributes in MISP (alert action) 27 | param._cam = {"task": ["Create", "Update"], "subject": ["endpoint"], "category": ["Cyber Security", "Threat Intelligence"], "technology": [{"version": ["2.4.119"], "product": "MISP", "vendor": "MISP"}], "supports_adhoc": true, "drilldown_uri": "search?q=search%20index%3D\"_internal\"&earliest=0&latest="} 28 | is_custom = 1 29 | payload_format = json 30 | icon_path = alert_misp_alert_sighting.png 31 | param.misp_instance = 32 | param.title = * 33 | param.description = * 34 | param.unique = 35 | param.mode = byvalue 36 | param.type = 0 37 | -------------------------------------------------------------------------------- /package/default/collections.conf: -------------------------------------------------------------------------------- 1 | # http://docs.splunk.com/Documentation/CIM/4.13.0/User/Malware 2 | [kvmisp_filename] 3 | field.misp_category = string 4 | field.misp_datetime = string 5 | field.misp_description = string 6 | field.misp_event_id = number 7 | field.misp_filename = string 8 | field.misp_malware_sample = string 9 | field.misp_md5 = string 10 | field.misp_object_id = number 11 | field.misp_orgc_id = number 12 | field.misp_sha1 = string 13 | field.misp_sha256 = string 14 | field.misp_sha512 = string 15 | field.misp_ssdeep = string 16 | field.misp_size_in_bytes = string 17 | field.misp_tag = string 18 | field.misp_text = string 19 | field.misp_timestamp = number 20 | field.misp_to_ids = string 21 | field.misp_attribute_id = string 22 | field.misp_attribute_uuid = string 23 | replicate = false 24 | 25 | # http://docs.splunk.com/Documentation/CIM/4.13.0/User/Web 26 | [kvmisp_web] 27 | field.misp_category = string 28 | field.misp_datetime = string 29 | field.misp_description = string 30 | field.misp_domain = string 31 | field.misp_event_id = number 32 | field.misp_hostname = string 33 | field.misp_ip_dst = string 34 | field.misp_ip_src = string 35 | field.misp_object_id = number 36 | field.misp_orgc_id = number 37 | field.misp_tag = string 38 | field.misp_text = string 39 | field.misp_timestamp = number 40 | field.misp_to_ids = string 41 | field.misp_url = string 42 | field.misp_user_agent = string 43 | field.misp_user_agent_length = number 44 | field.misp_attribute_id = string 45 | field.misp_attribute_uuid = string 46 | replicate = false 47 | 48 | # http://docs.splunk.com/Documentation/CIM/4.13.0/User/Email 49 | [kvmisp_email] 50 | field.misp_category = string 51 | field.misp_datetime = string 52 | field.misp_description = string 53 | field.misp_email_attachment = string 54 | field.misp_email_src = string 55 | field.misp_email_src_display_name = string 56 | field.misp_email_subject = string 57 | field.misp_event_id = number 58 | field.misp_object_id = number 59 | field.misp_orgc_id = number 60 | field.misp_tag = string 61 | field.misp_text = string 62 | field.misp_timestamp = number 63 | field.misp_to_ids = string 64 | field.misp_attribute_id = string 65 | field.misp_attribute_uuid = string 66 | replicate = false 67 | -------------------------------------------------------------------------------- /package/default/commands.conf: -------------------------------------------------------------------------------- 1 | [mispfetch] 2 | python.version = python 3 | filename = mispfetch.py 4 | chunked = true 5 | 6 | [mispgetevent] 7 | python.version = python 8 | filename = mispgetevent.py 9 | chunked = true 10 | 11 | [mispgetioc] 12 | python.version = python 13 | filename = mispgetioc.py 14 | chunked = true 15 | 16 | [mispsearch] 17 | python.version = python 18 | filename = mispsearch.py 19 | chunked = true 20 | 21 | [misprest] 22 | python.version = python 23 | filename = misprest.py 24 | chunked = true 25 | -------------------------------------------------------------------------------- /package/default/data/ui/nav/default.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package/default/data/ui/views/configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /package/default/data/ui/views/inputs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /package/default/data/ui/views/misp_alert_create_event.xml: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | | makeresults | eval misp_domain="www.example.com" 13 | 14 | 15 | Adaptative alert action to create or update MISP events - check https://github.com/remg427/misp42splunk/ for any update 16 |
17 | 18 | 19 | misp_instance 20 | misp_instance 21 | 22 | | rest /services/configs/conf-misp42splunk_instances 23 | | rename eai:acl.app as app, title as misp_instance 24 | | fields misp_instance 25 | -24h@h 26 | now 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | param.eventid="$eventid$" 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | param.unique="$unique$" 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | param.info="$info$" 68 | 69 | 70 | 71 | 72 | 73 | Sharing Group 74 | All communities 75 | This community only 76 | Connected communities 77 | Your organisation only 78 | 0 79 | 0 80 | 81 | 82 | 83 | Medium 84 | Undefined 85 | High 86 | Low 87 | 4 88 | 4 89 | 90 | 91 | 92 | Ongoing 93 | Complete 94 | Initial 95 | 0 96 | 0 97 | 98 | 99 | 100 | tlp:white 101 | tlp:clear 102 | tlp:green 103 | tlp:amber 104 | tlp:amber+strict 105 | tlp:red 106 | tlp:unclear 107 | tlp:ex:chr 108 | tlp_amber 109 | tlp_amber 110 | 111 | 112 | 113 | AMBER 114 | WHITE 115 | GREEN 116 | RED 117 | PAP_AMBER 118 | PAP_AMBER 119 | 120 | 121 | 122 | No (0) 123 | Yes (1) 124 | 0 125 | 0 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | param.tags="$tags$" 135 | 136 | 137 | 138 | 139 | 140 | 141 | -15m 142 | now 143 | 144 | 145 | 146 | 147 | | makeresults | eval misp_domain="www.example.com" 148 | 149 | 150 | 151 | Run 152 | Preparation 153 | 154 | 155 | 156 | 157 | 158 | False 159 | False 160 | 161 |
162 | 163 | 164 |

In addition to parameters above, you can also set additional inline fields.

165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 215 | 216 | 217 | 218 | 219 | 220 |
field nameDescription
misp_attribute_tagComma(,)-separated string of tags to add to attributes defined on the same row.
misp_categoryUse this field to define the category for attributes defined on the same row. This way you can help MISP to create the proper attribute when a same type is avalaible for several categories
misp_commentInline comment string for attributes on the same row.
misp_dateInline misp_date EPOCH value to set the date of the MISP event.
misp_first_seenInline EPOCH value to set the MISP event first seen.
misp_infoIf provided on the row, it replaces the string provided in alert configuration form for a given event. To work, misp_info must be provided at least on the last row of an event (see unique) otherwise the value will be set to the test set in alert form.
misp_last_seenInline EPOCH value to set the MISP event last seen.
misp_publish_on_creationString '1' or '0' to dynamically publish or not MISP events on creation. This field overwrites for the current MISP event the staic value defined in alert form.
misp_sg_idIf distribution is set to Sharing Group, field 'misp_sg_id' must be present and provide Sharing Group ID on target MISP instance.
misp_tagComma(,)-separated string of tags to dynamically add to event tag list (in additional to static list provided in alert form). Only new tags are added.
misp_timeIt must contain a timestamp. The first value found for a given event is taken into account to define MISP event date %Y-%m-%d. 213 | (misp_time on additional rows belonging to the same MISP event are discarded). 214 | If not provided, MISP event date is set to today().
misp_to_idsString 'True' or 'False' (case sensitive) to define to_ids for all attributes on the same row. (If not provided, default values defined in MISP will apply to each attribute).
221 | 222 |
223 | 224 | 225 | Create an alert in MISP instance $misp_instance$. If result looks good, select "run" above 226 | 227 | 228 | $sample_query$ 229 | | eval param_eventid="$eventid$", param_unique="$unique$", param_info="$info$", param_tags="$tags$" 230 | | eval sendalert_cmd="sendalert misp_alert_create_event param.misp_instance=$misp_instance$ param.title=\"$title$\" param.description=\"$description$\" param.distribution=$distribution$ param.threatlevel=$threatlevel$ param.analysis=$analysis$ param.tlp=$tlp$ param.pap=$pap$ param.publish_on_creation=$publish_on_creation$ " 231 | | eval sendalert_cmd=if(param_eventid!="", sendalert_cmd+" param.eventid=\""+param_eventid+"\"",sendalert_cmd) 232 | | eval sendalert_cmd=if(param_unique!="", sendalert_cmd+" param.unique=\""+param_unique+"\"", sendalert_cmd) 233 | | eval sendalert_cmd=if(param_info!="", sendalert_cmd+" param.info=\""+param_info+"\"",sendalert_cmd) 234 | | eval sendalert_cmd=if(param_tags!="", sendalert_cmd+" param.tags=\""+param_tags+"\"",sendalert_cmd) 235 | | fields - param_* 236 | -24h@h 237 | now 238 | 1 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 |
249 |
250 |
251 | 252 | 253 | Create an alert in MISP. Once the result is displayed below, an alert should have been created in MISP instance $misp_instance$. If not check logs below 254 | 255 | 256 | $sample_query$ 257 | | eval switch="$launch_alert$" 258 | | where switch=="True" 259 | | fields - switch 260 | | sendalert misp_alert_create_event param.misp_instance=$misp_instance$ param.title="$title$" param.description="$description$" $misp_eventid$ $misp_unique$ $misp_info$ param.distribution=$distribution$ param.threatlevel=$threatlevel$ param.analysis=$analysis$ param.tlp="$tlp$" param.pap="$pap$" param.publish_on_creation="$publish_on_creation$" $misp_tags$ 261 | -24h@h 262 | now 263 | 1 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 |
274 |
275 |
276 | 277 | 278 | Logs related to misp_alert_create_event 279 | 280 | 281 | (index=_* OR index=cim_*) (sourcetype="*misp42*") 282 | -1h@h 283 | now 284 | 1 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 |
295 |
296 |
297 |
-------------------------------------------------------------------------------- /package/default/data/ui/views/misp_alert_logs.xml: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | -60m@m 8 | now 9 | 10 | 11 |
12 | 13 | 14 | Logs related to MISP42 alert actions 15 | 16 | 17 | (index=_* OR index=cim_*) (sourcetype="*misp42*") 18 | $GlobalTimePicker.earliest$ 19 | $GlobalTimePicker.latest$ 20 | 1 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
-------------------------------------------------------------------------------- /package/default/data/ui/views/misp_alert_sighting.xml: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | | makeresults | eval misp_domain="www.example.com" 11 | 12 | 13 | Adaptative alert action to increase sightings counters on attributes of MISP events - check https://github.com/remg427/misp42splunk/ for any update 14 |
15 | 16 | 17 | misp_instance 18 | misp_instance 19 | 20 | | rest /services/configs/conf-misp42splunk_instances 21 | | rename eai:acl.app as app, title as misp_instance 22 | | fields misp_instance 23 | -24h@h 24 | now 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | param.description="$description$" 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | param.unique="$unique$" 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | param.source="$source$" 63 | 64 | 65 | 66 | 67 | 68 | Sighting for matching values 69 | Sighting for matching attributes UUID 70 | byvalue 71 | byvalue 72 | 73 | 74 | 75 | Sighting type 0 (default STIX interpretation of a sighting). 76 | Sighting type 1 - this sighting has been interpreted as a false-positive by the organisation. 77 | Sighting type 2, expiration sighting which defines when the sighted attributes is to be expired. 78 | 0 79 | 0 80 | 81 | 82 | 83 | 84 | -15m 85 | now 86 | 87 | 88 | 89 | 90 | | makeresults | eval misp_domain="www.example.com" 91 | 92 | 93 | 94 | Run 95 | Preparation 96 | 97 | 98 | 99 | 100 | 101 | False 102 | False 103 | 104 |
105 | 106 | 107 | 108 |

If you select by mode 'byuuid' Sighting for matching attributes UUID you need to have a field 'uuid' containing attribute UUID.

109 | 110 |
111 |
112 | 113 | 114 | Increment sightings counters '$mode$' in MISP instance $misp_instance$. If result looks good, select "run" above 115 | 116 | 117 | $sample_query$ 118 | | eval param_description="$description$", param_unique="$unique$", param_source="$source$" 119 | | eval sendalert_cmd="sendalert misp_alert_sighting param.misp_instance=$misp_instance$ param.title=\"$title$\" param.mode=\"$mode$\" param.type=$type$" 120 | | eval sendalert_cmd=if(param_description!="", sendalert_cmd + " param.description=\""+ param_description + "\"",sendalert_cmd) 121 | | eval sendalert_cmd=if(param_unique!="", sendalert_cmd+" param.unique=\""+param_unique+"\"", sendalert_cmd) 122 | | eval sendalert_cmd=if(param_source!="", sendalert_cmd+" param.source=\""+param_source+"\"",sendalert_cmd) 123 | | fields - param_* 124 | -24h@h 125 | now 126 | 1 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 |
137 |
138 |
139 | 140 | 141 | Create an alert in MISP. Once the result is displayed below, an alert should have been created in MISP instance $misp_instance$. If not check logs below 142 | 143 | 144 | $sample_query$ 145 | | eval switch="$launch_alert$" 146 | | where switch=="True" 147 | | fields - switch 148 | | sendalert misp_alert_sighting misp_alert_sighting param.misp_instance=$misp_instance$ param.title="$title$" param.mode=$mode$ param.type=$type$ $misp_description$ $misp_unique$ $misp_source$ 149 | -24h@h 150 | now 151 | 1 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 |
162 |
163 |
164 | 165 | 166 | Logs related to misp_alert_sighting 167 | 168 | 169 | (index=_* OR index=cim_*) sourcetype="ta:misp42:log" source="*/misp_alert_sighting_modalert.log" 170 | -1h@h 171 | now 172 | 1 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 |
183 |
184 |
185 |
-------------------------------------------------------------------------------- /package/default/data/ui/views/misp_overview.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | This dashboard provides an overview of MISP42 configuration and MISP alert actions in last 60 minutes. Go to the other dashboards to get details. 4 | 5 | 6 | 7 | 8 | | rest /services/configs/conf-misp42splunk_instances 9 | | rename eai:acl.app as app, title as misp_instance 10 | | fields misp_instance 11 | | stats count 12 | -24h@h 13 | now 14 | 1 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | (index=_* OR index=cim_*) (sourcetype="*misp42*") "[AL301]*" 39 | | stats count 40 | -60m@m 41 | now 42 | 1 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | (index=_* OR index=cim_*) (sourcetype="*misp42*") "[AL302]*" 68 | | stats count 69 | -60m@m 70 | now 71 | 1 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | (index=_* OR index=cim_*) (sourcetype="*misp42*") "[AL303]*" 97 | | stats count 98 | -60m@m 99 | now 100 | 1 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | (index=_* OR index=cim_*) (sourcetype="*misp42*") "[AL304]*" 126 | | stats count 127 | -60m@m 128 | now 129 | 1 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | List of MISP instances 155 | 156 | 157 | | rest /services/configs/conf-misp42splunk_instances 158 | | rename eai:acl.app as app, title as misp_instance 159 | | fields misp_instance 160 | -24h@h 161 | now 162 | 1 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 |
172 |
173 |
174 | 175 | 176 | Recent logs on MISP alert creation (last 60 minutes) 177 | 178 | 179 | (index=_* OR index=cim_*) (sourcetype="*misp42*") 180 | -60m@m 181 | now 182 | 1 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 |
-------------------------------------------------------------------------------- /package/default/data/ui/views/mispfetch.xml: -------------------------------------------------------------------------------- 1 |
2 | $$ $param_not_tags$ $param_onlyids$ $param_page$ $param_pipesplit$ $param_tags$ 3 | misp_restsearch=events 4 | 5 | misp_output_mode=fields 6 | attribute_limit=0 7 | expand_object=False 8 | getioc=False 9 | keep_galaxy=False 10 | keep_related=False 11 | limit=1000 12 | 13 | output=fields 14 | page=0 15 | pipesplit=True 16 | 17 | 18 | | makeresults | eval domain="www.example.com" 19 | 20 | 21 | MISP custom command mispfetch to retrieve events or attributes or events with their attributes (Streaming command) - check https://github.com/remg427/misp42splunk/ for any update 22 |
23 | 24 | 25 | misp_instance 26 | misp_instance 27 | 28 | | rest /services/configs/conf-misp42splunk_instances 29 | | rename eai:acl.app as app, title as misp_instance 30 | | fields misp_instance 31 | -24h@h 32 | now 33 | 34 | 35 | 36 | 37 | misp 38 | misp 39 | 40 | 41 | 42 | 43 | misp_restsearch=$restsearch$ 44 | 45 | Events 46 | Attributes 47 | events 48 | events 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | misp_http_body="$misp_http_body$" 58 | 59 | 60 | 61 | 62 | 63 | fields 64 | json 65 | 66 | misp_output_mode=$output$ 67 | 68 | fields 69 | fields 70 | 71 | 72 | 73 | 74 | 75 | attribute_limit=$attribute_limit$ 76 | 77 | 78 | 0 79 | 0 80 | 81 | 82 | 83 | 84 | 85 | expand_object=$expand_object$ 86 | 87 | 88 | True 89 | False 90 | False 91 | False 92 | 93 | 94 | 95 | 96 | getioc=$getioc$ 97 | 98 | True 99 | False 100 | False 101 | False 102 | 103 | 104 | 105 | 106 | keep_galaxy=$keep_galaxy$ 107 | 108 | True 109 | False 110 | False 111 | False 112 | 113 | 114 | 115 | 116 | keep_related=$keep_related$ 117 | 118 | True 119 | False 120 | False 121 | False 122 | 123 | 124 | 125 | 126 | 127 | limit=$limit$ 128 | 129 | 130 | 1000 131 | 1000 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | not_tags="$not_tags$" 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | page=$page$ 149 | 150 | 151 | 0 152 | 0 153 | 154 | 155 | 156 | 157 | 158 | pipesplit=$pipesplit$ 159 | 160 | 161 | True 162 | False 163 | True 164 | True 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | prefix=$prefix$ 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | tags="$tags$" 187 | 188 | 189 | 190 | 191 | 192 | | makeresults | eval domain="www.example.com" 193 | 194 |
195 | 196 | 197 | 198 | 203 |

Quick start

204 | 205 |

The command mispfetch is a streaming command. It cannot be on a first line of a SPL (key difference with mispgetioc and mispgetevent).

206 | 207 |

It can take parameters from the command line or from the result set before. The values coming from the result set have priority over the values passed on the command line.

208 | 209 |

This way is very flexible to adapt the REST HTTP body of the request against one of the 2 supported MISP REST API endpoints: /events/restSearch or /attributes/restSearch

210 | 211 |

It has only one mandatory parameter "misp_instance:" that has to be set when the command is invoked. The following SPL are equivalent:

212 | 213 |
214 |       | makeresults 
215 |       | mispfetch misp_instance="misp"
216 |               
217 | 218 |
219 |       | makeresults 
220 |       | eval misp_instance="misp"
221 |       | mispfetch
222 |               
223 | 224 |

List of supported parameters

225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 263 | 277 | 278 | 279 | 280 | 281 | 282 | 286 | 287 | 288 | 289 | 290 | 291 | 294 | 295 | 296 | 297 | 298 | 299 | 303 | 304 | 305 | 306 | 307 | 308 | 313 | 314 | 315 | 316 | 317 | 318 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 353 | 354 | 355 | 356 | 357 | 359 | 360 | 361 | 362 | 363 | 365 | 366 | 367 |
ParamSyntaxDefaultDescription
238 | misp_instance 239 | misp_instance=<misp_account>NoneOne of the accounts defined in tab configuration (the list is also displayed on the drop-down box)
misp_restsearchmisp_restsearch=(events|attributes)eventsUse this parameter to select between the endpoint "/events/restSearch" or "/attributes/restSearch".
misp_http_bodymisp_http_body=JSON 254 |
255 |       {
256 |           "returnFormat": "json",
257 |           "last": "1h",
258 |           "published": True,
259 |           "withAttachments": False
260 |       }
261 |                       
262 |
264 |

Provide an HTTP body in JSON format.

265 | 266 |

You can test it using the MISP RESP graphical client on MISP.

267 | 268 |

you can also use a SPL to prepare it.

269 | 270 |

⚠️ 2 keys are enforced on any HTTP body

271 | 272 |
    273 |
  • returnFormat: JSON
  • 274 |
  • withAttachments: False
  • 275 |
276 |
attribute_limitattribute_limit=<int>0 283 |

Limit the number of attributes per event when fetching events and getioc is True (param limit applies to the number of events). This parameter has no effect when fetching attributes (use limit in that case).

284 |

Valid only when fetching attributes.

285 |
expand_objectexpand_object=<1|y|Y|t|true|True|0|n|N|f|false|False>FalseBoolean to have object attributes expanded (one per row). By default, attributes of one object are displayed on same row. 292 |

Valid only when fetching attributes.

293 |
getiocgetioc=<1|y|Y|t|true|True|0|n|N|f|false|False>False 300 |

Include event attributes in the output. In tabular view there will be one row per object or attribute of events.

301 |

Valid only when fetching events.

302 |
keep_galaxykeep_galaxy=<1|y|Y|t|true|True|0|n|N|f|false|False>True 309 |

Boolean to keep Galaxy information (by default) or remove it.

310 |

It can be usefull for misp_output_mode=json if you plan to use spath that has a default limit of 5000 characters to parse the JSON payload.

311 |

Valid only when fetching events.

312 |
keep_relatedkeep_related=(1|y|Y|t|true|True|0|n|N|f|false|False)FalseBoolean to keep Related event info or remove it in ouput=json (full JSON record). This is useful if you plan to use spath that has a limit of 5000 characters to parse the JSON payload. 319 |

Valid only when fetching events.

320 |
limitlimit=<1|y|Y|t|true|True|0|n|N|f|false|False>1000Define the limit for each MISP search; default 1000. 0 = no pagination
misp_output_modemisp_output_mode=(fields|json)fieldsnative Splunk tabular view or JSON
not_tagsnot_tags="" 338 | Comma(,)-separated string of tags to exclude. Wildcard is %.
pagepage=int0define the page for each MISP search; default 0 = iterate through all pages
pipesplitpipesplit=<1|y|Y|t|true|True|0|n|N|f|false|False>Truesplit combined attribute types such as domain|ip 351 |

Valid only when fetching attributes.

352 |
prefixprefix=string 358 | prefix set to every MISP key. The value is set by this parameter, then by the value defined for the instznce and finally default to "misp_"
tagstags="" 364 | Comma(,)-separated string of tags to search for. Wildcard is %.
368 | 369 |
370 |
371 | 372 | 373 | Custom command mispfetch 374 | 375 | $sample_query$ | mispfetch misp_instance=$misp_instance$ $param_restsearch$ $param_misp_http_body$ $param_misp_output_mode$ $param_attribute_limit$ $param_expand_object$ $param_getioc$ $param_keep_galaxy$ $param_keep_related$ $param_limit$ $param_not_tags$ $param_page$ $param_pipesplit$ $param_prefix$ $param_tags$ 376 | 377 | $sample_query$ | mispfetch misp_instance=$misp_instance$ $param_restsearch$ $param_misp_http_body$ $param_misp_output_mode$ $param_attribute_limit$ $param_expand_object$ $param_getioc$ $param_keep_galaxy$ $param_keep_related$ $param_limit$ $param_not_tags$ $param_page$ $param_pipesplit$ $param_prefix$ $param_tags$ | table * 378 | 0 379 | 380 | 1 381 | 382 | 383 | 384 | 385 |
386 |
387 |
388 |
-------------------------------------------------------------------------------- /package/default/data/ui/views/misprest.xml: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | MISP custom command misprest - Generic wrapper (Generating command) - check https://github.com/remg427/misp42splunk/ for any update 7 |
8 | 9 | 10 | 11 | -24h@h 12 | now 13 | 14 | 15 | 16 | 17 | misp_instance 18 | misp_instance 19 | 20 | | rest /services/configs/conf-misp42splunk_instances 21 | | rename eai:acl.app as app, title as misp_instance 22 | | fields misp_instance 23 | -24h@h 24 | now 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | GET 33 | POST 34 | PUT 35 | DELETE 36 | GET 37 | GET 38 | 39 | 40 | 41 | /servers/getVersion 42 | /servers/getVersion 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | json_request="$value$" 53 | 54 | 55 | 56 |
57 | 58 | 59 | 60 |

Select a misp_instance, a target API endpoint and provide a valid JSON

61 |

in JSON, escape each " like this \"

62 |

for example

63 |
    64 |
  • method: POST
  • 65 |
  • target: /attributes/restSearch
  • 66 |
  • json_request: {\"returnFormat\": \"json\", \"last\": \"20d\"}
  • 67 |
68 | 69 |
70 |
71 | 72 | 73 | Custom command misprest 74 | 75 | 76 | | misprest misp_instance=$misp_instance$ method=$method$ target=$target$ $misp_json_request$ 77 | 0 78 | 79 | 1 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
-------------------------------------------------------------------------------- /package/default/data/ui/views/mispsearch.xml: -------------------------------------------------------------------------------- 1 |
2 | 3 | misp 4 | 5 | 6 | limit=10 7 | 8 | page=1 9 | pipesplit=true 10 | prefix=misp_ 11 | 12 | | makeresults | eval domain="www.example.com" 13 | 14 | 15 | MISP custom command mispsearch search matching attributes in MISP (Streaming command) - check https://github.com/remg427/misp42splunk/ for any update 16 |
17 | 18 | 19 | misp_instance 20 | misp_instance 21 | 22 | | rest /services/configs/conf-misp42splunk_instances 23 | | rename eai:acl.app as app, title as misp_instance 24 | | fields misp_instance 25 | -24h@h 26 | now 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | misp_http_body="$value$" 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | limit=$value$ 54 | 55 | 56 | 10 57 | 10 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | not_tags="$value$" 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | page=$value$ 75 | 76 | 77 | 1 78 | 1 79 | 80 | 81 | 82 | 83 | 84 | pipesplit=$value$ 85 | 86 | 87 | True 88 | False 89 | True 90 | True 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | prefix=$value$ 100 | 101 | 102 | misp_ 103 | misp_ 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | tags="$value$" 113 | 114 | 115 | 116 | 117 | 118 | | makeresults | eval domain="www.example.com" 119 | 120 |
121 | 122 | 123 | 124 |

Select a misp_instance and a field name to search for.

125 |

Additional arguments:

126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 149 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 185 | 186 | 187 | 188 | 189 | 191 | 192 | 193 | 194 | 195 | 197 | 198 | 199 |
ParamSyntaxDefaultDescription
misp_http_bodymisp_http_body=JSON 140 |
141 |                 {
142 |                     "returnFormat": "json",
143 |                     "last": "1h",
144 |                     "published": True,
145 |                     "withAttachments": False
146 |                 }
147 |               
148 |
150 |

Provide an HTTP body in JSON format.

151 |

You can test it using the MISP RESP graphical client on MISP.

152 |

you can also use a SPL to prepare it.

153 |

⚠️ 2 keys are enforced on any HTTP body

154 |
    155 |
  • returnFormat: JSON
  • 156 |
  • withAttachments: False
  • 157 |
158 |
limitlimit=<1|y|Y|t|true|True|0|n|N|f|false|False>10Define the limit for each MISP search; default 10. 0 = no pagination
not_tagsnot_tags="" 170 | Comma(,)-separated string of tags to exclude. Wildcard is %.
pagepage=int1define the page for each MISP search; default 1
pipesplitpipesplit=<1|y|Y|t|true|True|0|n|N|f|false|False>Truesplit combined attribute types such as domain|ip 183 |

Valid only when fetching attributes.

184 |
prefixprefix=string 190 | prefix set to every MISP key. The value is set by this parameter, then by the value defined for the instznce and finally default to "misp_"
tagstags="" 196 | Comma(,)-separated string of tags to search for. Wildcard is %.
200 | 201 |
202 |
203 | 204 | 205 | Custom command mispsearch 206 | 207 | $sample_query$ | mispsearch misp_instance=$misp_instance$ field=$field$ $param_misp_http_body$ $param_limit$ $param_page$ $param_pipesplit$ $param_prefix$ $param_tags$ $param_not_tags$ 208 | 209 | $sample_query$ | mispsearch misp_instance=$misp_instance$ field=$field$ $param_misp_http_body$ $param_limit$ $param_page$ $param_pipesplit$ $param_prefix$ $param_tags$ $param_not_tags$ 210 | 0 211 | 212 | 1 213 | 214 | 215 | 216 | 217 |
218 |
219 |
220 |
221 | -------------------------------------------------------------------------------- /package/default/props.conf: -------------------------------------------------------------------------------- 1 | 2 | [source::...ta_misp42*.log*] 3 | sourcetype = ta:misp42:log 4 | 5 | [source::...misp_alert_create_event_modalert.log*] 6 | sourcetype = ta:misp42:log 7 | 8 | [source::...misp_alert_sighting_modalert.log*] 9 | sourcetype = ta:misp42:log 10 | 11 | -------------------------------------------------------------------------------- /package/default/savedsearches.conf: -------------------------------------------------------------------------------- 1 | # http://docs.splunk.com/Documentation/CIM/4.12.0/User/Malware 2 | [MISP_getioc_filename_related_last1d_to_KV_MISP_filename] 3 | action.email.useNSSubject = 1 4 | alert.track = 0 5 | cron_schedule = 0 0 * * * 6 | dispatch.earliest_time = -24h@h 7 | dispatch.latest_time = now 8 | display.events.fields = ["host"] 9 | display.general.timeRangePicker.show = 0 10 | display.general.type = statistics 11 | display.page.search.mode = verbose 12 | display.page.search.tab = statistics 13 | display.visualizations.charting.chart = bar 14 | display.visualizations.show = 0 15 | enableSched = 0 16 | disabled = 1 17 | request.ui_dispatch_app = misp42splunk 18 | request.ui_dispatch_view = search 19 | search = | mispgetioc misp_instance=misp last=1d geteventtag=t pipesplit=t type="filename,md5,sha1,sha256,sha512,ssdeep,filename|md5,filename|sha1,filename|sha256,filename|sha512,filename|ssdeep" \ 20 | | where isnotnull(misp_filename) or isnotnull(misp_md5) or isnotnull(misp_sha1) or isnotnull(misp_sha256) or isnotnull(misp_sha512) or isnotnull(misp_ssdeep) | fields - _time, _raw, host \ 21 | | outputlookup append=true MISP_filename 22 | 23 | # http://docs.splunk.com/Documentation/CIM/4.12.0/User/Web 24 | [MISP_getioc_web_related_last1d_to_KV_MISP_web] 25 | action.email.useNSSubject = 1 26 | alert.track = 0 27 | cron_schedule = 5 0 * * * 28 | dispatch.earliest_time = -24h@h 29 | dispatch.latest_time = now 30 | display.events.fields = ["host"] 31 | display.general.timeRangePicker.show = 0 32 | display.general.type = statistics 33 | display.page.search.mode = verbose 34 | display.page.search.tab = statistics 35 | display.visualizations.charting.chart = bar 36 | display.visualizations.show = 0 37 | enableSched = 0 38 | disabled = 1 39 | request.ui_dispatch_app = misp42splunk 40 | request.ui_dispatch_view = search 41 | search = | mispgetioc misp_instance=misp last=1d pipesplit=t geteventtag=t type="domain,ip-dst,ip-src,url,user_agent,domain|ip"\ 42 | | where isnotnull(misp_domain) or isnotnull(misp_ip_dst) or isnotnull(misp_ip_src) or isnotnull(misp_url) or isnotnull(misp_user_agent) \ 43 | | eval misp_user_agent_length=if(isnotnull(misp_user_agent),len(misp_user_agent),'') | fields - _time, _raw, host \ 44 | | outputlookup MISP_web append=true 45 | 46 | 47 | # http://docs.splunk.com/Documentation/CIM/4.12.0/User/Email 48 | [MISP_getioc_email_related_last1d_to_KV_MISP_email] 49 | action.email.useNSSubject = 1 50 | alert.track = 0 51 | cron_schedule = 3 0 * * * 52 | dispatch.earliest_time = -24h@h 53 | dispatch.latest_time = now 54 | display.events.fields = ["host"] 55 | display.general.timeRangePicker.show = 0 56 | display.general.type = statistics 57 | display.page.search.mode = verbose 58 | display.page.search.tab = statistics 59 | display.visualizations.charting.chart = bar 60 | display.visualizations.show = 0 61 | enableSched = 0 62 | disabled = 1 63 | request.ui_dispatch_app = misp42splunk 64 | request.ui_dispatch_view = search 65 | search = | mispgetioc misp_instance=misp last=1d geteventtag=t type="email-attachment,email-src,email-src-display-name,email-subject" limit=0\ 66 | | where isnotnull(misp_email_attachment) or isnotnull(misp_email_src) or isnotnull(misp_email_src_display_name) or isnotnull(misp_email_subject) | fields - _time, _raw, host \ 67 | | outputlookup MISP_email append=true 68 | 69 | 70 | [MISP_email_intel_last1d] 71 | action.email.useNSSubject = 1 72 | alert.track = 0 73 | dispatch.earliest_time = -24h@h 74 | dispatch.latest_time = now 75 | display.general.timeRangePicker.show = 0 76 | display.general.type = statistics 77 | display.page.search.mode = verbose 78 | display.page.search.tab = statistics 79 | display.visualizations.show = 0 80 | enableSched = 0 81 | disabled = 1 82 | request.ui_dispatch_app = misp42splunk 83 | request.ui_dispatch_view = search 84 | search = | mispgetioc misp_instance=misp last=1d to_ids=t geteventtag=t type="email-src,email-subject" limit=0\ 85 | | where isnotnull(misp_email_src) or isnotnull(misp_email_subject)\ 86 | | eval weight=case(match(misp_tag,"tlp:white"),20,match(misp_tag,"tlp:green"),40,match(misp_tag,"tlp:amber"),80,match(misp_tag,"tlp:red"),100,true(),50)\ 87 | | eval description = "https://" + misp_host + "/events/view/" + misp_event_uuid | rename misp_email_src AS src_user misp_email_subject AS subject \ 88 | | where isnotnull(src_user) or isnotnull(subject) | fields description,src_user,subject,weight \ 89 | | outputlookup misp_email_intel.csv 90 | 91 | [MISP_file_intel_last1d] 92 | action.email.useNSSubject = 1 93 | alert.track = 0 94 | dispatch.earliest_time = -24h@h 95 | dispatch.latest_time = now 96 | display.general.timeRangePicker.show = 0 97 | display.general.type = statistics 98 | display.page.search.mode = verbose 99 | display.page.search.tab = statistics 100 | display.visualizations.show = 0 101 | enableSched = 0 102 | disabled = 1 103 | request.ui_dispatch_app = misp42splunk 104 | request.ui_dispatch_view = search 105 | search = | mispgetioc misp_instance=misp last=1d to_ids=t geteventtag=t pipesplit=t type="filename,md5,sha1,sha256,sha512,ssdeep,filename|md5,filename|sha1,filename|sha256,filename|sha512,filename|ssdeep"\ 106 | | eval description = "https://" + misp_host + "/events/view/" + misp_event_uuid \ 107 | | eval description=if(isnotnull(misp_filename),description + " on " + misp_filename,description) \ 108 | | eval description=if(isnotnull(misp_md5),description + " MD5,",description) \ 109 | | eval description=if(isnotnull(misp_sha1),description + " SHA1,",description) \ 110 | | eval description=if(isnotnull(misp_sha256),description + " SHA256,",description) \ 111 | | eval description=if(isnotnull(misp_sha512),description + " SHA512,",description) \ 112 | | eval description=if(isnotnull(misp_ssdeep),description + " SSEEEP",description) \ 113 | | eval file_hash=mvappend(misp_md5, misp_sha1, misp_sha256, misp_sha512, misp_ssdeep) \ 114 | | eval file_hash=coalesce(file_hash,"preserve_single_value_file_hash") \ 115 | | mvexpand file_hash \ 116 | | eval file_hash=if(file_hash="preserve_single_value_file_hash", null(), file_hash) \ 117 | | rename misp_filename AS file_name | where isnotnull(file_name) or isnotnull(file_hash) \ 118 | | eval weight=case(match(misp_tag,"tlp:white"),20,match(misp_tag,"tlp:green"),40,match(misp_tag,"tlp:amber"),80,match(misp_tag,"tlp:red"),100,true(),50) \ 119 | | fields description,file_hash,file_name,weight \ 120 | | outputlookup misp_file_intel.csv 121 | 122 | [MISP_http_intel_last1d] 123 | action.email.useNSSubject = 1 124 | alert.track = 0 125 | dispatch.earliest_time = -24h@h 126 | dispatch.latest_time = now 127 | display.general.timeRangePicker.show = 0 128 | display.general.type = statistics 129 | display.page.search.mode = verbose 130 | display.page.search.tab = statistics 131 | display.visualizations.show = 0 132 | enableSched = 0 133 | disabled = 1 134 | request.ui_dispatch_app = misp42splunk 135 | request.ui_dispatch_view = search 136 | search = | mispgetioc misp_instance=misp last=1d to_ids=t geteventtag=t type="url,user_agent" \ 137 | | eval description = "https://" + misp_host + "/events/view/" + misp_event_uuid \ 138 | | rename misp_user_agent AS http_user_agent, misp_url AS url \ 139 | | eval weight=case(match(misp_tag,"tlp:white"),20,match(misp_tag,"tlp:green"),40,match(misp_tag,"tlp:amber"),80,match(misp_tag,"tlp:red"),100,true(),50) \ 140 | | where isnotnull(url) or isnotnull(http_user_agent)| fields description,http_referrer,http_user_agent,url,weight\ 141 | | outputlookup misp_http_intel.csv 142 | 143 | [MISP_ip_intel_last1d] 144 | action.email.useNSSubject = 1 145 | alert.track = 0 146 | dispatch.earliest_time = -24h@h 147 | dispatch.latest_time = now 148 | display.general.timeRangePicker.show = 0 149 | display.general.type = statistics 150 | display.page.search.mode = verbose 151 | display.page.search.tab = statistics 152 | display.visualizations.show = 0 153 | enableSched = 0 154 | disabled = 1 155 | request.ui_dispatch_app = misp42splunk 156 | request.ui_dispatch_view = search 157 | search = | mispgetioc misp_instance=misp last=1d to_ids=t geteventtag=t pipesplit=t type="domain,ip-dst,ip-src,domain|ip" limit=0\ 158 | | eval description = "https://" + misp_host + "/events/view/" + misp_event_uuid\ 159 | | rename misp_domain AS domain\ 160 | | eval ip = mvappend(misp_ip_dst,misp_ip_src)\ 161 | | eval ip = coalesce(ip,"preserve_single_value_ip")\ 162 | | mvexpand ip\ 163 | | eval ip = if(ip="preserve_single_value_ip", null(), ip)\ 164 | | eval weight=case(match(misp_tag,"tlp:white"),20,match(misp_tag,"tlp:green"),40,match(misp_tag,"tlp:amber"),80,match(misp_tag,"tlp:red"),100,true(),50)\ 165 | | where isnotnull(ip) or isnotnull(domain) | fields description,domain,ip,weight\ 166 | | outputlookup misp_ip_intel.csv 167 | 168 | [MISP dummy test action create alert] 169 | action.misp_alert_create_event = 1 170 | action.misp_alert_create_event.param.description = test all scenarios 171 | action.misp_alert_create_event.param.distribution = 3 172 | action.misp_alert_create_event.param.eventid = inline_eventkey 173 | action.misp_alert_create_event.param.eventkey = inline_eventkey 174 | action.misp_alert_create_event.param.info = Test MISP event 175 | action.misp_alert_create_event.param.misp_instance = misp 176 | action.misp_alert_create_event.param.pap = PAP_GREEN 177 | action.misp_alert_create_event.param.publish_on_creation = 1 178 | action.misp_alert_create_event.param.tags = test_alert,creation,update 179 | action.misp_alert_create_event.param.title = Test MISP create event 180 | action.misp_alert_create_event.param.tlp = TLP_GREEN 181 | action.misp_alert_create_event.param.unique = inline_eventkey 182 | action.webhook.enable_allowlist = 0 183 | alert.suppress = 0 184 | alert.track = 1 185 | counttype = number of events 186 | cron_schedule = 15 * * * * 187 | description = Test to create or update MISP events including simple or combined attributes, domain|ip, email, file 188 | disabled = 1 189 | dispatch.earliest_time = -15m 190 | dispatch.latest_time = now 191 | display.general.type = statistics 192 | display.page.search.mode = fast 193 | display.page.search.tab = statistics 194 | enableSched = 0 195 | disabled = 1 196 | quantity = 0 197 | relation = greater than 198 | request.ui_dispatch_app = misp42splunk 199 | request.ui_dispatch_view = search 200 | search = | makeresults count=10\ 201 | | streamstats count as rc\ 202 | | eval no_domain="one.one.one.one", no_ip="1.1.1.1", no_first_seen=(now() - 86400), no_last_seen=strftime(now(),"%FT%T"), no_port="53", no_registration_date=strptime("2020-01-01T00:00:00","`%FT%T"), no_text="test domain|ip object"\ 203 | | eval fo_filename="malware_" + md5(tostring(now()+rc)) +".exe", fo_fullpath="C:\Windows\system32\malware_" + md5(tostring(now()+rc)) + ".exe", fo_md5=md5(fo_filename), fo_sha1=sha1(fo_filename), fo_sha256=sha256(fo_filename)\ 204 | | eval eo_bcc="name@domain.com", eo_cc=md5(tostring(now()+rc)) + "@domain.com", eo_from="phishing" + md5(tostring(now() + rc)) + "@maldom.com", eo_ip_src="1.2.3." + rc, eo_subject="Your account is going to be locked in " + rc + " days", eo_to="you.as.victim@gooddom.com"\ 205 | | eval inline_eventkey=case(rc % 7 == 0, "fd31ca83-2e50-40e1-9ffb-2f14155fd169", rc % 5 == 0, "67", rc % 2 == 0, "even", 1==1, null())\ 206 | | eval misp_info = coalesce("test with eventkey:" + inline_eventkey + " - " + rc, "test MISP index " + rc) 207 | 208 | -------------------------------------------------------------------------------- /package/default/searchbnf.conf: -------------------------------------------------------------------------------- 1 | ################## 2 | # mispgetioc 3 | ################## 4 | [mispgetioc-command] 5 | syntax = | mispgetioc 6 | shortdesc = Retrieve attributes of matching events in MISP instance. Must provide either option "json_request", "date", "eventid", "last", "publish_timestamp" or "timestamp". 7 | description = mispgetioc searches for matching attributes in MISP instance.\ 8 | You can filter on "category" or "type" of attributes, on attributes having the "to_ids" flag set\ 9 | or specific "tags" or not having tags "not_tags".\ 10 | usage = public 11 | example1 = | mispgetioc misp_instance=misp last=10d limit=0 12 | comment1 = Retrieve attributes of all events published in last 10 days. 13 | example2 = | mispgetioc misp_instance=misp date="2023-01-01,2023-01-31" category="Payload delivery,Network%" type=ip-dst to_ids=TRUE 14 | comment2 = Retrieve up to first 1000 (default limit) attributes from events having event date set between 2023-01-01 and 2023-01-31, attributes of type 'ip-dst', and categories 'Payload delivery' or starting with 'Network' and with the flag "to_ids" set. 15 | related = misp 16 | tags = misp 17 | 18 | [mispgetioc-options] 19 | syntax = misp_instance= json_request= date= eventid= \ 20 | last=d|h|m publish_timestamp=d|h|m timestamp=d|h|m \ 21 | category= decay_score_threshold= decaying_model= exclude_decayed= expand_object= \ 22 | geteventtag= include_decay_score= include_deleted= include_sightings= limit= \ 23 | not_tags= output= page= pipesplit= prefix= tags= \ 24 | threat_level_id= to_ids= type= warning_list= 25 | 26 | 27 | ################## 28 | # mispsearch 29 | ################## 30 | [mispsearch-command] 31 | syntax = mispsearch 32 | shortdesc = Search in MISP for attributes having the value of field 33 | description = Search in MISP for attributes having the value of field - 34 | example1 = | mispsearch misp_instance=misp field=clientip prefix="misp_dev_" 35 | comment1 = Retrieve attributes properties (attribute uuid, event_id, type, category, to_ids flag) for matching values of field clientip 36 | example2 = ... | field ip | mispsearch misp_instance=misp field=ip misp_instance=ops misp_http_body="{\"returnFormat\": \"json\", \"withAttachments\": \"false\", \"includeEventUuid\": \"true\", \"includeEventTags\": \"true\"}" 37 | comment2 = Use a JSON request body for customised search (it is much simpler to prepare a field misp_http_body before) 38 | 39 | [mispsearch-options] 40 | syntax = misp_instance= field= misp_http_body= \ 41 | limit= not_tags= page= pipesplit= prefix= tags= 42 | 43 | description = mispsearch takes the value of fieldname and searches in MISP instance for matching attributes. 44 | 45 | ################## 46 | # mispgetevent 47 | ################## 48 | [mispgetevent-command] 49 | syntax = |mispgetevent 50 | shortdesc = Retrieve events in MISP instance. Must provide either option "timestamp", "eventid", "publish_timestamp", "date" or "json_request". 51 | description = mispgetevent searches in MISP instance matching events\ 52 | You can filter on "category" or "type" of attributes, or specific "tags" or "not_tags".\ 53 | You may display the attribute uuid (getuuid: default=FALSE) or creating org (getorg: default=FALSE) 54 | usage = public 55 | example1 = | mispgetevent misp_instance=misp timestamp=10d 56 | comment1 = Retrieve events changed in last 10 days. you can use range e.g. "14d,7d" 57 | example2 = | mispgetevent misp_instance=misp timestamp=10d category="Payload delivery,Network%" type=ip-dst 58 | comment2 = Retrieve events edited in last 10 days with attributes of type 'ip-dst' and categories 'Payload delivery' or starting by 'Network'. 59 | example3 = | mispgetevent misp_instance=misp eventid="123,124,678" 60 | comment3 = Retrieve event with id 123, 124 & 678. you can mix id and uuid. 61 | example4 = | mispgetevent misp_instance=misp publish_timestamp="14d,7d" 62 | comment4 = Retrieve events published between last 14 and 7 days. 63 | related = misp 64 | tags = misp 65 | 66 | [mispgetevent-options] 67 | syntax = misp_instance= json_request= date= eventid= \ 68 | last=d|h|m publish_timestamp=d|h|m timestamp=d|h|m \ 69 | category= exclude_local_tags= expand_object= getioc= include_sightings= \ 70 | keep_galaxy= keep_related= limit= not_tags= output= page= \ 71 | pipesplit= prefix= published= tags= threat_level_id= to_ids= \ 72 | type= warning_list= 73 | 74 | 75 | ################## 76 | # mispfetch 77 | ################## 78 | [mispfetch-command] 79 | syntax = | mispfetch 80 | shortdesc = Streaming command to retrieve events or attributes from MISP instance. 81 | description = mispfetch pulls events and/or attributes from MISP instance \ 82 | Define your MISP instances as input. limit is set by default to 1000.\ 83 | You can filter on "category" or "type" of attributes, or specific "tags" or on contrary not having "not_tags".\ 84 | To return also attribute values when searching MISP events, use getioc=True \ 85 | To split multivalue attributes into 2 fields set boolean pipesplit=True. \ 86 | For example domain|ip will be split into misp_domain and misp_ip \ 87 | but misp_type and misp_value will still have the original values. 88 | usage = public 89 | example1 = | mispfetch misp_instance=misp_instance_name getioc=1 limit=100 attribute_limit=1000 90 | comment1 = Retrieve 100 events published in last 1 day (default). For each MISP event, return up to 1000 attributes. 91 | related = misp 92 | tags = misp 93 | 94 | [mispfetch-options] 95 | syntax = misp_instance= misp_restsearch= misp_http_body= misp_output_mode= \ 96 | attribute_limit= expand_object= getioc= keep_galaxy= keep_related= limit= \ 97 | not_tags= page= pipesplit= prefix= tags= 98 | 99 | ################## 100 | # misprest 101 | ################## 102 | [misprest-command] 103 | syntax = | misprest 104 | shortdesc = This custom command is a wrapper to call MISP API endpoints. 105 | description = MISP REST API wrapper: provide misp_instance, method, target and a valid JSON request. (don't forget to escape " like this \") 106 | usage = public 107 | example1 = | misprest misp_instance=misp method=GET target="/servers/getVersion" 108 | comment1 = retrieve MISP server version 109 | example2 = | misprest misp_instance=misp method=POST target="/attributes/restSearch" json_request="{\"returnFormat\": \"json\", \"last\": \"20d\"}" 110 | comment2 = retrieve MISP attributes of all events published in last 20 days and display as events 111 | related = misp 112 | tags = misp 113 | 114 | [misprest-options] 115 | syntax = misp_instance= method= target= json_request= 116 | -------------------------------------------------------------------------------- /package/default/server.conf: -------------------------------------------------------------------------------- 1 | [shclustering] 2 | conf_replication_include.misp42splunk_instances = true 3 | conf_replication_include.misp42splunk_settings = true 4 | -------------------------------------------------------------------------------- /package/default/transforms.conf: -------------------------------------------------------------------------------- 1 | # http://docs.splunk.com/Documentation/CIM/4.12.0/User/Malware 2 | [MISP_filename] 3 | collection = kvmisp_filename 4 | external_type = kvstore 5 | fields_list = _key,misp_filename,misp_attribute_id,misp_attribute_uuid,misp_category,misp_datetime,misp_description,misp_event_id,misp_malware_sample,misp_md5,misp_object_id,misp_orgc_id,misp_sha1,misp_sha256,misp_sha512,misp_ssdeep,misp_size_in_bytes,misp_tag,misp_text,misp_timestamp,misp_to_ids 6 | 7 | # http://docs.splunk.com/Documentation/CIM/4.12.0/User/Web 8 | [MISP_web] 9 | collection = kvmisp_web 10 | external_type = kvstore 11 | fields_list = _key,misp_domain,misp_attribute_id,misp_attribute_uuid,misp_ip_dst,misp_ip_src,misp_url,misp_user_agent,misp_user_agent_length,misp_category,misp_datetime,misp_description,misp_event_id,misp_object_id,misp_orgc_id,misp_tag,misp_timestamp,misp_to_ids 12 | 13 | [MISP_email] 14 | collection = kvmisp_email 15 | external_type = kvstore 16 | fields_list = _key,misp_attribute_id,misp_attribute_uuid,misp_email_attachment,misp_email_src,misp_email_src_display_name,misp_email_subject,misp_category,misp_datetime,misp_description,misp_event_id,misp_object_id,misp_orgc_id,misp_tag,misp_timestamp,misp_to_ids 17 | -------------------------------------------------------------------------------- /package/lib/requirements.txt: -------------------------------------------------------------------------------- 1 | splunktaucclib 2 | splunk-sdk 3 | solnlib -------------------------------------------------------------------------------- /package/metadata/default.meta: -------------------------------------------------------------------------------- 1 | # Application-level permissions 2 | [] 3 | access = read : [ * ], write : [ admin, sc_admin ] 4 | export = system 5 | -------------------------------------------------------------------------------- /package/static/README.md: -------------------------------------------------------------------------------- 1 | # misp42splunk icons and logo 2 | 3 | misp42splunk icons and logo are adapted from MISP logos and licensed under [CC-BY](https://creativecommons.org/licenses/by/4.0/). 4 | 5 | [MISP](https://github.com/MISP/MISP) logos are licensed under [CC-BY](https://creativecommons.org/licenses/by/4.0/). 6 | 7 | If you have question related to MISP logos, don't hesitate to contact the maintainers of the MISP project 8 | -------------------------------------------------------------------------------- /package/static/appIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/package/static/appIcon.png -------------------------------------------------------------------------------- /package/static/appIconAlt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/package/static/appIconAlt.png -------------------------------------------------------------------------------- /package/static/appIconAlt_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/package/static/appIconAlt_2x.png -------------------------------------------------------------------------------- /package/static/appIcon_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remg427/misp42splunk/1063721ccb0608aaed6b08c8082247c18cbf2b2b/package/static/appIcon_2x.png --------------------------------------------------------------------------------