Collection of scripts to interact with ESET (https://eti.eset.com) MISP instance.
Script to extract yara rules from MISP for a given event or ESET Threat Actor.
Usage:
python extract_yara_from_misp.py -e EVENT_ID
python extract_yara_from_misp.py -g "GROUP NAME"
python extract_yara_from_misp.py -a (extracts all yara rules)
Script to extract Threat Actor's region from MISP for a given event.
Usage:
python extract_threat_actor_country.py -e EVENT_ID
Examples.ipynb is a Jupyter Notebook (https://jupyter.org) is inspired from PyMISP doc and examples (https://github.com/MISP/PyMISP/tree/main/examples) and contains the complete setup and several example queries to help users get started with the ESET MISP instance.
Examples of queries for interacting with ETI TAXII feeds (https://help.eset.com/eti_portal/en-US/taxii_feeds.html).
Complete examples can be found here:
The following sections provide example GraphQL queries and common investigation workflows.
Use the iocSearch query with any domain, URL, IPv4 address, or file hash (MD5
/ SHA-1 / SHA-256). The indicator type is detected automatically, you don't
need to tell it what you're searching for.
query ($conditions: String!) { iocSearch(conditions: $conditions) { __typename ... on IoCDomain { domain state first_seen last_seen } ... on IoCFile { sha1 sha256 threat first_seen } } }
{ "conditions": "%example.com" }Example response:
{
"data": {
"iocSearch": [
{
"__typename": "IoCDomain",
"domain": "example.com",
"state": "malicious",
"first_seen": "2026-05-01T12:00:00Z",
"last_seen": "2026-07-10T08:30:00Z"
}
]
}
}Use iocReputationCheck to find out if indicators are currently blocked by
ESET. Pass one list type per request, sha256, sha1, url, ip, or
domain with up to 100 values.
query ($domain: [String!]) { iocReputationCheck(domain: $domain) { indicator_value blocked } }
{ "domain": ["example.com", "malicious-example.net"] }Example response:
{
"data": {
"iocReputationCheck": [
{ "indicator_value": "example.com", "blocked": false },
{ "indicator_value": "malicious-example.net", "blocked": true }
]
}
}Only one of
sha256,sha1,url,ip, ordomaincan be used per request mixing types will return a validation error.
Use iocSearch to find IP indicators in all relevant types.
query SubnetSweep($conditions: String!) {
iocSearch(conditions: $conditions) {
__typename
... on IoCIp { ip port state confidence location }
... on IoCDomain { domain ip state countries { country count } }
... on IoCUrl { url ip state threat }
... on IoCCnc { cnc host ip threat state last_alive }
}
}
{ "conditions": "1.2.3.%" }query DomainInfrastructureSweep($conditions: String!) {
iocSearch(conditions: $conditions) {
__typename
... on IoCDomain { domain ip state confidence last_seen countries { country count } }
... on IoCUrl { url domain state threat first_seen }
... on IoCCnc { cnc host threat state last_alive }
}
}
{ "conditions": "%.malicious-example.com" }query ActorInfrastructureHunt(
$pattern1: String!
$pattern2: String!
$pattern3: String!
) {
campaignA: iocSearch(conditions: $pattern1) {
__typename
... on IoCDomain { domain state }
... on IoCUrl { url threat }
}
campaignB: iocSearch(conditions: $pattern2) {
__typename
... on IoCDomain { domain state }
... on IoCUrl { url threat }
}
campaignC: iocSearch(conditions: $pattern3) {
__typename
... on IoCCnc { cnc threat state }
}
}
{
"pattern1": "%-update-service.com",
"pattern2": "secure-%.net",
"pattern3": "%.dyndns-example.org"
}Driven by boolean variables, so one saved query can return a lean or a fully-enriched payload on demand, no separate "light" and "heavy" query to maintain.
query FlexibleSweep(
$conditions: String! = "198.51.100.%"
$withCountries: Boolean = false
$withDetections: Boolean = false
) {
iocSearch(conditions: $conditions) {
__typename
... on IoCUrl {
url
state
threat
countries @include(if: $withCountries) { country count }
opener_detection @include(if: $withDetections)
downloaded_detection @include(if: $withDetections)
}
... on IoCDomain {
domain
state
countries @include(if: $withCountries) { country count }
}
... on IoCCnc {
cnc
threat
state
prot_l4 @skip(if: true)
}
}
}
{ "conditions": "198.51.100.%", "withCountries": true, "withDetections": false }-
Create a query to
iocSearchBotnet:query BotnetWithCncs($sha1: String!) { iocSearchBotnet(sha1: $sha1) { threat targets cncs { url tcp last_alive } } } {"sha1": "4c289f978c4c464da05cee4c33101ea7f5291d7b"}
-
Add a post-response script:
const botnets = res.body?.data?.iocSearchBotnet || []; const enriched = []; for (const botnet of botnets) { const detailsByCnc = new Map(); for (const cnc of botnet.cncs || []) { const cncValue = cnc.url; if (!cncValue) continue; bru.setVar("pivotCnc", cncValue); try { const lookupResponse = await bru.runRequest("cnc-lookup"); if (lookupResponse.status !== 200) { console.error("C&C lookup failed:", cncValue, lookupResponse.statusText); continue; } const graphqlResponse = lookupResponse.data ?? lookupResponse.body; if (graphqlResponse?.errors) { console.error("GraphQL C&C lookup failed:", cncValue, graphqlResponse.errors); continue; } const matches = graphqlResponse?.data?.iocSearchCnc || []; for (const match of matches) { if (match?.cnc) { detailsByCnc.set(match.cnc, match); } } } catch (error) { console.error("C&C lookup request failed:", cncValue, error.message); } } enriched.push({ ...botnet, cnc_details: [...detailsByCnc.values()], }); } bru.setVar("enrichedBotnets", JSON.stringify(enriched)); console.log(enriched);
-
Create a second query and name it
cnc-lookup:fragment CncCore on IoCCnc { cnc host ip port state prot_l4 prot_l7 } query cnclookup($cnc: String!) { iocSearchCnc(cnc: $cnc) { ...CncCore } } {"cnc": "{{pivotCnc}}"}
-
The expected result appears in the console:
[ 0:{ "threat":"Win32/Phorpiex.AS worm" "targets":[] "cncs":[ 0:{...} 1:{...} ] "cnc_details":[ 0:{ "cnc":"http://178.16.54.109/1" "host":"178.16.54.109" "ip":"178.16.54.109" "port":80 "state":"Blocked" "prot_l4":"TCP" "prot_l7":"http" } 1:{ "cnc":"http://178.16.54.109/2" "host":"178.16.54.109" "ip":"178.16.54.109" "port":80 "state":"Blocked" "prot_l4":"TCP" "prot_l7":"http" } ] } ]