diff --git a/analyzers/RiskIQ/RiskIQ_Articles.json b/analyzers/RiskIQ/RiskIQ_Articles.json
new file mode 100644
index 000000000..7dcead066
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Articles.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "articles"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: OSINT articles that reference an indicator.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Articles",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Artifacts.json b/analyzers/RiskIQ/RiskIQ_Artifacts.json
new file mode 100644
index 000000000..d367b0416
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Artifacts.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "artifacts"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: Illuminate / PassiveTotal project artifacts that match an indicator.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Artifacts",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Certificates.json b/analyzers/RiskIQ/RiskIQ_Certificates.json
new file mode 100644
index 000000000..1b457e3a6
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Certificates.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "certificates"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: SSL/TLS certificates associated with an indicator.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Certificates",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Components.json b/analyzers/RiskIQ/RiskIQ_Components.json
new file mode 100644
index 000000000..a132b80f9
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Components.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "components"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: web components observed during crawls on a hostname.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Components",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Cookies.json b/analyzers/RiskIQ/RiskIQ_Cookies.json
new file mode 100644
index 000000000..363f1e0b7
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Cookies.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "cookies"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: cookies observed during crawls on a hostname.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Cookies",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_HostpairChildren.json b/analyzers/RiskIQ/RiskIQ_HostpairChildren.json
new file mode 100644
index 000000000..ff378f789
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_HostpairChildren.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "hostpair_children"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: hosts with a child web component relationship to an IOC.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_HostpairChildren",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_HostpairParents.json b/analyzers/RiskIQ/RiskIQ_HostpairParents.json
new file mode 100644
index 000000000..78cb4ec5c
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_HostpairParents.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "hostpair_parents"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: hosts with a parent web component relationship to an IOC.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_HostpairParents",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Malware.json b/analyzers/RiskIQ/RiskIQ_Malware.json
new file mode 100644
index 000000000..d9c2f686e
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Malware.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "malware"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: malware hashes from various sources associated with an IOC.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Malware",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Projects.json b/analyzers/RiskIQ/RiskIQ_Projects.json
new file mode 100644
index 000000000..cc6845a78
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Projects.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "projects"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: Illuminate / PassiveTotal projects that contain an artifact which matches an IOC.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Projects",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Reputation.json b/analyzers/RiskIQ/RiskIQ_Reputation.json
new file mode 100644
index 000000000..882d84b06
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Reputation.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "reputation"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ Illuminate Reputation Score for an indicator.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Reputation",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Resolutions.json b/analyzers/RiskIQ/RiskIQ_Resolutions.json
new file mode 100644
index 000000000..84fb93e23
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Resolutions.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "resolutions"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: PDNS resolutions for an IOC.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Resolutions",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Services.json b/analyzers/RiskIQ/RiskIQ_Services.json
new file mode 100644
index 000000000..6910d9f4b
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Services.json
@@ -0,0 +1,41 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "services"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "ip"
+ ],
+ "description": "RiskIQ: services observed on an IP address.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Services",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Subdomains.json b/analyzers/RiskIQ/RiskIQ_Subdomains.json
new file mode 100644
index 000000000..f4b5245a7
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Subdomains.json
@@ -0,0 +1,42 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "subdomains"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "fqdn",
+ "domain"
+ ],
+ "description": "RiskIQ: subdomains observed historically in pDNS records.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Subdomains",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Summary.json b/analyzers/RiskIQ/RiskIQ_Summary.json
new file mode 100644
index 000000000..21b0a2b8e
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Summary.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "summary"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ Illuminate and PassiveTotal datasets with records for an indicator.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Summary",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Trackers.json b/analyzers/RiskIQ/RiskIQ_Trackers.json
new file mode 100644
index 000000000..eb492af25
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Trackers.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "trackers"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ: trackers observed during a crawl on a host.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Trackers",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/RiskIQ_Whois.json b/analyzers/RiskIQ/RiskIQ_Whois.json
new file mode 100644
index 000000000..a9eb09e60
--- /dev/null
+++ b/analyzers/RiskIQ/RiskIQ_Whois.json
@@ -0,0 +1,43 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_analyzer.py",
+ "config": {
+ "auto_extract": true,
+ "property": "whois"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": 180,
+ "description": "Number of days back to search for date-bounded historical queries",
+ "multi": false,
+ "name": "days_back",
+ "required": false,
+ "type": "number"
+ }
+ ],
+ "dataTypeList": [
+ "domain",
+ "fqdn",
+ "ip"
+ ],
+ "description": "RiskIQ Whois lookup for an indicator.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_Whois",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/analyzers/RiskIQ/_analyzer.py b/analyzers/RiskIQ/_analyzer.py
new file mode 100755
index 000000000..eb5c010cb
--- /dev/null
+++ b/analyzers/RiskIQ/_analyzer.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+# encoding: utf-8
+
+from cortexutils.analyzer import Analyzer
+
+from passivetotal import analyzer as riqanalyzer
+from _services import SERVICES
+
+VERSION = '1.0'
+
+
+
+class IlluminateAnalyzer(Analyzer):
+ def __init__(self):
+ Analyzer.__init__(self)
+ self._property = self.get_param('config.property', None, 'RiskIQ Illuminate Analyzer object property is missing')
+ self._username = self.get_param('config.username', None, 'RiskIQ Illuminate username is missing')
+ self._api_key = self.get_param('config.api_key', None, 'RiskIQ Illuminate api_key is missing')
+ self._days_back = self.get_param('config.days_back', None, 'RiskIQ Illuminate days_back is missing')
+ riqanalyzer.init(username=self._username, api_key=self._api_key)
+ riqanalyzer.set_date_range(days_back=self._days_back)
+ riqanalyzer.set_context('thehive','riq-analyzer',VERSION,'analyzer')
+ if self._property not in SERVICES:
+ self.error('Unknown property {}'.format(self._property))
+ self._svc = SERVICES[self._property]()
+
+ def run(self):
+ ioc = self.get_data()
+ try:
+ ioc_obj = riqanalyzer.get_object(ioc)
+ except riqanalyzer.AnalyzerError as e:
+ self.error('Cannot instantiate object for that type of input: {}'.format(e))
+ try:
+ value = getattr(ioc_obj, self._property)
+ except AttributeError as e:
+ self.error('Unknown property {}'.format(self._property))
+ except riqanalyzer.AnalyzerAPIError as e:
+ if e.status_code == 404:
+ self.report({'found': False, 'records': []})
+ return
+ else:
+ self.error('API error while getting property "{0}": {1}'.format(self._property, e))
+ except riqanalyzer.AnalyzerError as e:
+ self.error('Analyzer error while getting property "{0}": {1}'.format(self._property, e))
+ try:
+ data = value.as_dict
+ except Exception as e:
+ self.error('Cannot transform property "{0}" to dictionary: {1}'.format(self._property, e))
+ data = self._svc.transform(data)
+ self.report(data)
+
+ def summary(self, raw):
+ return self._svc.summarize(raw)
+
+ def artifacts(self, report):
+ svc_artifacts = self._svc.build_artifacts(report)
+ if svc_artifacts is None:
+ return super().artifacts(report)
+ return svc_artifacts
+
+
+
+
+if __name__ == '__main__':
+ IlluminateAnalyzer().run()
+
+
diff --git a/analyzers/RiskIQ/_services.py b/analyzers/RiskIQ/_services.py
new file mode 100644
index 000000000..2b5162f3a
--- /dev/null
+++ b/analyzers/RiskIQ/_services.py
@@ -0,0 +1,495 @@
+import json
+
+
+class IlluminateServiceFile():
+ """Base service class for all RiskIQ service files."""
+
+ def __init__(self):
+ self._name = ''
+ self._version = '1.0'
+ self._author = 'RiskIQ'
+ self._url = 'https://github.com/TheHive-Project/Cortex-Analyzers'
+ self._license = 'AGPL-V3'
+ self._command = 'RiskIQ/_analyzer.py'
+ self._baseconfig = 'RiskIQ'
+ self._description = ''
+ self._dataTypeList = ["domain","fqdn","ip"]
+ self._taxonomy_namespace = 'RIQ'
+ self._taxonomy_predicate = 'RiskIQ'
+ self._taxonomy_valuekey = None
+ self._config = {
+ 'property': None,
+ 'auto_extract': True
+ }
+ self._configitems = [
+ {
+ 'name': 'username',
+ 'description': 'API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)',
+ 'type': 'string',
+ 'multi': False,
+ 'required': True
+ },
+ {
+ 'name': 'api_key',
+ 'description': 'API key of the RiskIQ Illuminate or PassiveTotal account',
+ 'type': 'string',
+ 'multi': False,
+ 'required': True
+ },
+ {
+ 'name': 'days_back',
+ 'description': 'Number of days back to search for date-bounded historical queries',
+ 'type': 'number',
+ 'multi': False,
+ 'required': False,
+ 'defaultValue': 180
+ },
+ ]
+
+ def generate(self):
+ """Creates and writes a JSON file to the local directory describing this service."""
+ obj = {
+ 'name': self._name,
+ 'version': self._version,
+ 'author': self._author,
+ 'url': self._url,
+ 'license': self._license,
+ 'description': self._description,
+ 'dataTypeList': self._dataTypeList,
+ 'command': self._command,
+ 'baseConfig': self._baseconfig,
+ 'config': self._config,
+ 'configurationItems': self._configitems,
+ }
+ filename = '{}.json'.format(self._name)
+ with open(filename, 'w') as f:
+ json.dump(obj, f, indent=2, sort_keys=True)
+
+ def get_taxonomies(self, data):
+ """Get a list of taxonomies for this service."""
+ try:
+ level = self.get_taxonomy_level(data)
+ except AttributeError:
+ level = 'info'
+ return [
+ {
+ 'level': level,
+ 'namespace': self._taxonomy_namespace,
+ 'predicate': self._taxonomy_predicate,
+ 'value': data.get(self._taxonomy_valuekey, None),
+ }
+ ]
+
+ def summarize(self, data):
+ """Build summary data structure for data processed by this service."""
+ return { 'taxonomies': self.get_taxonomies(data) }
+
+ def transform(self, data):
+ """Optionally post-process report data.
+
+ Used by IlluminateAnalyzer.run() to give a service an opportunity to transform
+ data before sending it upstream.
+ """
+ return data
+
+ def build_artifacts(self, report):
+ return None
+
+
+
+class Reputation(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Reputation'
+ self._description = 'RiskIQ Illuminate Reputation Score for an indicator.'
+ self._taxonomy_predicate = 'ReputationScore'
+ self._taxonomy_valuekey = 'score'
+ self._config['property'] = 'reputation'
+
+
+ def get_taxonomy_level(self, data):
+ levels = {
+ 'SUSPICIOUS': 'suspicious',
+ 'MALICIOUS': 'malicious',
+ 'GOOD': 'info',
+ 'UNKOWN': 'info'
+ }
+ return levels.get(data['classification'],'info')
+
+ def transform(self, data):
+ classes = {
+ 'SUSPICIOUS': 'warning',
+ 'MALICIOUS': 'danger',
+ 'GOOD': 'success',
+ 'UNKOWN': 'info'
+ }
+ data['uicontext'] = classes.get(data['classification'],'info')
+ for index, rule in enumerate(data['rules']):
+ if rule['severity'] >= 4:
+ uicontext = 'danger'
+ elif rule['severity'] >= 2:
+ uicontext = 'warning'
+ else:
+ uicontext = 'info'
+ data['rules'][index]['uicontext'] = uicontext
+ return data
+
+
+
+class Summary(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Summary'
+ self._description = 'RiskIQ Illuminate and PassiveTotal datasets with records for an indicator.'
+ self._taxonomy_predicate = 'Summary'
+ self._config['property'] = 'summary'
+
+ def get_taxonomies(self, data):
+ levels = {
+ 'malware_hashes': 'malicious',
+ 'articles': 'suspicious',
+ 'projects': 'suspicious'
+ }
+ taxonomies = []
+ for dataset, count in data.items():
+ if not type(count) is int:
+ continue
+ if dataset in levels.keys() and count > 0:
+ level = levels[dataset]
+ else:
+ level = 'info'
+ if dataset == 'total':
+ continue
+ if dataset == 'malware_hashes':
+ dataset = 'malware'
+ taxonomies.append({
+ 'level': level,
+ 'namespace': self._taxonomy_namespace,
+ 'predicate': dataset.title(),
+ 'value': count
+ })
+ return taxonomies
+
+ def transform(self, data):
+ levels = {
+ 'malware_hashes': 'danger',
+ 'projects': 'warning',
+ 'articles': 'danger'
+ }
+ uicontexts = {}
+ for field in ['resolutions','certificates','malware_hashes','projects','articles',
+ 'trackers','components','hostpairs','cookies','services']:
+ uicontexts[field] = levels.get(field,'info')
+ data['uicontexts'] = uicontexts
+ return data
+
+
+
+class Whois(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Whois'
+ self._description = 'RiskIQ Whois lookup for an indicator.'
+ self._taxonomy_predicate = 'Whois'
+ self._config['property'] = 'whois'
+
+ def get_taxonomies(self, data):
+ taxonomies = []
+ if 'age' in data:
+ age = data['age']
+ if age < 180:
+ level = 'suspicious'
+ taxonomies.append({
+ 'level': 'suspicious' if age < 180 else 'info',
+ 'namespace': self._taxonomy_namespace,
+ 'predicate': 'Whois Age (days)',
+ 'value': age
+ })
+ for email in data.get('emails',[]):
+ taxonomies.append({
+ 'level': 'info',
+ 'namespace': self._taxonomy_namespace,
+ 'predicate': 'Whois Email',
+ 'value': email
+ })
+ return taxonomies
+
+
+
+class SuspiciousCount:
+
+ def get_taxonomy_level(self, data):
+ return 'suspicious' if data.get('totalrecords',0) > 0 else 'info'
+
+
+
+class Articles(IlluminateServiceFile, SuspiciousCount):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Articles'
+ self._description = 'RiskIQ: OSINT articles that reference an indicator.'
+ self._taxonomy_predicate = 'Articles'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'articles'
+
+
+
+class Artifacts(IlluminateServiceFile, SuspiciousCount):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Artifacts'
+ self._description = 'RiskIQ: Illuminate / PassiveTotal project artifacts that match an indicator.'
+ self._taxonomy_predicate = 'Artifacts'
+ self._taxonomy_level = 'suspicious'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'artifacts'
+
+
+
+class Certificates(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Certificates'
+ self._description = 'RiskIQ: SSL/TLS certificates associated with an indicator.'
+ self._taxonomy_predicate = 'Certificates'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'certificates'
+
+
+
+class Components(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Components'
+ self._description = 'RiskIQ: web components observed during crawls on a hostname.'
+ self._taxonomy_predicate = 'Components'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'components'
+
+
+
+class Cookies(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Cookies'
+ self._description = 'RiskIQ: cookies observed during crawls on a hostname.'
+ self._taxonomy_predicate = 'Cookies'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'cookies'
+
+
+
+class HostpairParents(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_HostpairParents'
+ self._description = 'RiskIQ: hosts with a parent web component relationship to an IOC.'
+ self._taxonomy_predicate = 'HostpairParents'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'hostpair_parents'
+
+
+
+class HostpairChildren(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_HostpairChildren'
+ self._description = 'RiskIQ: hosts with a child web component relationship to an IOC.'
+ self._taxonomy_predicate = 'HostpairChildren'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'hostpair_children'
+
+
+
+class Malware(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Malware'
+ self._description = 'RiskIQ: malware hashes from various sources associated with an IOC.'
+ self._taxonomy_predicate = 'Malware'
+ self._taxonomy_level = 'malicious'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'malware'
+
+
+
+class Projects(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Projects'
+ self._description = 'RiskIQ: Illuminate / PassiveTotal projects that contain an artifact which matches an IOC.'
+ self._taxonomy_predicate = 'Projects'
+ self._taxonomy_level = 'suspicious'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'projects'
+
+
+
+class Resolutions(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Resolutions'
+ self._description = 'RiskIQ: PDNS resolutions for an IOC.'
+ self._taxonomy_predicate = 'Resolutions'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'resolutions'
+
+
+
+class Subdomains(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Subdomains'
+ self._description = 'RiskIQ: subdomains observed historically in pDNS records.'
+ self._dataTypeList = ['fqdn','domain']
+ self._taxonomy_predicate = 'Subdomains'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'subdomains'
+
+ def build_artifacts(self, report):
+ return [ { 'dataType': 'fqdn', 'data': r.get('hostname') } for r in report.get('records', []) ]
+
+
+
+class Trackers(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Trackers'
+ self._description = 'RiskIQ: trackers observed during a crawl on a host.'
+ self._taxonomy_predicate = 'Trackers'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'trackers'
+
+
+
+class Services(IlluminateServiceFile):
+
+ def __init__(self):
+ super().__init__()
+ self._name = 'RiskIQ_Services'
+ self._description = 'RiskIQ: services observed on an IP address.'
+ self._dataTypeList = ['ip']
+ self._taxonomy_predicate = 'Services'
+ self._taxonomy_level = 'info'
+ self._taxonomy_valuekey = 'totalrecords'
+ self._config['property'] = 'services'
+
+
+
+SERVICES = {
+ 'artifacts': Artifacts,
+ 'articles': Articles,
+ 'certificates': Certificates,
+ 'components': Components,
+ 'cookies': Cookies,
+ 'hostpair_parents': HostpairParents,
+ 'hostpair_children': HostpairChildren,
+ 'malware': Malware,
+ 'projects': Projects,
+ 'reputation': Reputation,
+ 'resolutions': Resolutions,
+ 'services': Services,
+ 'subdomains': Subdomains,
+ 'trackers': Trackers,
+ 'summary': Summary,
+ 'whois': Whois,
+}
+
+
+if __name__ == '__main__':
+ import argparse
+ import subprocess
+
+ parser = argparse.ArgumentParser()
+ cmdgroup = parser.add_mutually_exclusive_group()
+ cmdgroup.add_argument('--generate', dest='cmd', action='store_const', const='generate',
+ help='Generate service files for each service.')
+ cmdgroup.add_argument('--test', dest='cmd', action='store_const',const='test',
+ help='Test a service')
+ parser.add_argument('--property', choices=SERVICES.keys(),
+ help='Analyzer object property to test.')
+ parser.add_argument('--input',
+ help='Input value of the IOC to test.')
+ parser.add_argument('--type', choices=['fqdn','domain','ip'],
+ help='TheHive type of the IOC to test.')
+ parser.add_argument('--username',
+ help='API username; will be read from passivetotal lib if not provided')
+ parser.add_argument('--apikey',
+ help='API key; will be read from passivetotal lib if not provided')
+ parser.add_argument('--days-back', default=180, type=int,
+ help='Number of days back to search.')
+ args = parser.parse_args()
+
+ if args.cmd is None:
+ parser.print_help()
+ exit(1)
+
+ if args.cmd == 'generate':
+ if args.property is not None:
+ SERVICES[args.property]().generate()
+ else:
+ for svc in SERVICES.values():
+ svc().generate()
+ exit(0)
+
+ if args.cmd == 'test':
+ if args.property is None:
+ print('Property (--property) is required for a test')
+ exit(1)
+ if args.input is None:
+ print('Input value (--input) is required for a test')
+ exit(1)
+ if args.type is None:
+ print('Input type (--type) is required for a test')
+ if args.username is None or args.apikey is None:
+ from passivetotal.libs.account import AccountClient
+ client = AccountClient.from_config()
+ username = client.username if args.username is None else args.username
+ apikey = client.api_key if args.apikey is None else args.apikey
+ test = {
+ "data": args.input,
+ "dataType": args.type,
+ "tlp":0,
+ "config":{
+ "key":"1234567890abcdef",
+ "max_tlp":3,
+ "check_tlp":True,
+ "property":args.property,
+ "days_back": args.days_back,
+ "username": username,
+ "api_key": apikey,
+ "service": 'RiskIQ_{}'.format(args.property.title()),
+ }
+ }
+ result = subprocess.run(
+ ['python3','_analyzer.py'],
+ input=json.dumps(test),
+ stdout=subprocess.PIPE,
+ text=True
+ )
+ print(result.stdout)
+
+
\ No newline at end of file
diff --git a/analyzers/RiskIQ/requirements.txt b/analyzers/RiskIQ/requirements.txt
new file mode 100644
index 000000000..6d082c473
--- /dev/null
+++ b/analyzers/RiskIQ/requirements.txt
@@ -0,0 +1,2 @@
+cortexutils
+passivetotal>=2.5.2
\ No newline at end of file
diff --git a/responders/RiskIQ/RiskIQ_PushArtifactToProject.json b/responders/RiskIQ/RiskIQ_PushArtifactToProject.json
new file mode 100644
index 000000000..1472565c9
--- /dev/null
+++ b/responders/RiskIQ/RiskIQ_PushArtifactToProject.json
@@ -0,0 +1,62 @@
+{
+ "author": "RiskIQ",
+ "baseConfig": "RiskIQ",
+ "command": "RiskIQ/_responder.py",
+ "config": {
+ "service": "PushArtifactToProject"
+ },
+ "configurationItems": [
+ {
+ "description": "API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)",
+ "multi": false,
+ "name": "username",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "description": "API key of the RiskIQ Illuminate or PassiveTotal account",
+ "multi": false,
+ "name": "api_key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": "analyst",
+ "description": "Visiblity for new RiskIQ Illuminate projects (analyst, team, or public).",
+ "multi": false,
+ "name": "project_visibility",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "defaultValue": "Hive:",
+ "description": "Prefix to add when auto-generating project names from case names.",
+ "multi": false,
+ "name": "project_prefix",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "description": "Tag to apply to artifact in TheHive when is has been pushed to a RiskIQ Illuminate Project (leave blank to skip tagging).",
+ "multi": false,
+ "name": "thehive_artifact_tag",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "description": "Tag to apply to artifact in RiskIQ Illuminate when is has been pushed to an Illuminate Project (leave blank to skip tagging).",
+ "multi": false,
+ "name": "riq_artifact_tag",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "dataTypeList": [
+ "thehive:case_artifact"
+ ],
+ "description": "Push a case to a RiskIQ Illuminate project.",
+ "license": "AGPL-V3",
+ "name": "RiskIQ_PushArtifactToProject",
+ "url": "https://github.com/TheHive-Project/Cortex-Analyzers",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/responders/RiskIQ/_responder.py b/responders/RiskIQ/_responder.py
new file mode 100755
index 000000000..4fd4066d0
--- /dev/null
+++ b/responders/RiskIQ/_responder.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# encoding: utf-8
+
+
+from cortexutils.responder import Responder
+from _services import SERVICES
+
+
+
+class RiskIQIlluminate(Responder):
+ def __init__(self):
+ Responder.__init__(self)
+ self._username = self.get_param('config.username', None, 'RiskIQ Illuminate username is missing')
+ self._api_key = self.get_param('config.api_key', None, 'RiskIQ Illuminate api_key is missing')
+ self._service_name = self.get_param('config.service', None, 'Service name is required')
+ self._project_visibility = self.get_param('config.project_visiblity','analyst')
+ self._project_prefix = self.get_param('config.project_prefix', 'Hive:')
+ self._thehive_artifact_tag = self.get_param('config.thehive_artifact_tag',None)
+ self._riq_artifact_tag = self.get_param('config.riq_artifact_tag',None)
+ self._service = SERVICES[self._service_name](
+ visibility = self._project_visibility,
+ prefix = self._project_prefix,
+ thehive_artifact_tag = self._thehive_artifact_tag,
+ riq_artifact_tag = self._riq_artifact_tag,
+ username = self._username,
+ api_key = self._api_key
+ )
+
+ def run(self):
+ Responder.run(self)
+ self._service.run(self.get_param('data'))
+ report = self._service.get_report()
+ if 'error' in report:
+ self.error(report['error'])
+ self.report(report)
+
+ def operations(self, raw):
+ ops = []
+ for op in self._service.get_operations():
+ ops.append(self.build_operation(op['name'], **op['kwargs']))
+ return ops
+
+
+
+if __name__ == '__main__':
+ RiskIQIlluminate().run()
\ No newline at end of file
diff --git a/responders/RiskIQ/_services.py b/responders/RiskIQ/_services.py
new file mode 100644
index 000000000..41956907a
--- /dev/null
+++ b/responders/RiskIQ/_services.py
@@ -0,0 +1,199 @@
+import json
+from passivetotal import ProjectsRequest, ArtifactsRequest
+
+VERSION = '1.0'
+
+
+
+class IlluminateServiceFile():
+ """Base service class for all RiskIQ service files."""
+
+ def __init__(self, **kwargs):
+ self._name = 'RiskIQ_{}'.format(self.__class__.__name__)
+ self._version = VERSION
+ self._author = 'RiskIQ'
+ self._url = 'https://github.com/TheHive-Project/Cortex-Analyzers'
+ self._license = 'AGPL-V3'
+ self._command = 'RiskIQ/_responder.py'
+ self._baseconfig = 'RiskIQ'
+ self._description = ''
+ self._dataTypeList = []
+ self._config = { 'service': self.__class__.__name__ }
+ self._report = {}
+ self._operations = []
+ self._params = kwargs
+ self._configitems = [
+ {
+ 'name': 'username',
+ 'description': 'API username of the RiskIQ Illuminate or PassiveTotal account (usually an email address)',
+ 'type': 'string',
+ 'multi': False,
+ 'required': True
+ },
+ {
+ 'name': 'api_key',
+ 'description': 'API key of the RiskIQ Illuminate or PassiveTotal account',
+ 'type': 'string',
+ 'multi': False,
+ 'required': True
+ },
+ {
+ 'name': 'project_visibility',
+ 'description': 'Visiblity for new RiskIQ Illuminate projects (analyst, team, or public).',
+ 'type': 'string',
+ 'multi': False,
+ 'required': True,
+ 'defaultValue': 'analyst'
+ },
+ {
+ 'name': 'project_prefix',
+ 'description': 'Prefix to add when auto-generating project names from case names.',
+ 'type': 'string',
+ 'multi': False,
+ 'required': False,
+ 'defaultValue': 'Hive:'
+ },
+ {
+ 'name': 'thehive_artifact_tag',
+ 'description': 'Tag to apply to artifact in TheHive when is has been pushed to a RiskIQ Illuminate Project (leave blank to skip tagging).',
+ 'type': 'string',
+ 'multi': False,
+ 'required': False
+ },
+ {
+ 'name': 'riq_artifact_tag',
+ 'description': 'Tag to apply to artifact in RiskIQ Illuminate when is has been pushed to an Illuminate Project (leave blank to skip tagging).',
+ 'type': 'string',
+ 'multi': False,
+ 'required': False
+ }
+ ]
+
+ def generate(self):
+ """Creates and writes a JSON file to the local directory describing this service."""
+ obj = {
+ 'name': self._name,
+ 'version': self._version,
+ 'author': self._author,
+ 'url': self._url,
+ 'license': self._license,
+ 'description': self._description,
+ 'dataTypeList': self._dataTypeList,
+ 'command': self._command,
+ 'baseConfig': self._baseconfig,
+ 'config': self._config,
+ 'configurationItems': self._configitems,
+ }
+ filename = '{}.json'.format(self._name)
+ with open(filename, 'w') as f:
+ json.dump(obj, f, indent=2, sort_keys=True)
+
+ def add_operation(self, opname, **kwargs):
+ self._operations.append({ 'name': opname, 'kwargs': kwargs})
+
+ def run(self, input):
+ return
+
+ def get_report(self):
+ return self._report
+
+ def get_operations(self):
+ return self._operations
+
+
+
+class PushArtifactToProject(IlluminateServiceFile):
+
+ CUSTOMFIELD = 'riq-project-guid'
+
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ self._description = 'Push a case to a RiskIQ Illuminate project.'
+ self._dataTypeList = ["thehive:case_artifact"]
+
+ def run(self, data):
+ project_name = '{0}Case #{1} - {2}'.format(
+ self._params.get('prefix',''),
+ data['case']['caseId'],
+ data['case']['title']
+ )
+ self._report['case'] = data['case']
+ project_description = data['case'].get('description','')
+ project_visibility = self._params['visibility']
+ request = ProjectsRequest(username=self._params['username'], api_key=self._params['api_key'])
+ request.set_context('thehive','riq-responder',VERSION,self.__class__.__name__)
+ projects = request.find_projects(project_name, visibility=project_visibility)
+ if len(projects) > 1:
+ self._report['error'] = 'Found more than one project with the same name'
+ return
+ if len(projects) == 0:
+ project = request.create_project(project_name, visibility=project_visibility, description=project_description)
+ self._report['riq_project'] = project
+ else:
+ project = projects[0]
+ project_guid = project['guid']
+ request = ArtifactsRequest(username=self._params['username'], api_key=self._params['api_key'])
+ request.set_context('thehive','riq-responder',VERSION,self.__class__.__name__)
+ artifact_tag = self._params.get('riq_artifact_tag')
+ if artifact_tag is not None:
+ tags = [artifact_tag]
+ else:
+ tags = None
+ self._report['riq_artifact'] = request.upsert_artifact(project_guid, data['data'], tags=tags)
+ thehive_artifact_tag = self._params.get('thehive_artifact_tag')
+ if thehive_artifact_tag is not None:
+ if thehive_artifact_tag not in data['tags']:
+ self.add_operation('AddTagToArtifact', tag=thehive_artifact_tag)
+ #self._report['ops'] = self._operations
+
+
+
+
+
+
+
+SERVICES = {
+ 'PushArtifactToProject': PushArtifactToProject,
+}
+
+
+if __name__ == '__main__':
+ import argparse
+ import subprocess
+
+ parser = argparse.ArgumentParser()
+ cmdgroup = parser.add_mutually_exclusive_group()
+ cmdgroup.add_argument('--generate', dest='cmd', action='store_const', const='generate',
+ help='Generate service files for each service.')
+ cmdgroup.add_argument('--test', dest='cmd', action='store_const',const='test',
+ help='Test a service')
+ parser.add_argument('--responder', choices=SERVICES.keys(),
+ help='Responder to test.')
+ parser.add_argument('--input',
+ help='Input value of the IOC to test.')
+ parser.add_argument('--type', choices=['fqdn','domain','ip'],
+ help='TheHive type of the IOC to test.')
+ parser.add_argument('--username',
+ help='API username; will be read from passivetotal lib if not provided')
+ parser.add_argument('--apikey',
+ help='API key; will be read from passivetotal lib if not provided')
+ parser.add_argument('--days-back', default=180, type=int,
+ help='Number of days back to search.')
+ args = parser.parse_args()
+
+ if args.cmd is None:
+ parser.print_help()
+ exit(1)
+
+ if args.cmd == 'generate':
+ if args.responder is not None:
+ SERVICES[args.responder]().generate()
+ else:
+ for svc in SERVICES.values():
+ svc().generate()
+ exit(0)
+
+ if args.cmd == 'test':
+ print('Sorry, testing responders is not yet implemented.')
+
+
\ No newline at end of file
diff --git a/responders/RiskIQ/requirements.txt b/responders/RiskIQ/requirements.txt
new file mode 100644
index 000000000..e468bfcf1
--- /dev/null
+++ b/responders/RiskIQ/requirements.txt
@@ -0,0 +1,2 @@
+cortexutils
+passivetotal
diff --git a/thehive-templates/RiskIQ_Articles_1_0/long.html b/thehive-templates/RiskIQ_Articles_1_0/long.html
new file mode 100644
index 000000000..21d9ca406
--- /dev/null
+++ b/thehive-templates/RiskIQ_Articles_1_0/long.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+ No articles available for this IOC.
+
+
+
+ RiskIQ OSINT Articles
+
+
+
+
+
{{art.title}}
+
+ {{tag}}
+
+
Published {{art.date_published}} ({{art.type}})
+
{{art.summary}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Artifacts_1_0/long.html b/thehive-templates/RiskIQ_Artifacts_1_0/long.html
new file mode 100644
index 000000000..9c7008258
--- /dev/null
+++ b/thehive-templates/RiskIQ_Artifacts_1_0/long.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+ No RiskIQ artifacts reference this IOC.
+
+
+
+ RiskIQ Artifacts
+
+
+
+
+
+
Project GUID
+
{{art.project_guid}}
+
+
+
Article GUID
+
{{art.artifact_guid}}
+
+
+
Is monitored?
+
Created
+
+
+
{{art.is_monitored}}
+
{{art.created}}
+
+
+
+
{{art.owner}}
+
{{art.creator}}
+
+
+
User Tags
+
Global Tags
+
+
+
+ {{tag}}
+
+
+ {{tag}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Certificates_1_0/long.html b/thehive-templates/RiskIQ_Certificates_1_0/long.html
new file mode 100644
index 000000000..e644dc578
--- /dev/null
+++ b/thehive-templates/RiskIQ_Certificates_1_0/long.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+ No certificates available for this IOC.
+
+
+
+ RiskIQ TLS/SSL Certificates
+
+
+
+
+
+
+
Certificate
+
+
+
+
{{key}}
+
+ {{value}}
+ —
+
+
+
+
+
+
+
+
+
+
Subject
+
+
+
+
{{key|limitTo:50:7}}
+
+ {{value}}
+ —
+
+
+
+
+
+
+
+
+
Issuer
+
+
+
+
{{key|limitTo:50:6}}
+
+ {{value}}
+ —
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Components_1_0/long.html b/thehive-templates/RiskIQ_Components_1_0/long.html
new file mode 100644
index 000000000..45a048364
--- /dev/null
+++ b/thehive-templates/RiskIQ_Components_1_0/long.html
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+ No web components available for this IOC.
+
+
+
+ RiskIQ Web Components
+
+
+
+
+
+ Category
+ Component
+ Version
+ Host
+ First Seen
+ Last Seen
+
+
+
+
+ {{comp.category}}
+ {{comp.label}}
+ {{comp.version}}
+ {{comp.hostname}}
+ {{comp.firstseen}}
+ {{comp.lastseen}}
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Cookies_1_0/long.html b/thehive-templates/RiskIQ_Cookies_1_0/long.html
new file mode 100644
index 000000000..dff655a01
--- /dev/null
+++ b/thehive-templates/RiskIQ_Cookies_1_0/long.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+ No cookies available for this IOC.
+
+
+
+ RiskIQ Host Cookies
+
+
+
+
+
+
+
Distinct Cookie Names
+
+
+
+
+
+
Cookie History
+
+
+
+
+ Cookie Name
+ Cookie Domain
+ Host
+ First Seen
+ Last Seen
+
+
+
+
+ {{cook.name}}
+ {{cook.domain}}
+ {{cook.hostname}}
+ {{cook.firstseen}}
+ {{cook.lastseen}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_HostpairChildren_1_0/long.html b/thehive-templates/RiskIQ_HostpairChildren_1_0/long.html
new file mode 100644
index 000000000..499c03f1a
--- /dev/null
+++ b/thehive-templates/RiskIQ_HostpairChildren_1_0/long.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+ No child hostpairs available for this IOC.
+
+
+
+ RiskIQ Child Hostpairs
+
+
+
+
+
Child Hostpair History
+
+
+
+
+ Cause
+ Child
+ First Seen
+ Last Seen
+
+
+
+
+ {{hp.cause}}
+ {{hp.child}}
+ {{hp.firstseen}}
+ {{hp.lastseen}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_HostpairParents_1_0/long.html b/thehive-templates/RiskIQ_HostpairParents_1_0/long.html
new file mode 100644
index 000000000..d6bb8e566
--- /dev/null
+++ b/thehive-templates/RiskIQ_HostpairParents_1_0/long.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+ No parent hostpairs available for this IOC.
+
+
+
+ RiskIQ Parent Hostpairs
+
+
+
+
+
Parent Hostpair History
+
+
+
+
+ Cause
+ Parent
+ First Seen
+ Last Seen
+
+
+
+
+ {{hp.cause}}
+ {{hp.parent}}
+ {{hp.firstseen}}
+ {{hp.lastseen}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Malware_1_0/long.html b/thehive-templates/RiskIQ_Malware_1_0/long.html
new file mode 100644
index 000000000..287351165
--- /dev/null
+++ b/thehive-templates/RiskIQ_Malware_1_0/long.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+ No malware hashes found for this IOC.
+
+
+
+ RiskIQ Malware Hashes
+
+
+
+
+
+ Hash
+ Source
+ Date Collected
+
+
+
+
+ {{mal.hash}}
+ {{mal.source}}
+ {{mal.date_collected}}
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Projects_1_0/long.html b/thehive-templates/RiskIQ_Projects_1_0/long.html
new file mode 100644
index 000000000..7c2292d77
--- /dev/null
+++ b/thehive-templates/RiskIQ_Projects_1_0/long.html
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+ No projects found that reference this IOC.
+
+
+
+ RiskIQ Projects
+
+
+
+
{{proj.name}}
+
+
+
Description
+
{{proj.description}}
+
+
+
Created
+
{{proj.created}}
+
+
+
Visibility
+
{{proj.visibility}}
+
+
+
+
Owner
+
{{proj.owner}}
+
+
+
Creator
+
{{proj.creator}}
+
+
+
Organization
+
{{proj.organization}}
+
+
+
GUID
+
{{proj.project_guid}}
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Reputation_1_0/long.html b/thehive-templates/RiskIQ_Reputation_1_0/long.html
new file mode 100644
index 000000000..223a1a806
--- /dev/null
+++ b/thehive-templates/RiskIQ_Reputation_1_0/long.html
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+ No Reputation Score available for this IOC.
+
+
+
+ RiskIQ Illuminate Reputation Score
+
+
+
+ Score:
+
+
+
+ {{content.score}} / 100 ({{content.classification}})
+
+
+
+
+
+
+
+
+ Score Justification
+
+
+
+
+ Severity
+ Name
+ Description
+
+
+ {{r.severity}}
+ {{r.name || 'None'}}
+ {{r.description || 'None'}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Resolutions_1_0/long.html b/thehive-templates/RiskIQ_Resolutions_1_0/long.html
new file mode 100644
index 000000000..59737b40f
--- /dev/null
+++ b/thehive-templates/RiskIQ_Resolutions_1_0/long.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+ RiskIQ PDNS Resolutions
+
+
+
+
+
+
+
Earliest Available Record
+
{{content.firstseen}}
+
+
+
Most Recent Record
+
{{content.lastseen}}
+
+
+
+
+
Query Start Date
+
{{content.datestart}}
+
+
+
Query End Date
+
{{content.dateend}}
+
+
+
+
+
+
pDNS Resolution History
+
+
+
+
+ Type
+ Resolution
+ First Seen
+ Last Seen
+ Sources
+
+
+
+
+ {{p.recordtype}}
+ {{p.resolve}}
+ {{p.firstseen}}
+ {{p.lastseen}}
+
+ {{s}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Services_1_0/long.html b/thehive-templates/RiskIQ_Services_1_0/long.html
new file mode 100644
index 000000000..e09d4d8dc
--- /dev/null
+++ b/thehive-templates/RiskIQ_Services_1_0/long.html
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+ No service history available for this IOC.
+
+
+
+ RiskIQ Services
+
+
+
+
{{svc.protocol}} {{svc.port}} ({{svc.status}})
+
+
+
+
First Seen
+
Last Seen
+
Observations
+
+
+
{{svc.firstseen}}
+
{{svc.lastseen}}
+
{{svc.count}}
+
+
+
+
+
+
+
+ Service
+ Category
+ Version
+ First Seen
+ Last Seen
+
+
+
+
+ {{s.label}}
+ {{s.category}}
+ {{s.version}}
+ {{s.firstSeen}}
+ {{s.lastSeen}}
+
+
+
+
+
+
+
+
+
+
+
+
+ Scan Type
+ Observations
+ First Seen
+ Last Seen
+ Banners
+
+
+
+
+ {{b.scanType}}
+ {{b.count}}
+ {{b.firstSeen}}
+ {{b.lastSeen}}
+ View
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Service
+ Category
+ Version
+ First Seen
+ Last Seen
+
+
+
+
+ {{s.label}}
+ {{s.category}}
+ {{s.version}}
+ {{s.firstSeen}}
+ {{s.lastSeen}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Subdomains_1_0/long.html b/thehive-templates/RiskIQ_Subdomains_1_0/long.html
new file mode 100644
index 000000000..0df6b0a3c
--- /dev/null
+++ b/thehive-templates/RiskIQ_Subdomains_1_0/long.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+ RiskIQ Subdomains
+
+
+
+
+
+
+
Primary Domain
+
{{content.primary_domain}}
+
+
+
+
+
+
Observed Subdomains
+
+
+
+
+ Subdomain
+ Primary Domain
+ FQDN
+
+
+
+
+ {{p.subdomain}}
+ {{p.primary_domain}}
+ {{p.hostname}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
diff --git a/thehive-templates/RiskIQ_Summary_1_0/long.html b/thehive-templates/RiskIQ_Summary_1_0/long.html
new file mode 100644
index 000000000..7fa855534
--- /dev/null
+++ b/thehive-templates/RiskIQ_Summary_1_0/long.html
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+ RiskIQ Illuminate Available Datasets
+
+
+
+
+
+
+ pDNS
+
+
+
{{content.resolutions}}
+
+
+
+
+
+
+ TLS/SSL
+
+
+
{{content.certificates}}
+
+
+
+
+
+
+ Articles
+
+
+
{{content.articles}}
+
+
+
+
+
+
+ Projects
+
+
+
{{content.projects}}
+
+
+
+
+
+
+ Malware
+
+
+
{{content.malware_hashes}}
+
+
+
+
+
+
+ Trackers
+
+
+
{{content.trackers}}
+
+
+
+
+
+
+ Components
+
+
+
{{content.components}}
+
+
+
+
+
+
+ Cookies
+
+
+
{{content.cookies}}
+
+
+
+
+
+
+ Host Pairs
+
+
+
{{content.hostpairs}}
+
+
+
+
+
+
+ Services
+
+
+
{{content.services}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Trackers_1_0/long.html b/thehive-templates/RiskIQ_Trackers_1_0/long.html
new file mode 100644
index 000000000..28d4eba11
--- /dev/null
+++ b/thehive-templates/RiskIQ_Trackers_1_0/long.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+ No trackers available for this IOC.
+
+
+
+ RiskIQ Trackers
+
+
+
+
+
Tracker History
+
+
+
+
+ Tracker
+ Value
+ First Seen
+ Last Seen
+
+
+
+
+ {{t.trackertype}}
+ {{t.value}}
+ {{t.firstseen}}
+ {{t.lastseen}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file
diff --git a/thehive-templates/RiskIQ_Whois_1_0/long.html b/thehive-templates/RiskIQ_Whois_1_0/long.html
new file mode 100644
index 000000000..06f649bb5
--- /dev/null
+++ b/thehive-templates/RiskIQ_Whois_1_0/long.html
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+ No Whois data available for this IOC.
+
+
+
+ RiskIQ Whois Record
+
+
+
+
+
+
+
+
Primary Contact
+
+
Name
+
{{content.name}}
+
Organization
+
{{content.organization}}
+
Telephone
+
{{content.telephone}}
+
Email
+
{{content.email}}
+
+
+
+
+
+
Domain Registration
+
+
Registered
+
+ {{content.registered}}
+ {{content.age}}days
+ {{content.age}} days
+ {{content.age}} days
+
+
Expires
+
{{content.expiresAt}}
+
Registrar
+
{{content.registrar}}
+
Registry Updated
+
{{content.registryUpdatedAt}}
+
Name Servers
+
{{ns}}
+
+
+
+
+
+
+
+
Admin Contact
+
+
Name
+
{{content.admin.name}}
+
Organization
+
{{content.admin.organization}}
+
Telephone
+
{{content.admin.telephone}}
+
Email
+
{{content.admin.email}}
+
State/Province/Region
+
{{content.admin.state}}
+
Country
+
{{content.admin.country}}
+
+
+
+
+
+
Registrant Contact
+
+
Name
+
{{content.registrant.name}}
+
Organization
+
{{content.registrant.organization}}
+
Telephone
+
{{content.registrant.telephone}}
+
Email
+
{{content.registrant.email}}
+
State/Province/Region
+
{{content.registrant.state}}
+
Country
+
{{content.registrant.country}}
+
+
+
+
+
+
+
+
+
Tech Contact
+
+
Name
+
{{content.tech.name}}
+
Organization
+
{{content.tech.organization}}
+
Telephone
+
{{content.tech.telephone}}
+
Email
+
{{content.tech.email}}
+
State/Province/Region
+
{{content.tech.state}}
+
Country
+
{{content.tech.country}}
+
+
+
+
+
+
Billing Contact
+
+
Name
+
{{content.billing.name}}
+
Organization
+
{{content.billing.organization}}
+
Telephone
+
{{content.billing.telephone}}
+
Email
+
{{content.billing.email}}
+
State/Province/Region
+
{{content.billing.state}}
+
Country
+
{{content.billing.country}}
+
+
+
+
+
+
+ Raw Whois Record
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(artifact.data || artifact.attachment.name) | fang}}
+
+
+ {{content.errorMessage}}
+
+
\ No newline at end of file