Documentation
¶
Overview ¶
Package asm implements the attack surface discovery pipeline.
A scan runs a sequence of stages against a root domain:
subdomains (subfinder) -> dns (dnsx) -> ipdr (ipinfo.io) -> ports (naabu) -> certs (tlsx)
ipdr, ports and certs work from the hosts the DNS stage resolved. ports is opt-in (Options.PortScan).
Index ¶
- Variables
- func NormalizeDomain(input string) (string, error)
- type CertChange
- type CertDiff
- type DNSRecord
- type Diff
- type FieldChange
- type IPInfo
- type IPInfoChange
- type IPInfoDiff
- type OpenPort
- type Options
- type PortScan
- type ProgressFunc
- type Result
- type Scanner
- type SetDiff
- type Stage
- type Subdomain
- type SubdomainDiff
- type TLSCert
- type Target
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidDomain = errors.New("invalid domain")
ErrInvalidDomain is returned for input that is not a registrable hostname.
Functions ¶
func NormalizeDomain ¶
NormalizeDomain accepts a bare domain or a URL and returns the lowercased hostname, e.g. "https://Example.com/path" -> "example.com".
Types ¶
type CertChange ¶
CertChange is an endpoint (host:port) that presented a different certificate in the newer scan.
type CertDiff ¶
type CertDiff struct {
SetDiff[TLSCert]
Changed []CertChange `json:"changed"`
}
CertDiff also reports endpoints whose certificate was replaced.
type DNSRecord ¶
type DNSRecord struct {
Host string `json:"host"`
Type string `json:"type"`
Value string `json:"value"`
}
DNSRecord is a single resolved record for a host.
type Diff ¶
type Diff struct {
Subdomains SubdomainDiff `json:"subdomains"`
DNSRecords SetDiff[DNSRecord] `json:"dns_records"`
TLSCerts CertDiff `json:"tls_certs"`
IPInfo IPInfoDiff `json:"ip_info"`
}
Diff describes what changed between two scans of the same domain.
type FieldChange ¶
type FieldChange struct {
Field string `json:"field"`
Before string `json:"before"`
After string `json:"after"`
}
FieldChange is one flattened ipinfo field ("asn.name") that changed. An empty Before or After means the field was absent on that side.
type IPInfo ¶
type IPInfo struct {
IP string `json:"ip"`
Data json.RawMessage `json:"data"`
Error string `json:"error,omitempty"`
}
IPInfo is the ipinfo.io record for one IP address. Data is the API response exactly as returned (fields vary by plan); Error is set instead when the lookup failed.
type IPInfoChange ¶
type IPInfoChange struct {
IP string `json:"ip"`
Fields []FieldChange `json:"fields"`
}
IPInfoChange is an IP present in both scans with different ipinfo data.
type IPInfoDiff ¶
type IPInfoDiff struct {
// Compared is false when the previous scan predates IP lookups, so there
// is no baseline and nothing else in the diff is meaningful.
Compared bool `json:"compared"`
SetDiff[IPInfo]
Changed []IPInfoChange `json:"changed"`
}
IPInfoDiff lists IPs that appeared or disappeared and IPs whose ipinfo data changed (new owner, ASN, location...).
type Options ¶
type Options struct {
// SubfinderProviderConfig is the path to a subfinder provider-config.yaml
// holding API keys for passive sources. Optional.
SubfinderProviderConfig string
// SubfinderMaxTime caps passive enumeration time.
SubfinderMaxTime time.Duration
// Resolvers overrides the DNS resolvers used by dnsx ("udp:1.1.1.1:53").
// When empty, dnsx's public resolvers are used, falling back to the
// system resolver if they are unreachable.
Resolvers []string
// DNSConcurrency is the number of hosts resolved in parallel.
DNSConcurrency int
// TLSConcurrency is the number of hosts probed for TLS in parallel.
TLSConcurrency int
// TLSPorts are the ports probed for certificates.
TLSPorts []int
// TLSTimeout is the per-connection timeout.
TLSTimeout time.Duration
// IPInfoToken authenticates ipinfo.io lookups. Optional: without it the
// free tier's lower rate limits apply.
IPInfoToken string
// IPInfoBaseURL defaults to https://ipinfo.io.
IPInfoBaseURL string
// IPInfoConcurrency is the number of ipinfo lookups in flight.
IPInfoConcurrency int
// HTTPClient is used for ipinfo lookups.
HTTPClient *http.Client
// PortScan enables the naabu port-scanning stage. A full SYN scan of all
// TCP ports is heavy and needs raw-socket privileges, so it is opt-in.
PortScan bool
// SkipIPDR disables the IPDR stage (IP data records via ipinfo.io).
SkipIPDR bool
// SkipCerts disables the certificate stage (TLS inspection via tlsx).
SkipCerts bool
// PortScanType selects the naabu scan mode: "syn" (half-open, needs raw
// sockets, libpcap and a routable interface) or "connect" (full TCP
// connect, works through NAT such as a Docker bridge / WSL2). Defaults to
// "syn".
PortScanType string
// PortScanPorts is the naabu port spec; "-" means all 65535 TCP ports.
PortScanPorts string
// PortScanRate is packets per second.
PortScanRate int
// PortScanRetries is the per-port retry count.
PortScanRetries int
// PortScanThreads is naabu's worker count.
PortScanThreads int
// PortScanTimeout is the per-port timeout.
PortScanTimeout time.Duration
// Logger receives stage diagnostics. Defaults to slog.Default().
Logger *slog.Logger
}
Options tunes the scanner. Zero values fall back to defaults.
type PortScan ¶
type PortScan struct {
IP string `json:"ip"`
Hosts []string `json:"hosts"`
Ports []OpenPort `json:"ports"`
}
PortScan is the set of open ports found on one IPv4 address, with the hosts that resolved to it.
type ProgressFunc ¶
type ProgressFunc func(Stage)
ProgressFunc is called when the scan enters a new stage.
type Result ¶
type Result struct {
Domain string `json:"domain"`
Subdomains []Subdomain `json:"subdomains"`
DNSRecords []DNSRecord `json:"dns_records"`
TLSCerts []TLSCert `json:"tls_certs"`
IPInfo []IPInfo `json:"ip_info"`
PortScans []PortScan `json:"port_scans"`
}
Result is everything discovered for a domain.
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner runs the discovery pipeline. It is safe for concurrent use.
type SetDiff ¶
type SetDiff[T any] struct { Added []T `json:"added"` Removed []T `json:"removed"` }
SetDiff lists items present in only one of two scans.
type Subdomain ¶
type Subdomain struct {
Host string `json:"host"`
Sources []string `json:"sources"`
// CarriedOver is true when no source reported the host in this scan and
// it was kept from an earlier one (subdomains are append-only).
CarriedOver bool `json:"carried_over"`
}
Subdomain is a hostname discovered by passive enumeration.
type SubdomainDiff ¶
type SubdomainDiff struct {
SetDiff[Subdomain]
NoLongerReported []Subdomain `json:"no_longer_reported"`
}
SubdomainDiff also reports hosts that sources stopped returning. Since subdomains are append-only these stay in the scan as carried over, so they don't show up as Removed.
type TLSCert ¶
type TLSCert struct {
Host string `json:"host"`
IP string `json:"ip"`
Port int `json:"port"`
TLSVersion string `json:"tls_version"`
Cipher string `json:"cipher"`
SubjectCN string `json:"subject_cn"`
SubjectOrg []string `json:"subject_org"`
SANs []string `json:"sans"`
IssuerCN string `json:"issuer_cn"`
IssuerOrg []string `json:"issuer_org"`
Serial string `json:"serial"`
NotBefore time.Time `json:"not_before"`
NotAfter time.Time `json:"not_after"`
Expired bool `json:"expired"`
SelfSigned bool `json:"self_signed"`
Mismatched bool `json:"mismatched"`
Wildcard bool `json:"wildcard"`
FingerprintSHA256 string `json:"fingerprint_sha256"`
}
TLSCert is the leaf certificate presented by a host.
type Target ¶
type Target struct {
Domain string
// KnownSubdomains are subdomains found by earlier scans of Domain. They
// are kept in the result even if passive sources no longer report them,
// and still go through DNS and TLS so their current state is captured.
KnownSubdomains []Subdomain
}
Target is what a scan runs against.