@pipeworx/threatfox
Connect: https://gateway.pipeworx.io/threatfox/mcp · Install: one-click buttons
No MCP client? Skip the connection: POST https://gateway.pipeworx.io/v1/tools/search_packs {"query":"..."} to find a tool below, GET /v1/tools/<name> for its schema, POST the same URL with arguments for the data — see For AI agents.
Tools: 4
ThreatFox (abuse.ch) MCP — community IOC feed.
Tools
search_ioc(indicator, exact_match?)recent_iocs(days?)search_hash(hash)— see below, it asks TWO upstream queriessearch_malware(malware, limit?)
search_hash asks both hash relations
ThreatFox stores hashes in two unrelated places and abuse.ch’s own
query: "search_hash" only searches one of them:
| relation | what it means | upstream query |
|---|---|---|
sample_to_c2 | the hash is a malware SAMPLE; rows are the C&C servers it talks to | search_hash |
hash_listed_as_ioc | the hash is itself published as an indicator (threat_type: "payload") | search_ioc |
Asking only the first made this tool 34-for-34 empty over the 30d to
2026-09-16. It was never a broken endpoint — abuse.ch answers illegl_hash
(their typo) for a malformed hash and no_result for a well-formed one, so our
request was parsed and accepted throughout. It was the wrong question:
da168c3ff95c749beec0a2f29a1e6b82, taken from this pack’s own recent_iocs,
returns nothing from search_hash and a full row from search_ioc.
The tool now tries both and returns matched_relation saying which answered
(null when neither did, alongside searched_relations). The second call only
runs when the first found nothing, so a non-empty answer still costs one
request. Do not collapse the two into one unlabelled list: “this file phones
home to that C2” and “this file is itself flagged as a payload” are different
security facts.
Empty is a real answer here. ThreatFox tracks C&C infrastructure, not
samples — most hashes are in neither relation. Measured 2026-09-16: 7 of 40
recent MalwareBazaar sample hashes and 2 of 88 of ThreatFox’s own hash IOCs
resolved. For sample metadata use the malwarebazaar pack (same vendor, same
key), which indexes every sample.
Committed examples go stale by design. abuse.ch expires every IOC older
than six months (“IOCs older than 6 months are no longer exposed on the
ThreatFox API and ThreatFox Export”), which is why abuse.ch’s OWN documentation
example hash 2151c4b970eff0071948dbbc19066aa4 returns no_result today — it
points at IOC id 4726, from 2021. Re-verify the examples in
workers/gateway/src/tool-examples.json when they age out; never commit a
placeholder like d41d8cd98f00b204e9800998ecf8427e (the MD5 of the empty
string), which is what taught every caller the wrong shape.
Auth
- Platform key: gateway env
PLATFORM_ABUSECH_KEY(shared with malwarebazaar). - BYO:
?_apiKey=<key>after registering at https://auth.abuse.ch.
Data source
https://threatfox-api.abuse.ch/api/v1/ — header Auth-Key, POST with JSON body.
Tools
- search_ioc — Look up a specific indicator of compromise (IP, domain, URL, hash, etc.). Returns matching IOCs with malware family, confidence, threat-type, first/last seen, tags, references.
- recent_iocs — IOCs added to ThreatFox in the last N days. Useful for daily threat-intel ingestion.
- search_hash — Everything ThreatFox knows about one file hash (md5 / sha1 / sha256), across BOTH relations it stores hashes in —
- search_malware — IOCs tagged to a malware family (e.g., “Cobalt Strike”, “Emotet”, “QakBot”).
Tools
recent_iocs— IOCs added to ThreatFox in the last N days. Useful for daily threat-intel ingestion.search_hash— Everything ThreatFox knows about one file hash (md5 / sha1 / sha256), across BOTH relations it stores hashes in —search_ioc— Look up a specific indicator of compromise (IP, domain, URL, hash, etc.). Returns matching IOCs with malware family, confidence, threat-type, first/last seen, tags, references.search_malware— IOCs tagged to a malware family (e.g., Cobalt Strike , Emotet , QakBot ).