Setup guide
Splunk threat intelligence lookups from the isMalicious threat lists
No credit card required · Free API key
On this page08
What you get
A lookup and an ES threat list both read one indicator per line, so the plain lists fit. Start with the critical IPs and the category lists; add the critical domains only after sizing the lookup.
| List | Entries | Rebuilt | Plans |
|---|---|---|---|
| blocklist-ips-critical.txtDefault. IPs listed by 6 or more threat sources, or 3 or more with a critical category. | about 89,000 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-ips-c2.txtCommand-and-control IPs reported by C2 trackers, whatever their level. | about 44,000 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-domains-c2.txtCommand-and-control domains reported by C2 trackers, whatever their level. | about 22,000 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-domains-ransomware.txtDomains in the ransomware category, whatever their level. | about 3,600 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-domains-critical.txtOnly after sizing: one lookup row or ES record per entry. | about 2 million | every 12 h | Basic, Pro, and Enterprise |
/app/integrations/splunkIOCs and SOC events pushed to your HTTP Event Collector, as sourcetypes ismalicious:ioc and ismalicious:soc. Not a feed: see Limits. | up to 1,000 per sync | when you sync | Pro and Enterprise |
Entries rounded from the build of ; each list is rebuilt every 12 hours. Today’s counts are public and need no key.
What each plan receives
- FreeFree account, or no key: the first 10% of each list, marked
X-Blocklist-Version: lite. - Basic, Pro, and EnterpriseBasic, Pro, and Enterprise: every list in full.
- Pro and EnterpriseTAXII 2.1 collections, for platforms that read STIX indicators: Pro and Enterprise.
- Pro and EnterpriseSIEM destinations in the dashboard (Splunk, Elastic, Sentinel): Pro and Enterprise.
GET https:/
Full list or 10% sample
| Field | Basic, Pro, and Enterprise | Free |
|---|---|---|
X-Blocklist-Version: | full | lite |
X-Blocklist-Percentage: | 100 | 10 |
Total entries: | <COUNT> | <COUNT> |
First lines of the file
# IsMalicious.com Blocklist - IPs (Critical)
# Format: Plain
# Generated: <BUILD_TIME>
# Total entries: <COUNT>
# Update frequency: every 12 hours
# Category: All
# Threat level: Critical
# Website: https://ismalicious.com
# © <YEAR> IsMalicious (compilation). Licensed to the downloading account under https://ismalicious.com/terms; redistribution of the compilation prohibited. Third-party entries remain under their providers' licences — see https://ismalicious.com/sources.
#Values in angle brackets are set by each build.
Prerequisites
- Splunk Enterprise 9.x or 10.x for the lookups. On Splunk Cloud Platform you cannot edit
.conffiles directly: see Limits. - A shell on the search head, with
curlandawk. - An isMalicious API key and secret, from Account › API access.
- Outbound HTTPS (TCP 443) over IPv4 from the Splunk search head to
api.ismalicious.com, which has no IPv6 address.
Set it up
Copy your API key and secret
Open Account › API access and copy the API Key and the API Secret. The full list needs a Basic, Pro, or Enterprise plan; a Free key gets the 10% sample, which the script refuses.Create the app and its credentials file
On the search head, create$SPLUNK_HOME/withetc/ apps/ ismalicious_lists/ bin/,local/andlookups/. Write the key and the secret tolocal/ismalicious.netrc, owned by the Splunk user, mode 600: curl reads them from the file, not from the command line.local/ ismalicious. netrcnetrc machine api.ismalicious.com login <API_KEY> password <API_SECRET>Add the fetch script
Save it asbin/fetch_ismalicious.sh, mode 755. It downloads the list, refuses an error, a body that is not a list, the 10% sample and a file whose entry count differs from its header, writes a two-column CSV lookup without the#header lines, and prints one status line:status=okwith the row count, orstatus=refused, with the reason in splunkd.log. A refused list keeps the previous lookup.bin/ fetch_ismalicious. shsh #!/bin/sh # Rebuilds lookups/$NAME.csv from $LIST and prints one status line, which the # scripted input indexes. Copy it per list: for a domain list, set FIELD=domain. set -eu APP_DIR=$(cd "$(dirname "$0")/.." && pwd) NETRC="$APP_DIR/local/ismalicious.netrc" LIST=blocklist-ips-critical.txt NAME=ismalicious_ips FIELD=ip RAW=$(mktemp) TMP=$(mktemp "$APP_DIR/lookups/.$NAME.csv.XXXXXX") trap 'rm -f "$RAW" "$TMP"' EXIT # fetch_list LIST OUT: download one list to OUT and check it. # On any failure OUT is removed and the function returns 1. fetch_list() { if ! code=$(curl --silent --show-error --fail --netrc-file "$NETRC" \ --proto '=https' --max-time 300 --retry 2 \ --output "$2" --write-out '%{http_code}' "https://api.ismalicious.com/blocklist/download/$1"); then rm -f "$2"; echo "$1: download failed" >&2; return 1 fi if [ "$code" != 200 ]; then rm -f "$2"; echo "$1: HTTP $code" >&2; return 1 fi if ! head -n 1 "$2" | grep -q '^[#!] IsMalicious.com Blocklist'; then rm -f "$2"; echo "$1: not an isMalicious list" >&2; return 1 fi if grep -q 'Lite Version' "$2"; then rm -f "$2"; echo "$1: lite list received, check the API key and the plan" >&2 return 1 fi # Every entry ends with a newline, and one header counts them: refuse a # cut, doubled or empty file. if [ -n "$(tail -c 1 "$2")" ]; then rm -f "$2"; echo "$1: cut short, no final newline" >&2; return 1 fi if [ "$(grep -c '^[#!] Total entries:' "$2")" != 1 ]; then rm -f "$2"; echo "$1: not one Total entries line" >&2; return 1 fi total=$(sed -n 's/^[#!] Total entries: \([0-9,]*\)$/\1/p' "$2" | tr -d ,) got=$(grep -c '^[^#!]' "$2" || true) if [ -z "$total" ] || [ "$total" = 0 ] || [ "$got" != "$total" ]; then rm -f "$2"; echo "$1: $got entries, the header says ${total:-none}" >&2 return 1 fi } # On any failure the previous lookup stays; the reason goes to splunkd.log. if ! fetch_list "$LIST" "$RAW"; then echo "ismalicious_fetch status=refused list=$LIST" exit 1 fi awk -v field="$FIELD" -v list="${LIST%.txt}" 'BEGIN { print field ",ismalicious_list" } !/^[#!]/ && NF { print $1 "," list }' "$RAW" > "$TMP" chmod 644 "$TMP" mv -f "$TMP" "$APP_DIR/lookups/$NAME.csv" echo "ismalicious_fetch status=ok list=$LIST rows=$(($(wc -l < "$APP_DIR/lookups/$NAME.csv") - 1))"Schedule it and declare the lookup
Add both stanzas underlocal/, then restart Splunk. The scripted input runs twice a day; a cron interval does not run at start-up, so run the script once by hand the first time.local/ inputs. confconf [script://./bin/fetch_ismalicious.sh] # The lists are rebuilt every 12 hours. A cron interval does not run at start-up. interval = 17 3,15 * * * index = <INDEX> sourcetype = ismalicious:fetch disabled = 0local/ transforms. confconf [ismalicious_ips] filename = ismalicious_ips.csv # A domain lookup built the same way (FIELD=domain): [ismalicious_domains] filename = ismalicious_domains.csv case_sensitive_match = falseMatch your events
Look up the address field of your firewall or proxy events. Lookups match exact values: normalize the field first.SearchSPL index=<FIREWALL_INDEX> sourcetype=<FIREWALL_SOURCETYPE> | lookup ismalicious_ips ip AS dest_ip OUTPUT ismalicious_list | where isnotnull(ismalicious_list)In Enterprise Security, add a Line Oriented source (optional)
- Serve ES a copy fetched on a host you control with the script below, which applies the same checks, over HTTPS, and give ES that URL with no credential.
- In ES 8: Configure › Threat intelligence › Data sources › + Data source › Line Oriented.
- Name
ismalicious_ips_critical, Typethreatlist_ip, URL of your copy, Interval43200, Delimiting regular expression,, Skip header lines0, and keep the default Ignoring regular expression, which skips#lines. - Add one source per list with the Fields below, then turn on the threat matching searches you need.
Copy for Enterprise Securitysh #!/bin/sh # Every 12 hours from cron, on a host ES reaches over HTTPS. # /etc/ismalicious/netrc (mode 600) holds the three netrc lines. set -eu NETRC=/etc/ismalicious/netrc DIR=/var/www/blocklists FILE=blocklist-ips-critical.txt # fetch_list LIST OUT: download one list to OUT and check it. # On any failure OUT is removed and the function returns 1. fetch_list() { if ! code=$(curl --silent --show-error --fail --netrc-file "$NETRC" \ --proto '=https' --max-time 300 --retry 2 \ --output "$2" --write-out '%{http_code}' "https://api.ismalicious.com/blocklist/download/$1"); then rm -f "$2"; echo "$1: download failed" >&2; return 1 fi if [ "$code" != 200 ]; then rm -f "$2"; echo "$1: HTTP $code" >&2; return 1 fi if ! head -n 1 "$2" | grep -q '^[#!] IsMalicious.com Blocklist'; then rm -f "$2"; echo "$1: not an isMalicious list" >&2; return 1 fi if grep -q 'Lite Version' "$2"; then rm -f "$2"; echo "$1: lite list received, check the API key and the plan" >&2 return 1 fi # Every entry ends with a newline, and one header counts them: refuse a # cut, doubled or empty file. if [ -n "$(tail -c 1 "$2")" ]; then rm -f "$2"; echo "$1: cut short, no final newline" >&2; return 1 fi if [ "$(grep -c '^[#!] Total entries:' "$2")" != 1 ]; then rm -f "$2"; echo "$1: not one Total entries line" >&2; return 1 fi total=$(sed -n 's/^[#!] Total entries: \([0-9,]*\)$/\1/p' "$2" | tr -d ,) got=$(grep -c '^[^#!]' "$2" || true) if [ -z "$total" ] || [ "$total" = 0 ] || [ "$got" != "$total" ]; then rm -f "$2"; echo "$1: $got entries, the header says ${total:-none}" >&2 return 1 fi } mkdir -p "$DIR" tmp=$(mktemp "$DIR/.$FILE.XXXXXX") trap 'rm -f "$tmp"' EXIT fetch_list "$FILE" "$tmp" chmod 644 "$tmp" mv -f "$tmp" "$DIR/$FILE" # ES source URL: https://<INTERNAL_HOST>/blocklists/blocklist-ips-critical.txtES Fields, per listSplunk ES # Line Oriented sources, one per list: the file, then its Fields.
- blocklist-ips-critical.txt
ip:$1,description:isMalicious_ips_critical - blocklist-ips-c2.txt
ip:$1,description:isMalicious_ips_c2 - blocklist-domains-c2.txt
domain:$1,description:isMalicious_domains_c2 - blocklist-domains-ransomware.txt
domain:$1,description:isMalicious_domains_ransomware
# URL and hash lists route to http_intel and file_intel. /blocklist/stats does
# not count them: read a file's Total entries line before you add it.
- blocklist-urls-critical.txt
url:$1,description:isMalicious_urls_critical - blocklist-hashes-malware.txt
file_hash:$1,description:isMalicious_hashes_malware
# Line Oriented sources, one per list: the file, then its Fields. blocklist-ips-critical.txt ip:$1,description:isMalicious_ips_critical blocklist-ips-c2.txt ip:$1,description:isMalicious_ips_c2 blocklist-domains-c2.txt domain:$1,description:isMalicious_domains_c2 blocklist-domains-ransomware.txt domain:$1,description:isMalicious_domains_ransomware # URL and hash lists route to http_intel and file_intel. /blocklist/stats does # not count them: read a file's Total entries line before you add it. blocklist-urls-critical.txt url:$1,description:isMalicious_urls_critical blocklist-hashes-malware.txt file_hash:$1,description:isMalicious_hashes_malwareOr, in ES 8.2 or later, add a TAXII 2 source (untested)
- ES 8.2 and later download TAXII 2.0 and 2.1 collections. For STIX documents, Splunk documents its STIX 2 parsing for observable objects such as
observed-dataand says the pattern syntax of indicator objects is not supported; this guide assumes the TAXII 2 source uses the same parser. Each isMalicious collection therefore has an observables twin, its id followed by-observables, which serves each indicator as anobserved-dataobject: give ES that one. - This source never removes an indicator that isMalicious retires: Splunk says TAXII 2 feeds do not use the Maximum age setting, and the observables collection carries no retirements. The lists do remove it, because each download is a full snapshot: the lookup at its next rebuild, ES’s Line Oriented sources once their Maximum age has passed. Prefer them.
- In Configure › General settings › Credentials, select New credential: the API key as Username, the API secret as Password, and the app context, for example Enterprise Security.
- In Configure › Threat intelligence › Data sources, select + Data source, enter a Name, a Description and the collection URL below, keep Threat Intelligence checked, and select the credential on the Advanced tab.
- The POST argument
earliestsets how far back the first download reads; later downloads start from the previous run. - After the first download, Analytics › Audit › Threat intelligence audit shows the source; count its records in
ip_intelbefore relying on it. This path has not been tested in ES itself.
TAXII 2 sourceSplunk ES - Collection URL
https:// api. ismalicious. com/ taxii/ api-root/ collections/ c2-indicators-observables/ - POST arguments
earliest="-30d"
Collection URL .. https://api.ismalicious.com/taxii/api-root/collections/c2-indicators-observables/ POST arguments .. earliest="-30d"- ES 8.2 and later download TAXII 2.0 and 2.1 collections. For STIX documents, Splunk documents its STIX 2 parsing for observable objects such as
Push IOCs through HEC (optional, Pro or Enterprise)
- In Splunk, Settings › Add Data › Monitor › HTTP Event Collector: name
ismalicious, select the allowed index, Review, Submit, and copy the token; in Global Settings, set All Tokens to Enabled. - HEC must present a certificate from a public CA: Splunk’s default HEC certificate is self-signed and is refused, so set
serverCertin the[http]stanza of inputs.conf, or put HEC behind a load balancer that has one. - In
/app/integrations/splunk, enter the HEC URL (https://, port 8088, path/services/collector), the HEC token and the Index. - Click Test, then Sync now: each sync sends up to 1,000 IOCs (100 by default) from where the last one stopped, and SOC events go out within five minutes.
- In Splunk, Settings › Add Data › Monitor › HTTP Event Collector: name
Verify it works
|matches the list’sinputlookup ismalicious_ips | stats count countin /blocklist/stats, give or take one rebuild.- The
ismalicious:fetchevents show each run’s status and row count.status=litemeans the key did not get the full list, and the previous lookup was kept. - In ES, Analytics › Audit › Threat intelligence audit shows the source as downloaded, and the
ip_intelcount for itsthreat_keymatches the list. - After a sync,
sourcetype="ismalicious:ioc"holds the IOCs, and the integration page lists each delivery.
| inputlookup ismalicious_ips | stats count
index=<INDEX> sourcetype=ismalicious:fetch
| table _time status list rows
| inputlookup ip_intel | search threat_key=ismalicious_ips_critical | stats count
index=<INDEX> sourcetype="ismalicious:ioc" | stats count by event.entityTypeTroubleshooting
The fetch fails with a 401
The key or the secret in the netrc file is wrong or incomplete: the script logs status=refused, splunkd.log shows “download failed”, and the previous lookup stays. The response body reads “Blocklist not found or empty”; trust the status code. Copy both values again from Account › API access.
“lite list received” in splunkd.log
The plan is Free or lapsed: past due, unpaid, canceled, incomplete or paused counts as Free. The script logs status=refused and keeps the previous lookup until the key gets the full list.
“entries, the header says”, “cut short, no final newline” or “not one Total entries line” in splunkd.log
The file was empty, cut short or not one of the lists, as a proxy or captive portal answers. The script keeps the previous lookup; the next run tries again.
A timeout or a 502
A download that takes longer than 30 seconds on our side ends in a timeout or a 502: use a tier or category list from the table.
503 Service Unavailable
The list is being built for the first time, and the response carries Retry-After: 600. The script fails, keeps the previous lookup, and the next run picks the list up.
Header lines in the lookup or in ES
A lookup cannot hold the # header, which is why the script drops those lines. ES skips them with its default Ignoring regular expression: restore it if you changed it.
A listed value does not match
Lookups match exact values, and case-sensitively by default. Normalize the event field, and set case_sensitive_match = false on a domain lookup.
Certificate errors
curl checks the system CA store. Update it, check the clock, and exempt api.ismalicious.com from TLS inspection.
The HEC destination is refused or deliveries fail
The HEC URL must be a public HTTPS address with a certificate from a public CA: private and loopback addresses are refused when you save, and Splunk’s self-signed default certificate fails every delivery. Each delivery must be answered within 5 seconds, and the token must allow the index you entered.
Limits
- The TAXII 2 path does not remove what isMalicious retires: Splunk says TAXII 2 feeds do not use the Maximum age setting, and the observables collection carries no retirements, so a false positive cleaned from isMalicious stays in ES. The lists remove it, since each download is a full snapshot: prefer them. The path has not been tested in ES either. For STIX documents, Splunk documents parsing observables, not indicator patterns, which is why it takes a collection’s
-observablestwin; how the TAXII 2 source sends the credential is not documented, and which observables reach which threat intelligence collection is set in ES’s collections.conf. - The HEC push is not a feed: it runs when you click Sync now, sends at most 1,000 IOCs per sync, and nothing schedules it.
- ES’s own credential option for URL sources is not covered here: give ES an internal copy.
- No CSV, JSON, CIDR or wildcard files are served. The script builds the CSV lookup Splunk needs, and Splunk’s CIDR and WILDCARD match types have nothing to match in the lists.
- On Splunk Cloud Platform you cannot edit
.conffiles directly: packagebin/,local/andlookups/as a private app and install it after AppInspect vetting (Victoria Experience; not tested for this guide), upload the lookup by hand under Settings › Lookups, or use the HEC push. - Search head clusters are not covered: deploy the app from the deployer rather than writing lookups on one member.
- URL and hash lists exist with the same names, but /blocklist/stats does not count them: read a file’s
Total entriesline before building on it. - The 10% sample is the first tenth of an unsorted file, not the riskiest tenth.
Questions
Can Splunk Enterprise Security use the isMalicious TAXII feed?
Not tested. ES 8.2 and later download TAXII 2.0 and 2.1 collections, and for STIX documents Splunk documents parsing observed-data objects, not the patterns of indicator objects. Each isMalicious collection has an observables twin, its id followed by -observables, that serves observed-data: that is the collection to give ES, as in the TAXII 2 step. That source never removes an indicator isMalicious retires. The lists, loaded as Line Oriented sources, do, and work on every version: prefer them.
Does this work on Splunk Cloud Platform?
Not as written: Splunk Cloud Platform does not let you edit .conf files directly. A private app holding the script, its inputs and the lookup can run there after AppInspect vetting (Victoria Experience; not tested for this guide); otherwise upload the lookup by hand, or use the HEC push from the dashboard, on a Pro or Enterprise plan, which sends up to 1,000 IOCs per sync.
How often does the lookup refresh?
Twice a day, on the cron interval in inputs.conf. The lists are rebuilt every 12 hours, so a more frequent run downloads the same file. A cron interval does not run at start-up.
What happens with a free API key?
The download is the 10% sample, and the script refuses it: it logs status=refused and keeps the previous lookup. The full list needs a Basic, Pro, or Enterprise plan.
Why does a listed domain not match my events?
Lookups match exact values and are case-sensitive by default. Set case_sensitive_match = false on the domain lookup, and normalize the event field before the lookup.
Related
TAXII 2.1 collections for indicator match rules
Threat IPs and domains as CDB lists for Wazuh rules
Threat IPs and domains as Suricata datasets
Threat IPs and domains as QRadar reference sets
TAXII 2.1 indicators into Sentinel
Every list, level and category
TAXII 2.1 collections for SIEMs and TIPs
Authentication, endpoints and limits
Check a batch of indicators in one call
Get Started
Ready to get started?
No credit card required · Free API key