From 68ab77872331e1e3eaf7ceb0e7c15e52b977ce80 Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 25 Jun 2026 15:26:54 -0400 Subject: [PATCH 1/3] Add Flowtriq expansion module for DDoS threat intelligence --- misp_modules/modules/expansion/flowtriq.py | 302 +++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 misp_modules/modules/expansion/flowtriq.py diff --git a/misp_modules/modules/expansion/flowtriq.py b/misp_modules/modules/expansion/flowtriq.py new file mode 100644 index 00000000..2114f368 --- /dev/null +++ b/misp_modules/modules/expansion/flowtriq.py @@ -0,0 +1,302 @@ +""" +Flowtriq DDoS Intelligence Module for MISP + +Enriches IP addresses with DDoS attack data from Flowtriq. +When given an IP, queries the Flowtriq API to check if this IP +has been observed as a DDoS attack source across Flowtriq's +network of monitored infrastructure. + +Returns attack context including attack families, severity, +peak traffic rates, geographic origin, and threat intel matches. +""" + +import json +import requests + +misperrors = {'error': 'Error'} +mispattributes = { + 'input': ['ip-src', 'ip-dst'], + 'format': 'misp_standard', +} +moduleinfo = { + 'version': '1.0', + 'author': 'Flowtriq', + 'description': 'Query Flowtriq for DDoS attack intelligence on IP addresses', + 'module-type': ['expansion', 'hover'], + 'name': 'Flowtriq DDoS Intelligence', + 'logo': '', + 'requirements': ['Flowtriq API key and API URL'], + 'features': ( + 'Queries the Flowtriq IP threat lookup API to check whether an IP ' + 'has been observed as a DDoS attack source. Returns structured ' + 'enrichment data including attack families, severity breakdown, ' + 'peak PPS, ASN, country, risk score, and related attacker IPs.' + ), + 'references': ['https://flowtriq.com'], + 'input': 'An IP address attribute (ip-src or ip-dst).', + 'output': ( + 'MISP attributes and objects describing DDoS attack activity ' + 'associated with the queried IP: attack types, timestamps, ' + 'severity, peak traffic, related IPs, and threat intel matches.' + ), +} +moduleconfig = ['api_key', 'api_url'] + +_DEFAULT_API_URL = 'https://flowtriq.com' +_TIMEOUT = 15 + + +def handler(q=False): + if q is False: + return False + + request = json.loads(q) + + # Validate config + config = request.get('config', {}) + api_key = config.get('api_key', '').strip() + api_url = config.get('api_url', '').strip().rstrip('/') or _DEFAULT_API_URL + + if not api_key: + misperrors['error'] = 'Flowtriq API key is required. Set it in the module configuration.' + return misperrors + + # Extract IP from the request + ip = None + attribute = request.get('attribute', {}) + for attr_type in ('ip-src', 'ip-dst'): + if attr_type in request: + ip = request[attr_type] + break + if not ip and attribute.get('value'): + ip = attribute['value'] + + if not ip: + misperrors['error'] = 'No IP address provided in the request.' + return misperrors + + # Query Flowtriq IP threat lookup + try: + response = requests.post( + f'{api_url}/api/ip-lookup.php', + json={'ip': ip}, + headers={ + 'Authorization': f'Bearer {api_key}', + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'MISP-Flowtriq/1.0', + }, + timeout=_TIMEOUT, + verify=True, + ) + except requests.exceptions.ConnectionError: + misperrors['error'] = f'Cannot connect to Flowtriq API at {api_url}' + return misperrors + except requests.exceptions.Timeout: + misperrors['error'] = f'Flowtriq API request timed out after {_TIMEOUT}s' + return misperrors + except requests.exceptions.RequestException as e: + misperrors['error'] = f'Flowtriq API request failed: {e}' + return misperrors + + if response.status_code != 200: + misperrors['error'] = f'Flowtriq API returned HTTP {response.status_code}' + return misperrors + + try: + data = response.json() + except (ValueError, json.JSONDecodeError): + misperrors['error'] = 'Flowtriq API returned invalid JSON' + return misperrors + + if not data.get('ok'): + misperrors['error'] = data.get('error', 'Flowtriq API returned an error') + return misperrors + + if not data.get('found'): + return {'results': []} + + # Build enrichment results + results = _build_results(ip, data, attribute) + return {'results': results} + + +def _build_results(ip, data, attribute): + """Build MISP-standard enrichment results from Flowtriq API response.""" + results = [] + + risk_score = data.get('risk_score', 0) + reputation = data.get('reputation') + incidents = data.get('incidents', {}) + threat_intel = data.get('threat_intel', []) + related_ips = data.get('related_ips', {}) + ioc_matches = data.get('ioc_matches', {}) + + # -- Summary comment on the original attribute -- + summary_parts = [] + summary_parts.append(f'Flowtriq risk score: {risk_score}/100') + + total_incidents = incidents.get('total', 0) + if total_incidents: + summary_parts.append(f'Seen in {total_incidents} DDoS incident(s)') + + if reputation: + attack_count = reputation.get('attack_count', 0) + tenants_seen = reputation.get('tenants_seen', 0) + if attack_count: + summary_parts.append(f'{attack_count} attacks across {tenants_seen} network(s)') + if reputation.get('top_attack_family'): + summary_parts.append(f'Primary vector: {reputation["top_attack_family"]}') + if reputation.get('country'): + summary_parts.append(f'Country: {reputation["country"]}') + if reputation.get('asn'): + summary_parts.append(f'ASN: {reputation["asn"]}') + if reputation.get('peak_pps'): + summary_parts.append(f'Peak PPS: {reputation["peak_pps"]:,}') + + families = incidents.get('attack_families', {}) + if families: + top_families = ', '.join(list(families.keys())[:5]) + summary_parts.append(f'Attack families: {top_families}') + + severity = incidents.get('severity', {}) + sev_parts = [] + for level in ('critical', 'high', 'medium', 'low'): + count = severity.get(level, 0) + if count: + sev_parts.append(f'{count} {level}') + if sev_parts: + summary_parts.append(f'Severity: {", ".join(sev_parts)}') + + if threat_intel: + sources = set() + for ti in threat_intel: + sources.add(ti.get('source', 'unknown')) + summary_parts.append(f'Threat intel sources: {", ".join(sorted(sources))}') + + if ioc_matches: + summary_parts.append(f'IOC matches: {", ".join(list(ioc_matches.keys())[:5])}') + + summary_text = '. '.join(summary_parts) + '.' + + results.append({ + 'types': ['text'], + 'categories': ['External analysis'], + 'values': [summary_text], + 'comment': f'Flowtriq DDoS intelligence for {ip}', + }) + + # -- Reputation text attribute -- + if reputation: + rep_lines = [f'Flowtriq IP Reputation for {ip}:'] + rep_lines.append(f' Risk Score: {risk_score}/100') + rep_lines.append(f' Attack Count: {reputation.get("attack_count", 0)}') + rep_lines.append(f' Networks Seen: {reputation.get("tenants_seen", 0)}') + rep_lines.append(f' First Seen: {reputation.get("first_seen", "N/A")}') + rep_lines.append(f' Last Seen: {reputation.get("last_seen", "N/A")}') + rep_lines.append(f' Top Attack Family: {reputation.get("top_attack_family", "N/A")}') + rep_lines.append(f' Top Protocol: {reputation.get("top_protocol", "N/A")}') + rep_lines.append(f' Country: {reputation.get("country", "N/A")}') + rep_lines.append(f' ASN: {reputation.get("asn", "N/A")}') + rep_lines.append(f' Peak PPS: {reputation.get("peak_pps", 0):,}') + tags = reputation.get('tags', []) + if tags: + rep_lines.append(f' Tags: {", ".join(tags)}') + + results.append({ + 'types': ['text'], + 'categories': ['External analysis'], + 'values': ['\n'.join(rep_lines)], + 'comment': 'Flowtriq reputation data', + }) + + # -- ASN attribute -- + if reputation and reputation.get('asn'): + results.append({ + 'types': ['AS'], + 'categories': ['Network activity'], + 'values': [str(reputation['asn'])], + 'comment': f'ASN of {ip} per Flowtriq', + }) + + # -- First/last seen timestamps -- + if reputation: + if reputation.get('first_seen'): + results.append({ + 'types': ['datetime'], + 'categories': ['Network activity'], + 'values': [reputation['first_seen']], + 'comment': f'Flowtriq first seen {ip}', + }) + if reputation.get('last_seen'): + results.append({ + 'types': ['datetime'], + 'categories': ['Network activity'], + 'values': [reputation['last_seen']], + 'comment': f'Flowtriq last seen {ip}', + }) + + # -- Incident records as text -- + records = incidents.get('records', []) + if records: + inc_lines = [f'Flowtriq DDoS Incidents involving {ip} (last 90 days):'] + for i, rec in enumerate(records[:10], 1): + inc_lines.append(f' [{i}] {rec.get("date", "?")} - {rec.get("attack_family", "?")} ' + f'({rec.get("severity", "?")}) - ' + f'Peak: {rec.get("peak_pps", 0):,} pps / ' + f'{rec.get("peak_bps", 0):,} bps - ' + f'Duration: {rec.get("duration_sec", 0)}s - ' + f'{rec.get("source_ip_count", 0)} source IPs') + if rec.get('spoofing'): + inc_lines.append(f' Spoofing detected') + if rec.get('botnet'): + inc_lines.append(f' Botnet indicators') + + results.append({ + 'types': ['text'], + 'categories': ['External analysis'], + 'values': ['\n'.join(inc_lines)], + 'comment': 'Flowtriq incident history', + }) + + # -- Related attacker IPs -- + if related_ips: + for related_ip, co_occurrence in list(related_ips.items())[:10]: + results.append({ + 'types': ['ip-src'], + 'categories': ['Network activity'], + 'values': [related_ip], + 'comment': f'Co-attacker with {ip} in {co_occurrence} Flowtriq incident(s)', + 'tags': ['flowtriq:related-attacker'], + }) + + # -- Threat intel feed matches -- + if threat_intel: + ti_lines = [f'Threat Intel Matches for {ip}:'] + for ti in threat_intel: + ti_lines.append( + f' - {ti.get("source", "?")} ({ti.get("threat_type", "?")}) ' + f'confidence={ti.get("confidence", "?")} ' + f'seen={ti.get("times_seen", "?")}x ' + f'[{ti.get("first_seen", "?")} to {ti.get("last_seen", "?")}]' + ) + if ti.get('description'): + ti_lines.append(f' {ti["description"]}') + + results.append({ + 'types': ['text'], + 'categories': ['External analysis'], + 'values': ['\n'.join(ti_lines)], + 'comment': 'Flowtriq threat intel feed matches', + }) + + return results + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From cd2291525a5bee401762b1a0dce9ea1911d67f0e Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 25 Jun 2026 16:19:21 -0400 Subject: [PATCH 2/3] Fix flake8 F541: remove empty f-string prefixes --- misp_modules/modules/expansion/flowtriq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/flowtriq.py b/misp_modules/modules/expansion/flowtriq.py index 2114f368..03df3943 100644 --- a/misp_modules/modules/expansion/flowtriq.py +++ b/misp_modules/modules/expansion/flowtriq.py @@ -248,9 +248,9 @@ def _build_results(ip, data, attribute): f'Duration: {rec.get("duration_sec", 0)}s - ' f'{rec.get("source_ip_count", 0)} source IPs') if rec.get('spoofing'): - inc_lines.append(f' Spoofing detected') + inc_lines.append(' Spoofing detected') if rec.get('botnet'): - inc_lines.append(f' Botnet indicators') + inc_lines.append(' Botnet indicators') results.append({ 'types': ['text'], From b0bd84be190244841262e91d1a87cd980ad32190 Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 25 Jun 2026 17:34:30 -0400 Subject: [PATCH 3/3] Add Flowtriq logo and documentation metadata --- documentation/logos/flowtriq.png | Bin 0 -> 6773 bytes misp_modules/modules/expansion/flowtriq.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 documentation/logos/flowtriq.png diff --git a/documentation/logos/flowtriq.png b/documentation/logos/flowtriq.png new file mode 100644 index 0000000000000000000000000000000000000000..79703370514d766952b03d9f7ef18e863cc2cfce GIT binary patch literal 6773 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 Y$XbfVM*M*N*YZ(S)Kd5+XBGDU0A|{!%K!iX literal 0 HcmV?d00001 diff --git a/misp_modules/modules/expansion/flowtriq.py b/misp_modules/modules/expansion/flowtriq.py index 03df3943..8ed30e73 100644 --- a/misp_modules/modules/expansion/flowtriq.py +++ b/misp_modules/modules/expansion/flowtriq.py @@ -24,7 +24,7 @@ 'description': 'Query Flowtriq for DDoS attack intelligence on IP addresses', 'module-type': ['expansion', 'hover'], 'name': 'Flowtriq DDoS Intelligence', - 'logo': '', + 'logo': 'flowtriq.png', 'requirements': ['Flowtriq API key and API URL'], 'features': ( 'Queries the Flowtriq IP threat lookup API to check whether an IP '