Features • Installation • Usage • Example • Architecture • Scope
VisorGraph is a single 6.7 MB Go binary that takes a single seed (IP, domain, CIDR, ASN, cert fingerprint, banner string) and walks outward through CT logs, TLS handshakes, HTTP probes, and L7 fingerprinting until the graph saturates. Every active probe runs inside a gVisor (runsc) OCI sandbox, so untrusted endpoints cannot reach the researcher's box on SSRF or callback. The output is a typed provenance graph emitted as a JSONL stream for live chaining.
A port scanner stops at the open port. VisorGraph follows the cert. From a single IP it pivots through CT logs to sibling subdomains, walks TLS SANs back to the parent domain, queries an unauthenticated Prometheus as a passive topology oracle, classifies the exposure (legacy, management, accidental, intended), then matches discovered Go binaries against the Google OSV database for reachability-aware CVE scoping.
- Single 6.7 MB static Go binary, no runtime dependencies
- Goroutine worker pool, 10,000+ concurrent workers, native
-rpsrate limiting - Native gVisor
runscOCI sandboxing on every active probe (TLS, HTTP, L7 handshakes) - Pointer-based graph with atomic
UpsertNodecheck-plus-insert. No duplicate nodes - Google OSV vulnerability database integration with reachability analysis
- CT-log walk, TLS ClientHello, HTTP HEAD, L7 raw-TCP probe ladder, cloud-provider signature matching
- Built-in MITM / intercept detection (
-sandbox-check) flags sandbox-MITM environments before scanning - JSONL stream output for live graph updates and tool-chaining
- Rule-based exposure classifier:
legacy,mgmt,accidental,intended - Hard budget caps: cost, wallclock, /24, ASN
git clone https://github.com/nuclide-research/VisorGraph
cd VisorGraph
make setup-deps # installs gVisor (runsc) + Go deps
make build # produces ./bin/visorgraphOr direct go build:
go build -o bin/visorgraph ./cmd/visorgraphRequires Go 1.22 or later. gVisor is required only for active-probe sandboxing. Passive mode (-no-active) runs without it.
# high-speed discovery
./bin/visorgraph -domain target.com -workers 1000 -rps 50
# secure active recon (sandboxed)
./bin/visorgraph -ip 1.2.3.4 -sandbox-check
# passive-only
./bin/visorgraph -domain example.com -no-active
# real-time chaining
./bin/visorgraph -domain example.com | grep finding > findings.jsonl
# batch cert-pivot from a seeds file
./bin/visorgraph -seeds-file storefronts.txt -no-streamSeeds file format: one seed per line, IP or domain auto-detected via net.ParseIP. # comments and inline # notes are stripped.
Flags
| Flag | Default | Description |
|---|---|---|
-ip |
Seed IP address | |
-domain |
Seed FQDN | |
-seeds-file |
File of seeds, one per line. Combines with -ip / -domain |
|
-workers |
256 | Goroutine pool size |
-rps |
0 | Max requests per second (0 = unlimited) |
-no-active |
false | Disable active probes (passive-only) |
-sandbox-check |
false | Detect MITM / intercept before scanning |
-no-stream |
false | Suppress JSONL stream, emit only final graph |
Unauthenticated Prometheus as an internal-topology oracle. Input: a single IP with no open ports on 80 or 443.
./bin/visorgraph -ip 34.18.94.178 -no-streamThe Prometheus probe found an unauthenticated instance on :9090. From there the Prometheus API became a passive oracle. No packets to any internal IP:
[service] 34.18.94.178
service: prometheus 2.22.2
scrape_targets_count: 197
lifecycle_enabled: true (/-/quit and /-/reload exposed, DoS vector)
k8s_namespaces: ars, ars-v2, ars-core-api-reports, iam, external-secrets,
falcon-kac, falcon-system, kube-system, monitoring, common
ingress_domains: a112.appranix.net, a112-signin.appranix.net,
a112-monitoring.appranix.net
[domain] a112.appranix.net (org attributed: Appranix)
[domain] appranix.net (discovered via TLS SAN follow-on)
Internal topology surfaced without touching a single internal IP: K8s API server, 17 GKE nodes with kubelet read-only port :10255, Istio sidecar mesh, Cassandra cluster, ZooKeeper, 60+ named services. The chain: IP, Prometheus oracle, internal topology + ingress hostnames, TLS follow-on, parent domain, org attribution, all in one run.
| Component | Technology | Role |
|---|---|---|
| Engine | Go 1.22 goroutine pool | High-speed task orchestration |
| Shield | gVisor (runsc) |
Native OCI sandboxing per probe |
| Brain | Google OSV API | Reachability-aware CVE matching |
| Map | Pointer-based graph | Atomic check-plus-insert dedup |
cmd/visorgraph/ CLI entrypoint
internal/
engine/ dispatcher + fixed-point loop (pool + inFlight atomic)
graph/ RWMutex pointer graph, atomic UpsertNode
probe/ ProbeFunc registry: ct_logs, tls_clienthello, http_head, ...
vulncheck/ OSV REST API + govulncheck integration
sandbox/ MITM detection + gVisor runsc OCI harness
exposure/ rule-based classifier (legacy/mgmt/accidental/intended)
stream/ thread-safe JSONL encoder
l7/ raw TCP probe ladder + cloud-provider signature matching
budget/ hard caps: cost, wallclock, /24, ASN
types/ Seed, Node, Edge, Finding (typed string enums throughout)
VisorGraph makes real TCP and HTTPS connections during active probing. It does not authenticate, POST data, execute exploits, or modify anything on a target. Only scan systems you own or have explicit written authorization to assess. The gVisor sandbox protects the researcher from the target; respecting the rate limit and the target's terms of service is on the operator.
- aimap — AI/ML infrastructure fingerprint scanner, deep-enum stage after this one
- scanner — fast active-banner stage between passive discovery and deep enum
- VisorLog — findings ledger, the JSONL stream destination
- VisorBishop — meta-fingerprinter for AI observability platforms
- recongraph — typed provenance graph for multi-source recon
MIT. Part of the NuClide toolchain. Contact: nuclide-research.com
