Skip to content
volrant136
All research
NotesTLP:CLEARTLP:CLEAR — Disclosure is not limited. This material may be shared publicly without restriction.

Automating Threat Hunting with Hunt Intelligence (Hunt.io) — Part 1

Published
Reading time
5 min
Analyst
@volrant136
Region
ALL
  • hunt.io
  • automation
  • threat intelligence
  • python
  • HuntSQL
  • threat hunting
Cover Image -  Gemini Generated

Threat hunting has traditionally meant analysts scrolling through feeds, copy-pasting indicators of compromise (IOCs) into search bars, and manually cross-referencing infrastructure to figure out what's real, what's noise, and what's worth escalating.

Image
Figure 01: Traditional method for pivoting through IOCs to find cross reference evidences (Gemini Generated)

Automation doesn't replace the analyst but it removes the repetitive triage work (fetching, deduplicating, cross-referencing, filtering noise) so time can be utilize only for validation.

This is Part 1 of a series where I build exactly that kind of pipeline on top of Hunt.io's threat intelligence platform (opens in a new tab) by using IOC Hunter (opens in a new tab) feed by enriching them using HuntSQL™ (opens in a new tab) queries for automated pivots.

The API surface

The two endpoints this pipeline leans on:

EndpointPurpose
GET https://api.hunt.io/v1/feeds/ioc-hunterPulls the IOC Hunter feed as gzip-compressed JSONL, with a configurable lookback window (days) and optional publication_domain filter
GET https://api.hunt.io/v1/sql?query=...Runs a HuntSQL query and returns JSON results, with pagination support

Every request needs a token header for authentication, and the query parameter must be URL-encoded.

So, our pipeline runs one endpoint to get indicators, one endpoint to ask questions about them.

Where This Starts: IOC Hunter Posts

Before writing any code, it's worth looking at what we're automating around. The IOC Hunter → Posts view in the Hunt.io platform lists individual write-ups pulled from public threat research, each with extracted IOC counts (IPs, hosts, hashes) and any attributed malware or threat actor.

Image
Figure 02: Hunt.io IOC Hunter view of latest threat intelligence blogs/research entries

One entry from a recent feed pull:

Android RAT Survives Reboots Using Watchdog Services and Boot Receivers — sourced from cybersecuritynews.com

Opening the post shows the kind of structured metadata IOC Hunter extracts automatically: a written summary of the campaign, a list of associated hosts and IPs, malware files, and a timestamp.

Image
Figure 03: An example from Hunt.io IOC Hunter tracked research showing actionable intelligence

That structured record is exactly what our pipeline pulls via the feed endpoint.

The question our automation then answers is: has any of this infrastructure shown up elsewhere on the internet, under a different name, since this report was published?

A Template Query: Pivoting on title

Every page Hunt.io's crawlers touch gets a captured page title.

The core query pattern looks like this:

SQL

1-- Step 1: find what a known-bad IP or domain resolves to, title-wise
2SELECT title FROM crawler WHERE ip == 'Any IP / Domain'
3
4-- Step 2: take that title and find every OTHER host serving the same page
5SELECT url FROM crawler WHERE title == 'Any title'

The first query answers what does this indicator look like? The second answers the more valuable question: what else looks exactly like it?".

Here's the shape of the pipeline end to end:

Image
Figure 04: A diagram showing pipeline of our actual work

Two design choices matter here:

  1. Title filtering happens before the second pivot. Titles like "404 Not Found" or "Just a moment..." are common to millions of unrelated pages and gets dropped.

  2. The pivot is two-hop, not one-hop. IOC → title → other URLs sharing that title is what actually surfaces new, previously-unknown infrastructure.

Building It: Code Walkthrough (Pseudocode)

Let's talk about the pieces that matter conceptually, with the full implementation details deliberately left out.

a. Fetching and decoding the feed

Python

1def fetch_ioc_feed(token, days=7, publication_domain=None):
2    # GET the feed endpoint with a lookback window
3    response = get(FEED_URL, params={days, publication_domain}, headers=auth(token))
4    # feed body is gzip-compressed JSON-Lines — one IOC record per line
5    records = [json.loads(line) for line in gunzip(response.body).splitlines()]
6    return records

The feed itself is compressed JSONL rather than a single JSON blob so decompress first, then parse it.

b. Classifying and Pivoting per IOC

Python

1def enrich_ioc(token, ioc):
2    kind = classify(ioc.value)          # "ip" | "domain" | "other"
3    if kind == "other":
4        return ioc                      # no crawler pivot available for hashes/emails
5
6    titles = huntsql(token, title_query_for(kind, ioc.value))
7    titles = [t for t in titles if is_meaningful_title(t)]   # drop 404s, parked pages, etc.
8
9    urls = []
10    for title in titles[:5]:            # cap fan-out to respect rate limits
11        urls += huntsql(token, f"SELECT url FROM crawler WHERE title == '{title}'")
12
13    ioc.hunt_titles, ioc.hunt_urls = titles, urls
14    return ioc

The important idea, not the exact syntax: classify → query → filter → re-pivot.

c. Respecting the API's rate limits

As I am firing dozens of HuntSQL queries per feed pull, a simple token-bucket limiter and exponential backoff on 429 responses keeps the pipeline polite:

Python

1def rate_limited_request(...):
2    wait_if_over_budget()          # token bucket, N requests/sec
3    response = get(...)
4    if response.status == 429:
5        sleep(backoff)
6        retry()
7    return response

d. Rendering the Report

The last step folds every enriched IOC into rows of a static HTML table with client-side search/filter/sort in plain JavaScript for interactive purpose.

The Payoff: Reading the Generated Report

Running the pipeline against a 1-day feed window produced a report covering 236 IOCs containing 24 IPs, 131 domains, and 81 other indicator types, with 2 IOCs successfully enriched via the title pivot and 10 related URLs surfaced that weren't in the original feed at all.

Image
Figure 05: The pipeline output having two successful pivots

Boom!, we got pivot in one click.

One IP, 31.57.243.154, tagged with CornFlake / ChocoShell malware and attributed to UNC2452, matched a crawler-captured page titled "Admin Panel."

Pivoting on that title alone surfaced ten additional URLs having a mix of typosquatted domains, fake Microsoft 365 login pages, and admin-panel lookalikes, none of which were present in the original feed record for that IP.

The same title pivot also matched another domain listed in same blog (owa-ms365.com), confirming the connection instead of leaving it as a coincidence.

2026 08 01 11 06 31
Figure 06: A second pivot confirming the results from first pivot

This is a pratical example showing how a single IOC from a published report turned into ten new leads, surfaced automatically, with zero manual pivoting. We are skipping the validation part here!

So, I have built pipeline that pull a feed, classify each IOC, pivot on page titles through HuntSQL, filter out the noise, and hand the analyst a browsable report instead manual work.

I will try more interesting pivots in the future. Stay Tuned!


Share

X(opens in a new tab)

Published by @volrant136