Back to Articles

fingerprintx: Building a Modern Service Fingerprinting Tool That Actually Fits in Pipelines

[ View on GitHub ]

fingerprintx: Building a Modern Service Fingerprinting Tool That Actually Fits in Pipelines

Hook

While nmap has been identifying network services since 1997, it was never designed for the world of ephemeral containers, serverless functions, and microservices sprawling across thousands of IP addresses. fingerprintx rethinks service discovery for this reality.

Context

The traditional reconnaissance workflow has remained largely unchanged for decades: scan ports with nmap, identify services with nmap, maybe pipe results to other tools if you're feeling adventurous. This monolithic approach worked fine when you were mapping a corporate network with a few hundred hosts. But modern infrastructure—especially cloud-native environments—demands a different approach. When you're scanning ephemeral Kubernetes pods, auto-scaling EC2 instances, or serverless endpoints that live for minutes, you need tools that do one thing exceptionally well and compose cleanly with others.

Praetorian built fingerprintx to fill this gap: a standalone service fingerprinting utility that assumes ports are already open and focuses exclusively on rapid protocol identification. It's designed to slot into Unix pipelines, accept input from fast port scanners like masscan or Naabu, and output structured data that feeds into downstream security tools. The repository describes it as a "standalone utility for service discovery," but that undersells the architectural philosophy—this is about decomposing the reconnaissance workflow into composable, specialized tools that work together rather than relying on monolithic scanners.

Technical Insight

fingerprintx implements a plugin-based architecture where each protocol gets its own detection module. The tool currently ships with 51 plugins covering everything from SSH and RDP to modern vector databases like ChromaDB and Pinecone. Each plugin knows how to initiate protocol-specific handshakes, parse responses, and extract metadata. This isn't about pattern matching banners—it's about speaking enough of each protocol to confidently identify services.

The core abstraction is elegant. As a Go library, you can import fingerprintx and use it programmatically:

package main

import (
    "fmt"
    "net"
    "time"
    
    "github.com/praetorian-inc/fingerprintx/pkg/plugins"
    "github.com/praetorian-inc/fingerprintx/pkg/scan"
)

func main() {
    target := scan.Target{
        Address: net.ParseIP("192.168.1.100"),
        Port:    8080,
    }
    
    result, err := scan.TargetPort(target, time.Second*5, plugins.Default)
    if err != nil {
        panic(err)
    }
    
    if result != nil {
        fmt.Printf("Service: %s\n", result.Protocol)
        fmt.Printf("Version: %s\n", result.Version)
        fmt.Printf("Metadata: %+v\n", result.Metadata)
    }
}

This API design reveals fingerprintx's real strength: it's a library first, CLI second. You pass a target, timeout, and plugin set, and get back structured results. Want to fingerprint services from your own Go application? Import the library. Building a custom security scanner? Use fingerprintx as a component. The CLI tool is essentially a thin wrapper around this library that handles input parsing and output formatting.

The plugin system is where things get interesting. Each plugin implements a simple interface: given a network connection, attempt a protocol handshake and return what you learned. For HTTP services, fingerprintx sends a GET request and parses headers. For Redis, it sends PING and INFO commands. For SSH, it completes enough of the handshake to extract version banners. The plugins don't just return "HTTP" or "Redis"—they extract versions, server software, configuration details, and other metadata that security teams actually need.

The fast mode implementation demonstrates smart engineering tradeoffs. When you enable --fast, fingerprintx only attempts protocols that typically run on each port. Port 22? Try SSH. Port 443? Try HTTPS. Port 3306? Try MySQL. This leverages the reality that most services run on their default ports, letting you fingerprint thousands of hosts without exhaustive protocol attempts. For large-scale reconnaissance, this 80/20 optimization makes the difference between a ten-minute scan and a ten-hour scan.

The pipeline-native design manifests in practical ways. You can pipe Naabu output directly into fingerprintx:

# Fast port scan with Naabu, then identify services
echo "example.com" | naabu -silent | fingerprintx -o results.json

# Or from a file of targets
cat targets.txt | fingerprintx --json

# Combine with jq for filtering
cat targets.txt | fingerprintx --json | jq '.[] | select(.protocol=="ssh")'

This composability isn't accidental—it's the architectural principle. fingerprintx outputs URI format by default (similar to nmap's open port notation), but supports JSON and CSV for downstream processing. Every design decision reinforces the same philosophy: do service fingerprinting well, output structured data, and get out of the way.

Gotcha

The most important limitation is right in the description: fingerprintx assumes ports are already open. It won't discover which ports are listening—you need a separate port scanner for that. This adds complexity to your workflow. You can't just run fingerprintx example.com and get results; you need to first scan with nmap, masscan, Naabu, or another port scanner, then pipe those results to fingerprintx. For quick ad-hoc reconnaissance, this two-step process feels clunky compared to nmap's all-in-one approach.

The 51-protocol limitation matters more than the number suggests. nmap has over 1,000 service signatures accumulated over 25+ years, including obscure protocols, legacy systems, and proprietary enterprise software. If you're pentesting a network with industrial control systems, mainframe terminals, or custom TCP services, fingerprintx might return nothing where nmap would identify the service. The tool is optimized for modern infrastructure—cloud services, databases, message queues, web services—not legacy enterprise environments. Additionally, while the plugin architecture is elegant, extending it requires writing Go code and understanding the internal APIs. With nmap, you can add service signatures by editing text files. The tradeoff for fingerprintx's speed and clean architecture is less flexibility for edge cases.

Verdict

Use if: You're building automated reconnaissance pipelines for cloud or containerized infrastructure, need service fingerprinting as a component in custom Go tooling, or want to combine fast port scanners with detailed service identification across large IP ranges. fingerprintx excels when you need speed, composability, and modern protocol support. Skip if: You prefer all-in-one scanning tools, need comprehensive detection across legacy or obscure protocols, or primarily work with small networks where nmap's slower but more thorough approach is acceptable. Also skip if you're not comfortable with multi-tool workflows—the requirement for separate port scanning adds friction that isn't worth it for simple use cases.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/praetorian-inc-fingerprintx.svg)](https://starlog.is/api/badge-click/developer-tools/praetorian-inc-fingerprintx)