A lot of security work starts the same way. Something hands you an indicator: an IP out of a firewall log, a file hash off an alert, a domain from an email that did not feel right. Before you can do anything with it you have to answer two questions. Is it bad, and what is it connected to?
The usual way to answer that is by hand, one tab at a time: VirusTotal, then AbuseIPDB, then a WHOIS lookup, then crt.sh, then a couple of blocklists, holding half-loaded browser tabs in your head while you try to form an opinion. It works, but it is slow and easy to do inconsistently, and it falls apart around the third indicator of a busy day.
So I built a command-line tool that does that first pass in one shot. It is called
indict. You give it one indicator, it figures out the type, asks every source that
fits at the same time, and hands back a single report with a verdict and the reasons
under it.
What it does Link to heading
Two kinds of sources feed the report. Some are free and need no account: DNS and reverse DNS, WHOIS, RIPEstat for network ownership, certificate transparency for subdomains, and a set of free threat blocklists (FireHOL, the Tor exit list, Spamhaus, Feodo Tracker, URLhaus, OpenPhish). Two more need a free API key to switch on: VirusTotal for multi-engine reputation and AbuseIPDB for abuse history.
Real talk: the tool runs with no keys, but it is at its best with those two free ones in. The nice part is that it is honest about the difference, and the blocklists mean even the keyless run can return a real verdict, not just facts. The clearest way to see all of that is to run the same indicator both ways.
The same indicator, with and without keys Link to heading
Here is a Tor exit node, looked up with no keys at all:
$ indict 171.25.193.25
╭───────────────────────────────────╮
│ 171.25.193.25 (ip) SUSPICIOUS │
╰───────────────────────────────────╯
source verdict summary
dns unknown reverse DNS: tor-exit-read-me.dfri.se
whois unknown network SE-TORNET, org DFRI-MNT, country SE
ripestat unknown AS198093 DFRI-AS, prefix 171.25.193.0/24, abuse abuse@dfri.net
greynoise unknown not observed by GreyNoise
blocklists suspicious listed on 1 of 4 blocklist(s): Tor exit node
Not run: abuseipdb (no key), virustotal (no key)
With zero keys it already reaches a SUSPICIOUS verdict, because the Tor exit list
is a free feed. The rows above it are facts (who owns the network, what the reverse
DNS says), and the “Not run” panel is honest about the two sources it could not check
without a key. Now the same command with a free VirusTotal and AbuseIPDB key added:
$ indict 171.25.193.25
╭──────────────────────────────────╮
│ 171.25.193.25 (ip) MALICIOUS │
╰──────────────────────────────────╯
source verdict summary
dns unknown reverse DNS: tor-exit-read-me.dfri.se
whois unknown network SE-TORNET, org DFRI-MNT, country SE
ripestat unknown AS198093 DFRI-AS, prefix 171.25.193.0/24, abuse abuse@dfri.net
greynoise unknown not observed by GreyNoise
blocklists suspicious listed on 1 of 4 blocklist(s): Tor exit node
abuseipdb malicious abuse confidence 100/100 from 103 report(s); Tor exit node
virustotal malicious 15/91 engines flagged malicious, 2 suspicious
• [malicious] abuseipdb: abuse confidence 100/100 from 103 report(s)
• [malicious] virustotal: 15/91 engines flagged malicious, 2 suspicious
• [suspicious] blocklists: listed on 1 of 4 blocklist(s): Tor exit node
Same indicator, more sources light up, and the verdict escalates to MALICIOUS. The
evidence panel at the bottom is the part that matters: 100/100 abuse confidence from
103 reports, 15 of 91 engines flagging it. Those are numbers you can sanity-check and
paste into a ticket, not a black box telling you to trust it.
That contrast is the whole pitch. Keyless, the tool can already confirm something is bad. Add two free keys and it corroborates that with cross-engine reputation and tells you how strong the signal is.
Following the connections Link to heading
Checking reputation is only half of it. The other question, “what is this connected
to,” is where a report stops being a lookup. Every source drops breadcrumbs (the IPs
a domain resolves to, the subdomains in its certificates, the reverse DNS on an
address), and the tool clusters them. Here is example.com, keyless:
$ indict example.com
╭──────────────────────────────╮
│ example.com (domain) UNKNOWN │
╰──────────────────────────────╯
source verdict summary
dns unknown resolves to 4 IP(s); 2 nameservers, 1 mail host
whois unknown registrar RESERVED-IANA, created 1995-08-14
crt.sh unknown 5 unique subdomain(s) in certificate transparency logs
urlscan unknown 10000 scan(s) on record; served from 9 distinct IP(s)
Correlated infrastructure
104.20.23.154 (2) example.com, www.example.com
172.66.147.243 (2) example.com, www.example.com
162.43.120.101 (1) example.com
...
crt.sh turned up subdomains, the tool resolved them, and it grouped everything by the
IP it lands on. So instead of a flat list of names you see the shape of the hosting:
www.example.com sits on the same addresses as the apex. On a benign domain that is
unremarkable, but on a real investigation this is exactly where the interesting thing
hides. The subdomain sitting alone on its own IP, away from the rest, is the one worth
a second look. The clustering does not decide that for you, it just arranges the facts
so the odd one out is visible instead of buried on line 34 of a certificate dump.
The honest parts Link to heading
A triage tool you cannot trust is worse than none, so the design leans on a few rules.
The verdict is never invented. When nothing flags an indicator and nothing clears it,
the report says UNKNOWN, not CLEAN. Absence from a blocklist is not a clean bill
of health, and plenty of malicious infrastructure is simply too new to be in any feed
yet. Keyless, the tool can confirm bad, not confirm good.
It also refuses to overreach. It will not call a whole domain malicious just because someone hosted one bad link on it (attackers park payloads on GitHub and pastebins all the time), so blocklist hits on a URL are matched exactly, not by domain. And file hashes are the honest weak spot: with no key they have almost nothing to go on, which is a limit worth stating rather than hiding.
Everything else is in service of trust. Every verdict carries its evidence so you can
audit it. Sources it could not reach are shown as “not run,” never dropped quietly, so
you always know what was actually checked. It cites its sources and links out instead
of copying their data. A --redact flag strips WHOIS registrant names and emails
before you share a report, and --json and --markdown give you the same result for
a pipeline or a ticket.
Where it is Link to heading
indict is a personal project, part of moving my own work from cloud and security
fundamentals toward investigations and OSINT. It is on
GitHub, it runs without any keys, and the
fastest way to get a feel for it is to add the two free keys and point it at
something.
