AMFeedlyCustomerScripts/Feedly_Threat_Actor_IOC_Extractor
GitHub: AMFeedlyCustomerScripts/Feedly_Threat_Actor_IOC_Extractor
Stars: 0 | Forks: 0
# Feedly Threat Actor IOC Extractor
Extract Indicators of Compromise (IPs, domains, file hashes, URLs) associated with any threat actor tracked by Feedly Threat Intelligence — directly from the command line.
## Overview
This tool implements a repeatable two-step workflow from the Feedly Threat Actor Insights card to a full IOC list:
1. **Resolve** — search for a threat actor by name (e.g. `"Lazarus Group"`, `"APT28"`) to retrieve its Feedly NLP entity ID via the autocomplete endpoint.
2. **Extract** — call the threat actor relationships endpoint to pull all associated IOC entities and classify them by type.
### Use Cases
- **SIEM / SOAR ingestion** — feed IOCs into Splunk, Sentinel, or XSOAR in JSON or CSV
- **Firewall / EDR blocklists** — export IP and domain lists for policy updates
- **Threat hunt preparation** — pull hashes before a hunt to pre-populate detection tools
- **Incident response** — quickly enumerate all known IOCs for an actor during an active incident
- **Scheduled automation** — run on a cron/schedule against a fixed actor ID for continuous refresh
## Requirements
- Python 3.8+
- Feedly Enterprise API token
- `requests` library (`pip install requests`)
- `PyYAML` library for config file support (`pip install pyyaml`) — optional
## Installation
# 1. Clone the repository
git clone https://github.com/AMFeedlyCustomerScripts/Feedly_Threat_Actor_IOC_Extractor.git
cd Feedly_Threat_Actor_IOC_Extractor
# 2. Install dependencies
pip install -r requirements.txt
# 3. Configure credentials (choose one method)
### Credential Options
**Option A — Environment variable (recommended for automation)**
export FEEDLY_API_KEY="your_feedly_api_token"
**Option B — .env file**
echo 'FEEDLY_API_KEY="your_feedly_api_token"' > .env
**Option C — Config file**
cp config.yaml.template config.yaml
# Edit config.yaml and set feedly.api_token
## Usage
### Basic — search by name
python feedly_threat_actor_ioc.py --actor "Lazarus Group"
The script resolves the name to an entity ID interactively if multiple actors match, then fetches all associated IOCs across all time.
### Skip name resolution — use entity ID directly
python feedly_threat_actor_ioc.py \
--actor-id "nlp/f/entity/gz:ta:68391641-859f-4a9a-9a1e-3e5cf71ec376"
Useful in automation where the entity ID is already known (e.g. stored from a prior `--dry-run`).
### Restrict the time window
python feedly_threat_actor_ioc.py --actor "APT28" --interval LAST_30_DAYS
### Filter by IOC type
# IPs and domains only
python feedly_threat_actor_ioc.py --actor "Lazarus" --type ip,domain
# Hashes only
python feedly_threat_actor_ioc.py --actor "Lazarus" --type hash
### Save output to a file
# Plain text — grouped by type, one value per line
python feedly_threat_actor_ioc.py --actor "Lazarus" --output lazarus_iocs.txt --format txt
# JSON — full metadata per IOC
python feedly_threat_actor_ioc.py --actor "Lazarus" --output lazarus_iocs.json --format json
# CSV — one row per IOC, ready for Excel or SIEM import
python feedly_threat_actor_ioc.py --actor "Lazarus" --output lazarus_iocs.csv --format csv
### Dry run — resolve name only, skip IOC fetch
python feedly_threat_actor_ioc.py --actor "Sandworm" --dry-run
Prints the entity ID without making the relationships call. Useful for pre-validating actor names in scripts.
### Use a config file
cp config.yaml.template config.yaml
# Edit config.yaml
python feedly_threat_actor_ioc.py --actor "Lazarus" --config config.yaml
### Debug the raw API response
python feedly_threat_actor_ioc.py --actor "APT29" --dump-raw --verbose
Prints the full JSON response from the relationships endpoint to stderr — helpful when first running against a new actor or API version.
## Command-Line Reference
| Option | Short | Description |
|--------|-------|-------------|
| `--actor NAME` | `-a` | Threat actor name to search (mutually exclusive with `--actor-id`) |
| `--actor-id ID` | `-i` | Skip name resolution; use this entity ID directly |
| `--interval WINDOW` | | Time window: `LAST_7_DAYS`, `LAST_30_DAYS`, `LAST_3_MONTHS`, `LAST_6_MONTHS`, `LAST_1_YEAR`, `FOREVER` (default: `FOREVER`) |
| `--type TYPES` | `-t` | Comma-separated filter: `ip`, `domain`, `hash`, `url`, `email`, `other`, `all` (default: `all`) |
| `--format FORMAT` | `-f` | Output format: `txt`, `json`, `csv` (default: `txt`) |
| `--output FILE` | `-o` | Write to file instead of stdout |
| `--config FILE` | `-c` | Path to YAML config file (default: `config.yaml` in current/script dir) |
| `--api-key KEY` | | Override API key from environment / config |
| `--search-count N` | | Max actor candidates returned in the search step (default: `10`) |
| `--dry-run` | | Resolve actor name and print entity ID only |
| `--dump-raw` | | Print raw relationships JSON to stderr |
| `--verbose` | `-v` | Enable debug output |
## Output Formats
### Text (`--format txt`)
Plain-text, grouped by IOC type. One value per line — easy to paste into tickets or blocklist configs.
# Feedly Threat Actor IOC Export
# Actor : Lazarus Group
# ID : nlp/f/entity/gz:ta:68391641-859f-4a9a-9a1e-3e5cf71ec376
# Window : FOREVER
# Date : 2026-05-29T14:30:00Z
# Total : 42 IOC(s)
## DOMAIN (18)
cdn-apple.com
download-node.com
...
## HASH (14)
3a4b5c6d7e8f...
...
## IP (10)
185.220.101.45
...
### JSON (`--format json`)
Full metadata including article mention count and Feedly entity ID per IOC.
{
"meta": {
"actor_name": "Lazarus Group",
"actor_id": "nlp/f/entity/gz:ta:68391641-859f-4a9a-9a1e-3e5cf71ec376",
"interval": "FOREVER",
"exported_at": "2026-05-29T14:30:00+00:00",
"total_iocs": 42
},
"iocs": [
{
"value": "185.220.101.45",
"ioc_type": "ip",
"ioc_group": "ip",
"article_count": 7,
"entity_id": "nlp/f/entity/ioc:..."
},
{
"value": "cdn-apple.com",
"ioc_type": "domain",
"ioc_group": "domain",
"article_count": 3,
"entity_id": "nlp/f/entity/ioc:..."
}
]
}
### CSV (`--format csv`)
One row per IOC with actor context columns — ready for Excel, Splunk lookup tables, or SIEM ingestion.
value,ioc_type,ioc_group,article_count,entity_id,actor_name,actor_id,interval
185.220.101.45,ip,ip,7,nlp/f/entity/ioc:...,Lazarus Group,nlp/f/entity/gz:ta:...,FOREVER
cdn-apple.com,domain,domain,3,nlp/f/entity/ioc:...,Lazarus Group,nlp/f/entity/gz:ta:...,FOREVER
## IOC Type Classification
The script classifies each IOC value automatically using regex:
| Group | Fine-grained types | Examples |
|-------|--------------------|---------|
| `ip` | `ip` (IPv4/CIDR), `ipv6` | `185.220.101.45`, `2001:db8::1` |
| `domain` | `domain` | `cdn-apple.com`, `update-service.net` |
| `hash` | `hash_md5`, `hash_sha1`, `hash_sha256`, `hash_sha512` | 32/40/64/128 hex chars |
| `url` | `url` | `https://malicious.example/payload` |
| `email` | `email` | `attacker@protonmail.com` |
| `other` | `unknown` | Anything that does not match above |
Defanged IOCs (e.g. `hxxps://`, `[.]`) are normalised before classification.
## Configuration File
Copy `config.yaml.template` to `config.yaml` and edit as needed:
feedly:
api_token: "YOUR_FEEDLY_API_TOKEN_HERE"
defaults:
interval: "FOREVER" # Default time window
format: "json" # Default output format
ioc_types: "all" # Default IOC type filter
output_dir: "./output" # Auto-save timestamped files here (optional)
CLI flags always override config file values.
## Automation Example
#!/bin/bash
# Daily IOC refresh for a known actor
export FEEDLY_API_KEY="..."
python feedly_threat_actor_ioc.py \
--actor-id "nlp/f/entity/gz:ta:68391641-859f-4a9a-9a1e-3e5cf71ec376" \
--interval LAST_7_DAYS \
--type ip,domain \
--format csv \
--output /var/threat-intel/lazarus_$(date +%Y%m%d).csv
## Troubleshooting
**No IOCs returned**
The relationships endpoint surfaces IOC entities that Feedly has explicitly linked to the actor. If the result is empty:
- Try `--interval FOREVER` to widen the time window.
- Use `--dump-raw --verbose` to inspect the raw response structure.
- Verify the entity ID with `--dry-run` — a wrong ID returns 404.
- Some actors may have relationships classified as malware/TTP only, with no linked IOC entities.
**Authentication error**
Verify your token is set: `echo $FEEDLY_API_KEY`. Tokens can be generated at `https://feedly.com/v3/auth/dev`.
**Multiple actors matched**
The search returns up to `--search-count` (default 10) candidates. Use the interactive menu to pick the correct one, or use `--actor-id` to bypass search entirely once you know the entity ID.
## API Endpoints Used
| Step | Method | Endpoint |
|------|--------|----------|
| Name resolution | `GET` | `/v3/search/entities?query={name}&count={n}` |
| IOC extraction | `GET` | `/v3/ml/relationships/actor/{id}?intervalType={window}` |
## License
© 2025 Feedly, Inc. All rights reserved. See the script header for the full disclaimer.