Infrastructure Intelligence Model Query Language

Ask structural questions about adversary infrastructure.

IIMQL is a read-only query language for IIM chains, patterns and feeds. It turns infrastructure questions into compact queries: roles, relations, techniques, entities, confidence and chain shape in one place.

Language Read-only
Model IIM v1.1
Use Case Chain search
iimql query
MATCH (e:entry)-->(s:staging)-->(p:payload)-[:connect]->(c:c2) WHERE e.techniques HAS "IIM-T019" AND c.techniques HAS "IIM-T011" RETURN chain.chain_id, chain.actor_id, c.entity.value
What this asks

Show chains where a geofenced entry path eventually lands in a payload that talks to a domain-rotation or dynamic-DNS C2.

shapeentry → staging → payload → c2
filterIIM techniques
outputchain + actor + endpoint
01 - Why IIMQL

IOC search answers artifacts. IIMQL answers structure.

IOC feeds can tell you whether a domain, IP or hash was seen before. IIMQL is for the next question: how does this thing sit inside an operation? Is it an entry, a redirector, a staging host, a payload, or a C2 node? Which relation connects it to the rest of the chain?

Problem

Ad-hoc pivots are annoying

Every chain library eventually turns into one-off scripts. Find me all C2 domains with dynamic DNS. Find chains that use archive delivery. Find two redirectors before staging. Repeat forever.

IIMQL gives you one query surface
Point

Infrastructure is graph-shaped

IIM already models adversary infrastructure as chains and patterns. IIMQL makes that model searchable without throwing analysts back into Python for every structural question.

You query roles, edges and properties
Boundary

It does not replace ATT&CK

ATT&CK describes behavior. IIM describes infrastructure. IIMQL queries the infrastructure side: hosting, routing, resolution, gating and composition.

Scope is strictly infrastructure
02 - Simple documentation

Small language. Useful questions.

The public-facing mental model can stay simple: pick what you want to match, optionally filter it, then return the fields you care about.

Query shape

Most day-to-day IIMQL queries follow one shape: MATCH target, then optional WHERE, then optional RETURN. The target can be a whole chain, a flattened object, or a graph pattern.

MATCH chain WHERE confidence IN ["confirmed", "likely"] RETURN chain_id, actor_id, confidence

Targets

Use simple targets when you do not need a chain shape. This is perfect for listing all C2 positions, searching domain entities, or finding a specific relation type across a corpus.

MATCH chain
Top-level chain documents

Filter by actor, confidence, source, review status or metadata.

MATCH position
Role assignments

Find entries, redirectors, staging nodes, payloads or C2 positions.

MATCH entity
Observed artifacts

Search URLs, domains, IPs, files, hashes, certificates and related values.

MATCH relation
Edges

Find download, redirect, drops, execute, connect and resolution relations.

MATCH position WHERE role = "c2" AND techniques HAS "IIM-T008" RETURN chain.chain_id, entity.value, techniques

Graph patterns

This is the interesting part. Nodes are written as roles, optionally with aliases. Edges can be untyped arrows or typed relations. The result is a structural question over IIM chains.

MATCH (e:entry)-[:download]->(s:staging)-[:drops]->(p:payload)-[:connect]->(c:c2) RETURN chain.chain_id, e.entity.value, c.entity.value

Aliases like e, s, p and c can be used later in WHERE or RETURN.

Filters

Filters cover the basics: equality, ordering, regex, membership, substring matching, boolean logic and grouping. For analyst use, the most important operators are usually HAS, IN, regex and boolean combinations.

Comparisons

=!=<><=>=

Useful for confidence, sequence order, counts and metadata.

Collections

HASINNOT IN

Query technique arrays, confidence sets, actor IDs and role lists.

Text

CONTAINS=~!~

Substring and regex matching for domains, URLs and file names.

Logic

ANDORNOT

Combine shape and property constraints without script glue.

Return fields

Omit RETURN for a readable summary. Add one field for scalar output, or multiple fields for structured rows. Qualified field names make complex results clear.

MATCH entity WHERE type = "domain" AND value =~ /\.duckdns\.org$/ RETURN chain.chain_id, value

Matching semantics

IIMQL is structural, not fuzzy. A pattern only matches when the relation exists in the chain. Missing fields do not magically become false. String comparisons are case-sensitive because IIM values are not normalized by default.

# Direct structural match. Nothing is skipped. MATCH (:entry)-->(:payload) # This does not mean "entry eventually reaches payload". # It means there is a direct edge between those positions.
03 - Syntax map

The language in one screenful.

Keep the homepage practical. People should be able to copy one example, understand the moving parts and then leave for the repository when they need the formal grammar.

Entry
phishing URL
IIM-T019
Redirector
edge worker
IIM-T015
Staging
archive host
IIM-T024
Payload
loader
IIM-T023
C2
dynamic DNS
IIM-T008
04 - Examples

A tiny query workbench.

This is intentionally not a full parser in the browser. It is a documentation-friendly example switcher: select a query, read what it means, copy it into the CLI or later wire this up against a hosted API.

IIMQL examples

Basic chain filter

05 - Install

Use it locally. Pipe it into your CTI workflow.

The CLI is designed for local chain folders, JSON output and quick analyst pivots. The Python API exists for tools that want IIMQL as an embedded query layer.

CLI

Install the package, point it at a directory of IIM chains, and run inline queries or query files.

pip install -e . iimql 'MATCH position WHERE role = "c2"' examples/chains/ iimql --format json 'MATCH entity WHERE type = "domain"' examples/chains/ | jq .

Python API

Parse once, execute many times, or use the one-liner helper when embedding IIMQL into Kraken, Workbench tooling or another local pipeline.

from iimql import parse, execute, load_paths docs = load_paths(["examples/chains/"]) q = parse('MATCH position WHERE role = "c2"') for row in execute(q, docs.chains): print(row["chain"]["chain_id"])