# Indicator Pivoting

> Indicator pivoting methodology — how to use one known indicator to discover related infrastructure across the IOC graph. Decision tree by indicator type with concrete `/lookup-*` commands per pivot, a worked multi-hop example, pivot-quality scoring, and routing into the rigor pipeline. Use when the user asks "what else is connected to this IP / domain / hash / cert / actor?", needs to expand a si…

- **Type:** Skill
- **Install:** `agentstack add skill-liberty91ltd-cti-skills-indicator-pivoting`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Liberty91LTD](https://agentstack.voostack.com/s/liberty91ltd)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Liberty91LTD](https://github.com/Liberty91LTD)
- **Source:** https://github.com/Liberty91LTD/cti-skills/tree/main/skills/indicator-pivoting
- **Website:** https://liberty91.com/cti-skills

## Install

```sh
agentstack add skill-liberty91ltd-cti-skills-indicator-pivoting
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Indicator Pivoting

Pivoting is the art of using one known indicator to discover related infrastructure, expanding a single seed into an understanding of an adversary's wider operations. Each pivot is a directed-graph walk: the node is an indicator (typed), the edge is a relation derived from a lookup, and the cost is one or more API calls plus the analyst time to interpret the result.

This skill is the canonical "how to walk the IOC graph" reference. The four investigation skills (`/ip-investigation`, `/domain-investigation`, `/hash-investigation`, `/url-investigation`) chain a fixed set of lookups for a known seed type; this skill takes over when you need to keep walking past their first-hop output.

**This skill invokes:** `/lookup-virustotal`, `/lookup-otx`, `/lookup-shodan`, `/lookup-censys`, `/lookup-urlscan`, `/lookup-abuseipdb`, `/lookup-greynoise`, `/lookup-misp`, `/lookup-opencti`, `/lookup-ransomwarelive`, `/lookup-reversinglabs`, `/lookup-crowdstrike`; then `/score-source`, `/apply-tlp`, `/confidence-language`. May hand off to `/threat-actor-profiling`, `/campaign-tracking`, `/malware-analysis`, `/yara-writing`, `/sigma-writing`, `/darkweb-collection`.

## When to invoke

- The user has a single seed IOC and asks "what else is related to this?"
- An investigation skill returned `pivot_candidates` and you need to walk further than its routing did
- You suspect a campaign cluster (multiple indicators in incident telemetry) and want to confirm common infrastructure
- A threat-actor profile mentions an indicator and you want to see the wider footprint
- You need to clearly document a pivot chain so a reviewer can trace your reasoning end-to-end

## Pivot decision trees, with the commands to run

For each starting indicator type: a tree of pivots, then the concrete commands. The `/lookup-*` form is the skill-invocation; the `python3 tools/clis/...` form is the underlying CLI. Both are equivalent — pick whichever the surrounding context demands.

### Starting from an IP address

```
IP address
├── Reputation (cheap, parallel first)
│   ├── /lookup-virustotal ip 
│   ├── /lookup-abuseipdb (abuse + categories)
│   └── /lookup-greynoise (noise/scanner classification — filters out internet-wide scanners)
├── Service fingerprint
│   ├── /lookup-shodan host           → ports, banners, products, vulns, hostnames
│   └── /lookup-censys host           → richer service detail, certificate chain, CPE
├── DNS pivots
│   ├── /lookup-shodan dns reverse    → reverse-DNS hostname(s)
│   ├── /lookup-virustotal ip  --relationships resolutions  → passive-DNS history
│   └── /lookup-otx ip                → community-reported domains for this IP
├── Cert pivots
│   ├── /lookup-shodan host           → cert SHA1 in the banner blob
│   └── /lookup-censys host           → cert fingerprint(s) → pivot to other hosts
├── JARM / TLS-fingerprint pivots
│   └── /lookup-shodan search 'ssl.jarm:""'   → other hosts with same JARM (often C2 family)
├── Range pivots (use sparingly — high false-positive risk)
│   └── /lookup-shodan search 'net:'          → neighbours in same /24
└── Malware pivots
    └── /lookup-virustotal ip  --relationships communicating_files,downloaded_files
```

**Concrete commands:**

```bash
# Reputation triple
python3 tools/clis/virustotal.py ip 185.220.101.45
python3 tools/clis/abuseipdb.py check 185.220.101.45 --verbose
python3 tools/clis/greynoise.py ip 185.220.101.45

# Service fingerprint
python3 tools/clis/shodan.py host 185.220.101.45 --history
python3 tools/clis/censys.py host 185.220.101.45 --at-time 2026-04-01T00:00:00Z

# Passive DNS + community
python3 tools/clis/virustotal.py ip 185.220.101.45 --relationships resolutions,communicating_files
python3 tools/clis/otx.py ip 185.220.101.45

# JARM-pivot to find C2 siblings
python3 tools/clis/shodan.py search 'ssl.jarm:"3fd21b20d00000021c43d21b21b43d41226ab2434c2cd1a5e1b3c2bc5f4f4e7"'
```

### Starting from a domain

```
Domain
├── Reputation (parallel)
│   ├── /lookup-virustotal domain            → detection ratio + categories + WHOIS-ish
│   ├── /lookup-otx domain                   → pulses + passive DNS
│   └── /lookup-urlscan domain               → existing scans (don't re-submit by default)
├── DNS resolution (always)
│   ├── /lookup-shodan dns resolve           → current A/AAAA records
│   └── /lookup-virustotal domain  --relationships resolutions  → historical
│       └── pivot the resolved IPs as IPs (above)
├── Subdomain enumeration
│   ├── /lookup-shodan dns domain            → subdomains Shodan knows about
│   └── /lookup-censys search 'names: '      → CT-derived names (paid plan only)
├── Certificate transparency
│   ├── /lookup-censys search 'services.tls.certificates.leaf_data.subject.common_name:'
│   │   → other hosts presenting a cert for this name
│   └── /lookup-virustotal domain            → cert SAN list in the response
│       └── cert SAN siblings → pivot each as a domain
├── WHOIS / registrant
│   └── /lookup-virustotal domain            → registrar, creation_date, registrant_email if not redacted
│       └── registrant email → /lookup-virustotal search …  (paid VT Intel only)
│       └── reverse-WHOIS via vendor (DomainTools, WhoisXMLAPI) — outside the bundled lookups
├── Ransomware-claim sweep
│   └── /lookup-ransomwarelive search --q 
│       → derive org-candidate by stripping public suffix and `www.`; see /domain-investigation §3
└── Content
    └── /lookup-urlscan domain               → screenshots, DOM, page tech
```

**Concrete commands:**

```bash
# Reputation + content
python3 tools/clis/virustotal.py domain evil.example.com
python3 tools/clis/otx.py domain evil.example.com
python3 tools/clis/urlscan.py search "domain:evil.example.com"

# DNS now + historical
python3 tools/clis/shodan.py dns resolve evil.example.com
python3 tools/clis/virustotal.py domain evil.example.com --relationships resolutions

# Cert-CN pivot (paid Censys plan)
python3 tools/clis/censys.py search 'services.tls.certificates.leaf_data.subject.common_name:"evil.example.com"'

# Ransomware-claim sweep
python3 tools/clis/ransomwarelive.py search --q "example"
```

### Starting from a URL

```
URL
├── Live scan (when a fresh capture is wanted)
│   └── /lookup-urlscan submit             → DOM, network log, screenshot, verdict
├── Existing scans
│   └── /lookup-urlscan search 'page.url:' → past captures (faster, no re-submit)
├── Reputation
│   └── /lookup-virustotal url             → detection ratio + redirect chain
├── Hostname pivot
│   └── extract hostname → pivot as a domain (above)
└── Path/kit fingerprint
    └── /lookup-urlscan search 'page.url:""'  → other URLs with same kit path
```

**Concrete commands:**

```bash
python3 tools/clis/urlscan.py submit "https://login-acme[.]net/secure/portal" --tags phishing
python3 tools/clis/urlscan.py search 'page.url:"/secure/portal/"'
python3 tools/clis/virustotal.py url "https://login-acme[.]net/secure/portal"
```

### Starting from a file hash

```
Hash (md5 / sha1 / sha256)
├── Reputation + family
│   ├── /lookup-virustotal file           → detection stats, family tags, sandbox verdicts
│   └── /lookup-otx file                  → community pulses, campaign attribution
├── C2 extraction (highest-value pivot)
│   └── /lookup-virustotal file  --relationships contacted_ips,contacted_domains,contacted_urls
│       → pivot each contacted indicator
├── Family lineage
│   └── /lookup-virustotal file  --relationships dropped_files,parent_files,similar_files
│       → walk dropper/payload chain
├── Submitter/source
│   └── /lookup-virustotal file  --relationships submissions   (premium)
├── Compilation artefacts
│   └── /lookup-virustotal file           → response includes pdb_path, compiler, packers
│       → distinctive PDB path → /lookup-virustotal search …  (premium)
└── Internal correlation
    ├── /lookup-misp search-attributes --value    → events that already include this hash
    └── /lookup-opencti lookup                    → observables/indicators already in your knowledge base
```

**Concrete commands:**

```bash
# Detection + family
python3 tools/clis/virustotal.py file 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

# C2 extraction (highest-value)
python3 tools/clis/virustotal.py file 5e884898... \
  --relationships contacted_ips,contacted_domains,contacted_urls --limit 25

# Family lineage
python3 tools/clis/virustotal.py file 5e884898... \
  --relationships dropped_files,parent_files,similar_files --limit 10

# OTX community attribution
python3 tools/clis/otx.py file 5e884898...

# Internal correlation via MISP / OpenCTI
python3 tools/clis/misp.py search-attributes --value 5e884898...
python3 tools/clis/opencti.py lookup 5e884898...
```

### Starting from a TLS certificate (SHA-256 or CN)

```
Certificate
├── Hosts presenting this cert
│   ├── /lookup-shodan search 'ssl.cert.fingerprint:'    → IPs serving the cert
│   └── /lookup-censys search 'services.tls.certificates.leaf_data.fingerprint_sha256:'
└── Other certs by same Subject
    └── /lookup-censys certs view      → issuer, validity, sibling SANs
```

**Concrete commands:**

```bash
python3 tools/clis/shodan.py search 'ssl.cert.fingerprint:"5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"'
python3 tools/clis/censys.py certs view 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
```

### Starting from a registrant email or org name

```
Registrant identity
├── Reverse-WHOIS (premium / outside the bundled lookups — DomainTools, WhoisXMLAPI, Constella)
├── Internal MISP correlation
│   └── /lookup-misp search-attributes --type whois-registrant-email --value 
└── Domains attributed to the same actor in OTX pulses
    └── /lookup-otx pulse-search ""
```

### Starting from an actor name, malware family, or campaign label

```
Actor / family / campaign string
├── Ransomware group (if applicable)
│   ├── /lookup-ransomwarelive group             → group profile, victims, IOCs
│   ├── /lookup-ransomwarelive iocs              → IOC dump for this group
│   └── /lookup-ransomwarelive yara              → YARA rules attributed to the group
├── Community pulses
│   └── /lookup-otx pulse-search ""   → IOCs from public pulses
├── MISP events
│   └── /lookup-misp search-events --tag "actor=" → previously catalogued IOCs
├── OpenCTI knowledge base
│   └── /lookup-opencti search ""      → intrusion sets, malware, reports, campaigns; then `get ` for relationships
└── Surface-web mentions (forums + Telegram)
    └── /darkweb-collection (with selectors built from the actor's known aliases)
```

### Starting from a Telegram channel handle or onion mirror

```
Channel handle / .onion address
├── Telegram channel handle
│   └── scripts/telegram_monitor.py --once --channels @  --selectors-file 
│       → match channel content against your IOC selectors
└── .onion address
    └── scripts/onion_search.py --query ""
        → resolve other clearnet/onion mirrors that mention the same string
```

(See `/darkweb-collection` for OPSEC and access posture before running these.)

## Pivot quality scoring

Not all pivots are equal. Score each before reporting:

| Pivot | Confidence | False-positive risk | Why |
|---|---|---|---|
| C2 IP/domain extracted from a reverse-engineered sample | High | Low | Direct, sample-grounded |
| TLS cert SHA-256 reused across hosts | High | Low | Cert reuse is intentional; the actor pinned it |
| Cert subject CN reused across hosts | Medium-High | Low-Medium | CN can be reused legitimately on shared certs |
| JARM fingerprint match | Medium-High | Medium | Strong for unusual JARMs; weak for default-config tools |
| Passive DNS (domain → IP, current) | Medium-High | Low-Medium | Direct; check the dates |
| Passive DNS (domain → IP, historical only) | Medium | Medium | Stale; could pre-date the campaign |
| Same WHOIS registrant email | Medium | Medium | Many real users share an email across domains |
| Same name server | Low | High | Popular NS providers host millions of unrelated domains |
| Same /24 IP range | Low | High | Cloud and CDN sharing |
| Same registrar | Very low | Very high | Registrars are infrastructure; near-meaningless alone |
| Same JA3/JA3S TLS client fingerprint | Medium | Medium | Strong for custom clients; weak for OS-default stacks |
| Same MITRE technique | Very low | Very high | Technique-level overlap is not infrastructure overlap |

**Rule of thumb**: a pivot chain is only as strong as its weakest link. Three High pivots in series ≈ High overall. One High + two Mediums ≈ Medium. Anything with a Low link ≈ Low overall, regardless of length.

## Pivot routing — by goal

| Goal | First-choice lookup | Second |
|---|---|---|
| IP reputation | `/lookup-virustotal` | `/lookup-abuseipdb`, `/lookup-greynoise` |
| Filter out internet-wide scanners | `/lookup-greynoise` | — |
| Service banner / vuln view | `/lookup-shodan` | `/lookup-censys` (richer, slower) |
| Cert fingerprint pivot | `/lookup-censys` | `/lookup-shodan` |
| JARM pivot | `/lookup-shodan` | `/lookup-censys` |
| Passive DNS | `/lookup-virustotal` `--relationships resolutions` | `/lookup-otx` |
| Subdomain enumeration | `/lookup-shodan dns domain` | `/lookup-censys` (paid) |
| Live URL analysis | `/lookup-urlscan submit` | `/lookup-virustotal url` |
| Existing URL captures | `/lookup-urlscan search` | — |
| File reputation | `/lookup-virustotal` | `/lookup-otx`, `/lookup-reversinglabs hash` |
| Authoritative classification + MITRE ATT&CK mapping | `/lookup-reversinglabs report --detailed` | `/malware-analysis` |
| Pivot to siblings by malware family / threat name | `/lookup-reversinglabs search 'threatname:'` | `/lookup-otx pulse-search` |
| Parent container / extracted children | `/lookup-reversinglabs containers` / `extracted` | — |
| C2 extraction from a hash | `/lookup-virustotal` `--relationships contacted_ips,contacted_domains,contacted_urls` | `/lookup-reversinglabs report --fields networkthreatintelligence` |
| Community/actor attribution | `/lookup-otx` | `/lookup-misp search-events --tag`, `/lookup-crowdstrike indicator` (actors + malware families linked to the IOC) |
| Actor → TTPs / reports / related infrastructure | `/lookup-crowdstrike actor ""` then `ttps` / `reports --actor` | `/threat-actor-profiling` |
| Internal correlation against own catalogued IOCs | `/lookup-misp` | `/lookup-opencti lookup` (observables + indicators, then `get` for relationships) |
| Ransomware-group profile / IOCs / YARA | `/lookup-ransomwarelive` | — |
| Ransomware-victim sweep on org name | `/lookup-ransomwarelive search --q` | — |
| Underground-forum / Telegram chatter | `/darkweb-collection` | — |

## Worked example — phish URL → APT cluster

A walkthrough of a realistic four-hop chain. Source rating in brackets.

**Seed.** `https://login-acme[.]net/secure/portal/login.html` reported by an internal user as a suspicious password-reset email. Source: internal phishing inbox (B2).

**Hop 1 — characterise the URL.**

```bash
python3 tools/clis/urlscan.py search 'page.url:"login-acme.net"'
python3 tools/clis/virustotal.py url "https://login-acme.net/secure/portal/login.html"
```

Result: URLScan shows two prior submissions in the last 14 days (both flagged phishing), screenshot is an Acme-cloned login page, JS posts credentials to `https://api.login-acme[.]net/auth`. VT detection ratio 12/89 — phishing. Pivot candidate: domain `login-acme.net` (B2).

**Hop 2 — domain → resolved IP and cert.**

```bash
python3 tools/clis/shodan.py dns resolve login-acme.net api.login-acme.net
python3 tools/clis/virustotal.py domain login-acme.net --relationships resolutions
```

Result: both names currently resolve to `198.51.100.42`; passive DNS shows the names appeared together on 2026-04-12 (10 days ago — newly weaponised). Pivot candidates: IP `198.51.100.42` (B2), creation cluster of `*.login-acme.net` subdomains.

**Hop 3 — IP → service fingerprint and cert SHA-256.**

```bash
python3 tools/clis/shodan.py host 198.51.100.42

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Liberty91LTD](https://github.com/Liberty91LTD)
- **Source:** [Liberty91LTD/cti-skills](https://github.com/Liberty91LTD/cti-skills)
- **License:** MIT
- **Homepage:** https://liberty91.com/cti-skills

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-liberty91ltd-cti-skills-indicator-pivoting
- Seller: https://agentstack.voostack.com/s/liberty91ltd
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
