What You Will Achieve
By the end of this playbook you will have:
- A Python script that pulls malicious IPs from isMalicious and formats them for your firewall
- iptables, pfSense, Palo Alto, and cloud implementation examples
- A cron schedule that keeps blocklists fresh every 6 hours
- Change detection so only new IPs are added (no duplicate rules)
Prerequisites
| Requirement | Details |
|---|---|
| isMalicious API key | Free tier gives 250 requests/month; Pro for the TAXII export |
| Python 3.9+ | Runs the sync script |
| Root/admin access | Required to modify firewall rules |
Step 1: Fetch Malicious IPs from isMalicious
The STIX/TAXII feed (Pro) is the cleanest way to pull IOCs in bulk. On the Free plan, check the addresses you already see (firewall logs, alerts) with the bulk check endpoint instead.
Using the TAXII feed (Pro)
import base64
import requests
TAXII_URL = "https://api.ismalicious.com/taxii/api-root/collections/malicious-ips/objects"
API_KEY = "YOUR_API_KEY"
API_SECRET = "YOUR_API_SECRET"
HDR = {"X-API-KEY": base64.b64encode(f"{API_KEY}:{API_SECRET}".encode()).decode()}
def fetch_malicious_ips():
resp = requests.get(
TAXII_URL,
params={"limit": 1000},
headers=HDR
)
resp.raise_for_status()
bundle = resp.json()
ips = []
for obj in bundle.get("objects", []):
if obj.get("type") == "indicator":
pattern = obj.get("pattern", "")
# Extract IPv4 from STIX pattern: [ipv4-addr:value = '1.2.3.4']
if "ipv4-addr:value" in pattern:
ip = pattern.split("'")[1]
ips.append(ip)
return ips
Step 2: iptables Integration
blocklist_update.sh
#!/bin/bash
set -euo pipefail
CHAIN="ISMALICIOUS_BLOCK"
BLOCKLIST="/tmp/ismalicious_ips.txt"
# Fetch fresh blocklist
python3 /opt/ismalicious/fetch_ips.py > "$BLOCKLIST"
# Create chain if it doesn't exist
iptables -N "$CHAIN" 2>/dev/null || true
iptables -F "$CHAIN"
# Re-add all rules
while IFS= read -r ip; do
[[ -z "$ip" ]] && continue
iptables -A "$CHAIN" -s "$ip" -j DROP
iptables -A "$CHAIN" -d "$ip" -j DROP
done < "$BLOCKLIST"
# Ensure chain is referenced from INPUT/OUTPUT/FORWARD
iptables -C INPUT -j "$CHAIN" 2>/dev/null || iptables -I INPUT 1 -j "$CHAIN"
iptables -C OUTPUT -j "$CHAIN" 2>/dev/null || iptables -I OUTPUT 1 -j "$CHAIN"
iptables -C FORWARD -j "$CHAIN" 2>/dev/null || iptables -I FORWARD 1 -j "$CHAIN"
echo "$(date): Updated $(wc -l < "$BLOCKLIST") blocked IPs"
Add to cron
# Every 6 hours
0 */6 * * * /opt/ismalicious/blocklist_update.sh >> /var/log/ismalicious_blocklist.log 2>&1
Step 3: pfSense with pfBlockerNG
pfBlockerNG cannot send the X-API-KEY header. Use a cron job on the firewall (or a jump host) to download with curl and Base64 credentials, then point pfBlockerNG at the local file (see blocklist guide):
curl -fsS -H "X-API-KEY: $(printf '%s' 'KEY:SECRET' | base64 | tr -d '\n')" \
"https://api.ismalicious.com/blocklist/download/blocklist-ips-all.txt" \
-o /var/db/pfblockerng/ismalicious-ips.txt
Step 4: AWS Security Group Automation
import boto3
from fetch_ips import fetch_malicious_ips
ec2 = boto3.client("ec2", region_name="us-east-1")
SG_ID = "sg-XXXXXXXXXXXXXXXXX"
def update_security_group():
ips = fetch_malicious_ips()
# Remove all existing deny rules
sg = ec2.describe_security_groups(GroupIds=[SG_ID])["SecurityGroups"][0]
if sg.get("IpPermissionsEgress"):
ec2.revoke_security_group_egress(
GroupId=SG_ID,
IpPermissions=sg["IpPermissionsEgress"]
)
# Add new deny rules in batches of 50 (AWS limit)
ip_ranges = [{"CidrIp": f"{ip}/32", "Description": "isMalicious block"} for ip in ips[:50]]
if ip_ranges:
ec2.authorize_security_group_egress(
GroupId=SG_ID,
IpPermissions=[{
"IpProtocol": "-1",
"IpRanges": ip_ranges
}]
)
print(f"Updated {len(ip_ranges)} blocked IPs in {SG_ID}")
if __name__ == "__main__":
update_security_group()
Step 5: Palo Alto Networks
PAN-OS EDL URLs cannot send X-API-KEY. Host the list yourself: cron downloads blocklist-ips-all.txt (or critical) via curl + Base64 credentials to an internal HTTPS server or object storage, then point the EDL at that URL.
Validation
After deploying, verify a known malicious IP is blocked:
# Test connectivity to a known malicious IP (should timeout/be blocked)
curl --connect-timeout 3 http://185.220.101.1/ || echo "Blocked successfully"
# Check iptables hit count
iptables -L ISMALICIOUS_BLOCK -v -n | head -20
Monitoring & Alerting
Log every blocked connection for SOC review:
# Log instead of silently drop (for SIEM ingestion)
iptables -A ISMALICIOUS_BLOCK -s "$ip" -j LOG --log-prefix "ISMALICIOUS_BLOCKED: "
iptables -A ISMALICIOUS_BLOCK -s "$ip" -j DROP
Route these logs to your SIEM and create an alert rule for high-frequency hits (potential active attack).
Related resources
- Free threat report — spot-check IPs before adding to blocklists
- API documentation — blocklist download endpoints and formats
- IP and domain blocklists — the lists, their categories and formats
- OpenCTI integration — STIX/TAXII into your TIP
- Case study: 600K IP hourly refresh — anonymized OpenCTI + firewall pipeline