A cross-platform, multi-source threat intelligence checker for SOC and IR workflows.
Accepts IP addresses, domains, URLs, and SHA256 file hashes as input.
Aggregates results from multiple free threat intelligence sources and outputs
Splunk-compatible key=value events to stdout, one event per line.
Built as a demonstration of SOC automation engineering: strict input validation, concurrent API lookups, OS-level enrichment, graceful error handling, and SIEM-ready structured output.
- Features
- Supported Sources
- Minimum Requirements
- Installation
- Configuration
- Usage: Python
- Usage: PowerShell
- Output Format
- Splunk Integration
- Use Cases
- Expected Outcomes
- Notes and Limitations
- Accepts IPs (v4 and v6), domains, full URLs, and SHA256 hashes
- Strict regex-based input validation. Private/reserved IPs are automatically skipped
- Concurrent API lookups (up to 5 parallel threads, Python only) to minimise runtime on large lists
- OS-level DNS and WHOIS enrichment using native tools (no API key required)
- Two run modes: fully automated (CLI flags or piped input) and interactive menu
- All events written to stdout. All diagnostic messages written to stderr
- One event per line; safe to redirect, pipe, or tail into any SIEM
- Graceful degradation: Sources with missing keys are skipped silently. Network failures produce error events
- Zero paid dependencies
| Source | Indicator Types | API Key Required |
|---|---|---|
| VirusTotal v3 | IP, domain, URL, SHA256 | Yes (free tier) |
| AbuseIPDB v2 | IP | Yes (free tier) |
| URLScan.io | URL, domain | Yes (free tier) |
| Shodan InternetDB | IP | No |
| IPinfo.io | IP | No (basic fields) |
| GreyNoise Community | IP | No |
| CIRCL hashlookup | SHA256 | No |
| OS: dig / nslookup | Domain, URL | No |
| OS: whois | IP, domain | No |
Free API key registration links:
- VirusTotal: https://www.virustotal.com/gui/join-us
- AbuseIPDB: https://www.abuseipdb.com/register
- URLScan.io: https://urlscan.io/user/signup
The scripts are fully functional without any API keys. Sources that require keys are skipped automatically when keys are absent, and all keyless sources still run.
| Requirement | Minimum Version |
|---|---|
| Python | 3.10 |
requests library |
2.28 |
| OS | Linux, FreeBSD, Windows |
| Network | Outbound HTTPS (port 443) |
| Optional OS tools | dig, whois, nslookup |
Install the only third-party dependency:
pip install requests
| Requirement | Minimum Version |
|---|---|
| PowerShell | 5.1 (Windows PowerShell or PowerShell 7+) |
| .NET | 4.5 (Windows) / 6.0 (Linux, FreeBSD) |
| OS | Linux, FreeBSD, Windows |
| Network | Outbound HTTPS (port 443) |
| Optional OS tools | dig, whois, nslookup |
PowerShell 5.1 is built into Windows 10 and Windows Server 2016 and later. PowerShell 7 installation: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell
No PowerShell modules beyond the standard library are required.
git clone https://github.com/eddietorial/ThreatIntelChecker.git
cd ThreatIntelChecker
pip install requests # Python onlyAPI keys are loaded from threat_intel.conf (INI format) in the same directory
as the script, then overridden by environment variables if present.
Environment variables always take precedence.
[API_KEYS]
VT_API_KEY = your_virustotal_key_here
ABUSEIPDB_API_KEY = your_abuseipdb_key_here
URLSCAN_API_KEY = your_urlscan_key_here# Linux / FreeBSD / macOS
export VT_API_KEY="your_virustotal_key"
export ABUSEIPDB_API_KEY="your_abuseipdb_key"
export URLSCAN_API_KEY="your_urlscan_key"# PowerShell (any platform)
$env:VT_API_KEY = "your_virustotal_key"
$env:ABUSEIPDB_API_KEY = "your_abuseipdb_key"
$env:URLSCAN_API_KEY = "your_urlscan_key"# Single indicator
python threat_intel_checker.py -i 203.0.113.5
# Multiple indicators
python threat_intel_checker.py -i 203.0.113.5 evil.example.com https://bad.site/payload
# From a file (one indicator per line; # = comment)
python threat_intel_checker.py -f iocs.txt
# Redirect output to a log file
python threat_intel_checker.py -f iocs.txt > events.log 2>errors.log
# Custom config path
python threat_intel_checker.py -i 1.2.3.4 --config /etc/soc/threat_intel.confpython threat_intel_checker.py
python threat_intel_checker.py --menupython threat_intel_checker.py --help
# Comments are ignored
203.0.113.5
evil.example.com
https://malicious.site/payload.exe
a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
# Single indicator
.\Invoke-ThreatIntelCheck.ps1 -Indicators 203.0.113.5
# Multiple indicators
.\Invoke-ThreatIntelCheck.ps1 -Indicators 203.0.113.5, evil.example.com
# From a file
.\Invoke-ThreatIntelCheck.ps1 -File .\iocs.txt
# Redirect output to a log file
.\Invoke-ThreatIntelCheck.ps1 -File .\iocs.txt | Out-File events.log
# Custom config path
.\Invoke-ThreatIntelCheck.ps1 -Indicators 1.2.3.4 -Config C:\SOC\threat_intel.conf
# Interactive menu
.\Invoke-ThreatIntelCheck.ps1 -Menu
.\Invoke-ThreatIntelCheck.ps1 # defaults to menu when no args are givenOn Linux and FreeBSD, invoke with pwsh:
pwsh Invoke-ThreatIntelCheck.ps1 -Indicators 203.0.113.5
pwsh Invoke-ThreatIntelCheck.ps1 -File iocs.txtEach source check produces one event on stdout as a flat key=value string.
Values containing spaces, commas, or equals signs are double-quoted.
All times are UTC.
Example output for a single IP address:
timestamp=2026-04-16T14:32:01Z host=analyst-ws app=ThreatIntelChecker version=1.1.0 indicator_type=ip indicator=203.0.113.5 source=os_dns verdict=unknown dns_resolved=203.0.113.5
timestamp=2026-04-16T14:32:02Z host=analyst-ws app=ThreatIntelChecker version=1.1.0 indicator_type=ip indicator=203.0.113.5 source=virustotal verdict=malicious vt_malicious=12 vt_suspicious=2 vt_harmless=60 vt_undetected=5 vt_country=CN vt_asn=12345 vt_as_owner="Example ISP Ltd" vt_reputation=-5
timestamp=2026-04-16T14:32:02Z host=analyst-ws app=ThreatIntelChecker version=1.1.0 indicator_type=ip indicator=203.0.113.5 source=abuseipdb verdict=malicious abuseipdb_score=98 abuseipdb_total_reports=47 abuseipdb_country=CN abuseipdb_isp="Example ISP" abuseipdb_usage_type=datacenter
timestamp=2026-04-16T14:32:03Z host=analyst-ws app=ThreatIntelChecker version=1.1.0 indicator_type=ip indicator=203.0.113.5 source=shodan_internetdb verdict=suspicious shodan_open_ports="22,80,443" shodan_vulns="CVE-2021-44228"
timestamp=2026-04-16T14:32:03Z host=analyst-ws app=ThreatIntelChecker version=1.1.0 indicator_type=ip indicator=203.0.113.5 source=greynoise verdict=malicious gn_noise=True gn_riot=False gn_classification=malicious gn_name="Unknown" gn_last_seen=2026-04-15
Example output for a SHA256 hash:
timestamp=2026-04-16T14:33:01Z host=analyst-ws app=ThreatIntelChecker version=1.1.0 indicator_type=sha256 indicator=41fb6c... source=virustotal verdict=malicious vt_malicious=13 vt_file_type=Android vt_file_name="rto challan.apk"
timestamp=2026-04-16T14:33:02Z host=analyst-ws app=ThreatIntelChecker version=1.1.0 indicator_type=sha256 indicator=41fb6c... source=circl_hashlookup verdict=unknown result=not_in_known_clean_db note=not_catalogued_as_clean_software
| Verdict | Meaning |
|---|---|
malicious |
One or more sources confirmed malicious activity |
suspicious |
Elevated risk signal, not fully confirmed (e.g. active scanner, open CVEs) |
clean |
Source returned no findings or confirmed the file is known-clean software |
unknown |
Source returned data but no verdict could be inferred |
skip |
Indicator was valid but excluded (e.g. private IP) |
error |
A lookup failed due to a network error or API issue |
CIRCL hashlookup indexes known clean software from the NIST NSRL and similar
datasets. A verdict=clean from this source means the file is a catalogued,
known-legitimate application. A verdict=unknown with result=not_in_known_clean_db
means the file is not recognised as clean software; combined with a VirusTotal
verdict=malicious, this is strong corroboration.
Redirect output to a monitored log file and configure a Splunk Universal Forwarder or file monitor input to ingest it.
python threat_intel_checker.py -f iocs.txt >> /var/log/threat_intel/events.loginputs.conf on the forwarder:
[monitor:///var/log/threat_intel/events.log]
index = threat_intel
sourcetype = threat_intel_kvprops.conf:
[threat_intel_kv]
KV_MODE = auto
TIME_FORMAT = %Y-%m-%dT%H:%M:%SZ
TIME_PREFIX = timestamp=python threat_intel_checker.py -f iocs.txt | \
while IFS= read -r line; do
curl -s -X POST "https://splunk-hec:8088/services/collector/event" \
-H "Authorization: Splunk YOUR_HEC_TOKEN" \
-d "{\"event\": \"$line\", \"sourcetype\": \"threat_intel_kv\"}"
done# Linux cron: run every 15 minutes against a dynamic IOC list
*/15 * * * * /usr/bin/python3 /opt/soc/threat_intel_checker.py \
-f /opt/soc/iocs.txt >> /var/log/threat_intel/events.log 2>/var/log/threat_intel/errors.logPhishing triage. Paste the sender IP, the domain in the From header, and any URLs from the email body. The script checks all concurrently and surfaces AbuseIPDB history, VirusTotal community verdicts, URLScan metadata, and GreyNoise internet-scanner classification.
Malware analysis support. Feed a SHA256 hash of a suspicious file to get VirusTotal engine results and a CIRCL hashlookup clean-software check. If VT flags the file as malicious and CIRCL confirms it is not catalogued as clean software, you have cross-source corroboration.
Threat hunt enrichment. Export a list of IPs from a SIEM query, run them
through the script, and pipe the structured output back into the SIEM as
enrichment events. The indicator field links them to your original events.
Incident response. During an active incident, drop IOCs into a file and run a single command to enumerate reputation across all sources. Output is immediately grep-able and can be shared with stakeholders as a log file.
Detection engineering. Use output as a labelled dataset to train or validate
ML-based detection models. Each event includes source-level confidence signals
(vt_malicious, abuseipdb_score, gn_classification) that can serve
as labels.
SOC playbook automation. Embed either script in a SOAR playbook action or a shell step in a CI/CD security gate to block deployments referencing known malicious infrastructure.
Given a list of indicators, you can expect:
- One or more
key=valueevents per indicator per source - Concurrent lookups (Python) completing a 50-indicator file in under 60 seconds on a standard internet connection (subject to API rate limits on free tiers)
verdict=maliciousevents wherever a source returns confirmed findingsverdict=cleanevents confirming a source checked and found nothingverdict=errorevents for any source that was unreachable, with anerrorkey describing the failure, allowing selective retry- Private, loopback, and reserved IPs rejected at validation with
verdict=skip - Malformed or unrecognised strings rejected at validation with
verdict=error
Rate limits. Free tiers on VirusTotal (4 requests/minute), AbuseIPDB (1,000 requests/day), and URLScan.io (100 searches/hour) apply. The script does not throttle automatically. For large-scale pipelines, add sleep intervals between batches or upgrade to a paid tier.
VirusTotal URL lookups. The script looks up an existing scan rather than
submitting a new one. If a URL has never been submitted to VirusTotal, the
result will be not_found.
GreyNoise coverage. GreyNoise observes internet-wide scanning activity.
IPs that have never generated background noise (e.g. targeted attackers that
probe specific hosts) will return result=not_observed, which does not mean
they are clean.
CIRCL hashlookup scope. This source only covers known clean software
catalogued by NIST NSRL and partner datasets. It does not catalogue malware.
A not_in_known_clean_db result is expected for any file that is not a
mainstream commercial or open-source application.
whois availability. The whois tool must be installed separately on some
systems. On Debian/Ubuntu: apt install whois. On FreeBSD: pkg install whois.
On Windows: https://docs.microsoft.com/en-us/sysinternals/downloads/whois.
If whois is absent, OS-level enrichment events for that indicator are omitted.
IPv6. IPv6 indicators are supported for validation and routing, but not all sources support IPv6 lookups on free tiers (notably AbuseIPDB and Shodan InternetDB).
PowerShell sequential execution. The PowerShell script runs checks
sequentially to maintain compatibility with PS 5.1 interactive hosts.
ForEach-Object -Parallel introduced in PS 7 conflicts with the stdin handle
required by interactive menus. For large batch jobs, redirect stdout to a file:
.\Invoke-ThreatIntelCheck.ps1 -File iocs.txt | Out-File events.log
Sensitive data. API keys are stored in plaintext in the config file. Use environment variables and a secrets manager in production environments.