paths was typed as a map of operation structs, so a spec with a $ref
string path item (valid in openapi 3.1) failed to decode and the whole
spec was dropped with no finding. decode path items into a generic tree
and extract per-operation security manually, treating a non-array
security value as absent so it inherits the global default rather than
being reported as an anonymous endpoint.
* feat(modules): tcp module executor
dial+probe+banner with word/regex/size matchers, ctx-honest watcher,
port required.
* feat(modules): add redis unauthenticated access tcp module
first shipped tcp module: sends INFO to redis (6379) and matches
redis_version in the reply, which an auth-required server never returns
(it answers -NOAUTH). extracts the leaked version.
* docs(modules): document the tcp module type
flip the http-only note and add a tcp config section (port, data,
word/regex/size matchers and regex extractors against the banner),
pointing at the redis demo module.
* feat(modules): support matchers-condition in tcp modules
tcp matchers were and-only; add the matchers-condition key (and default,
or) mirroring the http executor's checkMatchers, validated at load via the
shared validateMatchersCondition. documents the override in the combining
matchers section.
* feat(modules): decode escape sequences in tcp data payloads
decode C-style escapes (\\ \a \b \f \n \r \t \v \xHH) in a tcp module's
data value before it goes on the wire, so a single-quoted or plain yaml
scalar reaches the service with the same control bytes a double-quoted
one already would. an unrecognized escape is kept verbatim so no bytes
are silently lost.
* feat(modules): add tcp recon modules for common services
add five built-in tcp modules alongside the redis one so the executor
ships a real service set: memcached (unauth version probe), ssh and ftp
(passive banner version disclosure), mysql/mariadb (handshake exposure,
matched with matchers-condition: or across both auth plugin names), and
vnc (rfb handshake exposure). each fingerprint tracks the documented
on-wire protocol and pulls the version into an extractor.
* feat(modules): add mail and rsync tcp recon modules
add four more passive-banner tcp modules: smtp, pop3, and imap read the
greeting each mail service sends on connect (the smtp matcher requires
an esmtp/smtp marker so it does not fire on a bare 220 ftp greeting),
and rsync detects an exposed daemon by its @RSYNCD handshake. each pulls
the banner or protocol version into an extractor.
pointsAtSentinel parsed the reflected location without the whitespace
and control-byte removal a browser applies, so payloads like a
space-prefixed scheme-relative host or a tab embedded in the scheme
parsed to an empty host and a real off-site redirect was missed. strip
leading and trailing c0-or-space and all tab, cr and lf before parsing,
matching the navigation-time normalization, without removing mid-string
spaces so a same-site target is never reclassified as off-site.
the http scheme downgrade origin was queued against every target, so on
a plain-http target the http://host probe is the target's own origin and
any correct same-origin reflection was reported as a high-severity
downgrade. mark that origin https-only and skip it when the target
scheme is not https, where a downgrade is meaningless, while still
firing it on https targets that genuinely trust the http origin.
checkDatabaseErrors matched db error regexes against each response with
no baseline, so a site serving a shared error template, custom 500 or
waf block page containing a db error string reported an identical
high-severity sql injection finding on every probed path. capture the
main-url response as a baseline and skip, per pattern, any error string
already present in it while still checking other patterns, so only
differential probe-caused errors are reported.
the lfi scan matched evidence regexes against each response with no
baseline, so a page whose static content matched a pattern (a blog
containing an /etc/passwd-shaped line, a code sample with <?php) was
reported as an lfi finding for every parameter and payload combination.
take one baseline get of the unmodified target and exclude, per
evidence class, any pattern already present in it, so only
payload-caused disclosures are counted.
* feat(modules): add haproxy stats, nexus and pushgateway exposure modules
* chore(modules): trim redundant module header comments
drop the top-line comments on the new infra exposure modules that
just restated the id/name fields already in the info block
* feat(modules): add chartmuseum and verdaccio registry exposure modules
* chore(modules): trim redundant module header comments
drop the top-line comments on the new package registry exposure
modules that just restated the id/name fields already in the info block
* feat(modules): add tfvars, ansible-vault and ci config exposure
* chore(modules): trim redundant module header comments
drop the top-line comments on the new iac config exposure modules that
just restated the id/name fields already in the info block
* feat(modules): add guacamole and webmin login exposure modules
* chore(modules): trim redundant module header comments
drop the leading comment restating id/name on guacamole-login-exposure and webmin-login-exposure
* feat(modules): add nsq and pulsar exposure modules
* chore(modules): trim redundant module header comments
drop the leading comment restating id/name on nsq-stats-exposure and pulsar-metrics-exposure
detect a reachable postfixadmin setup.php (unhardened-install signal
and info disclosure, not an auth bypass since superadmin creation is
gated on a locally configured setup password) and the login panel.
markers and the setup-password form field and login field pair with
the brand string so prose mentions do not match.
fingerprint roundcube via its login-page body markers and zimbra via
its web client markers, both anded with status 200 and prose-trap
negatives to avoid matching pages that merely mention the products.
detect an anonymously reachable kubeflow pipelines apiserver, whose
same api accepts pipeline run submission with an attacker-supplied
workflow manifest (arbitrary container execution), and a metaflow
metadata service leaking flow and owner enumeration. match distinctive
snake_case json keys with status 200, fail closed on empty instances
to avoid the bare-substring false-positive class.
detect anonymously reachable localai /system (backend and loaded-model
disclosure) and invokeai /api/v1/app/runtime_config (host path and
config disclosure). both and json key markers with status 200 and
include secured-instance negatives so 401/403 boxes are not flagged.
require actual autoindex markup (parent-directory anchors, sort links,
bracketed parent link) instead of loose title text, so prose pages
titled "Index of /..." and iis tutorials no longer false-positive.
apache non-fancy parent-directory anchor tolerates surrounding
whitespace to avoid a false negative.
the live-response path flagged any host whose body matched a provider
fingerprint without checking dns, so a page merely containing a
provider 404 string was reported as a takeover. resolve the cname and
only confirm when it maps to the same provider; a contradicting cname
kills the flag. providers with a body signature but no apex to
correlate, and unresolvable lookups, degrade to a medium potential
finding via a new confidence field rather than confirming or dropping.
Detect an exposed Laravel Telescope dashboard, which reveals requests, queries,
exceptions and dumped variables. Telescope aborts with 403 before render when
gated, so a 200 carrying both the Telescope title and the Vue mount id proves
the gate is open. The and-condition on the mount id keeps an unrelated page that
merely names telescope, or a soft-404, from matching.
Detect unauthenticated web database browsers, which ship with no default auth
and hand over a full data browser once reachable. Each keys on an app-specific
anchor (the ME_SETTINGS global, the redisCommander.js script, ace-pgsql.js)
under an and-condition so a branded soft-404 cannot trip it. mongo-express also
requires the database-list heading, since its login form renders the same
settings global, title and logo but serves no data.
A module that fingerprints a redirect itself (open-redirect proofs, a Location
header, a 3xx status) could not see the redirect: the executor followed 3xx to
the final response before matchers ran, with no way to stop. Add an opt-in
disable-redirects field that scopes an ErrUseLastResponse policy to the module's
own client copy, so the shared httpx transport keeps following redirects for
every other module.
Crawl only capped recursion depth, not breadth per level. A link-heavy page
(pagination, faceted search) drives fetch count toward branching^depth with no
ceiling: a depth-3 crawl of a page linking 40 fresh urls issued 1641 real
fetches.
Cap total fetches at a page budget via an atomic counter in an OnRequest hook
that aborts once exceeded, and add a Truncated flag on CrawlResult so callers
can tell a run was cut short.
* refactor(cli): extract per-target scan into scanTarget
pull the target loop body into scanTarget, which returns a targetScan
holding that target's findings, report rows, scan labels and log files
instead of appending onto run-wide slices. the run loop merges those in
target order and finishRun handles the post-loop notify/silent/report/
summary steps. behavior is identical for the sequential run today; the
isolation is the seam a bounded worker pool needs next.
* feat(cli): scan targets in parallel with -concurrency
add -concurrency N (default 1) to run the per-target scan pool over
several targets at once. scanTarget already returns isolated
accumulators, so workers share only the console: output.SetConcurrent
serializes sink writes and de-animates spinners/progress so parallel
lines stay whole. results merge in input order, and concurrency 1 keeps
the sequential path unchanged.
add recon modules for container and proxy control planes that answer without authentication: traefik serves its full routing config at /api/overview when the api is enabled, nomad dumps the agent config at /v1/agent/self when acls are disabled (403 otherwise), and portainer discloses its version and instance id at the public /api/status.
add recon modules for unauthenticated local inference servers that leak model inventory without a key: ollama, koboldcpp, text-generation-webui (oobabooga), and tabby.
add recon modules for unauthenticated inference servers that expose an openai-style /v1/models inventory without a key: vllm, llama.cpp, lm studio, and infinity.
add recon modules for self-hosted databases whose http interface is reachable
without credentials: clickhouse runs arbitrary sql because the default user has
an empty password, confirmed here by reading the server version through the
http interface, and the open-source dgraph alpha has no authentication so its
/health endpoint discloses the cluster while /query and /admin read and drop
all data; a clickhouse that requires a password returns 403 and an alpha behind
an authenticating proxy returns 401 and neither is flagged.
add recon modules for self-hosted agent builders and compute
orchestration. ray exposes an unauthenticated compute dashboard whose
job api allows code execution, skypilot exposes an open cloud and
kubernetes control plane when basic auth is disabled, dify flags a
console that allows open registration, and langflow fingerprints a
reachable instance over its public version api.
the shared transport paced outbound requests but ignored a server
asking it to slow down, so a 429 or 503 came straight back as a failed
probe. back off and retry both through the one chokepoint every scanner
shares, honoring Retry-After (delta-seconds or http-date) and falling
back to capped exponential backoff.
gated behind -max-retries (default 2, 0 disables). bodyless GETs and
GetBody-backed requests replay safely; anything else is returned as-is.
add two recon modules detecting anon-by-design service registries that leak the internal service map:
- eureka-registry-exposure (medium): probes /eureka/apps and matches the registry envelope markers apps__hashcode and versions__delta (the EurekaJacksonCodec formats them with a default '_' -> '__' replacement, so they appear verbatim in both the xml and json forms); eureka has no auth, so an open registry discloses every instance's internal hostname, ip and ports and accepts rogue registrations, while a spring-security-secured eureka returns 401; extracts the first instance ip from either form
- spring-boot-admin-exposure (medium): probes /instances and matches the registration/healthUrl/statusInfo shape; the codecentric server ships no security, so an open one discloses every registered app's internal management and health urls (a pivot to their actuators), while a secured one returns 401 or redirects to login; extracts the first internal health url
both modules carry hard-test coverage: eureka is proven on both its xml and json forms, with an N-1 trap (an apps__hashcode-less envelope stays quiet) and a prose trap (a page that merely mentions the word applications stays quiet); sba has an N-1 statusInfo trap; both have 401 secured-instance cases and cross false-positive guards both ways.
add two recon modules detecting anon-by-design big-data control surfaces that leak operational topology:
- zookeeper-admin-exposure (medium): probes /commands/monitor on a zookeeper adminserver and matches the command/monitor envelope plus server_state; the adminserver has no auth on reads, so an open one discloses the version, ensemble role, connection counts and data size, while a firewalled or disabled adminserver is not reachable; extracts the version
- hadoop-namenode-exposure (medium): probes /jmx for the NameNodeInfo bean and matches the bean name plus LiveNodes/DeadNodes; the namenode jmx is unauthenticated unless kerberos spnego is set, so an open one discloses the hdfs version and every datanode internal hostname, while a kerberos-secured namenode returns 401; extracts the software version
trino/presto were dropped: /v1/info is ResourceSecurity(PUBLIC) so it answers on secured clusters too and would be a fingerprint, not an exposure.
both modules carry hard-test coverage with N-1 anchor traps (a non-monitor zookeeper command and a non-NameNodeInfo hadoop bean stay quiet), cross false-positive guards both ways and a secured-instance status case.
add two recon modules detecting exposed kafka management consoles that ship without authentication by default:
- kafka-ui-exposure (high): probes /api/clusters on a kafbat/provectus kafka ui and matches the kafka-ui-specific defaultCluster/brokerCount keys plus the online/offline/initializing status enum; an open instance leaks the cluster topology and lets an unauthenticated session browse and produce records, while a login- or oauth-protected instance returns 401 and is not flagged
- kafdrop-exposure (high): probes / with an Accept: application/json header and matches the ClusterInfoVO summary/preferredReplicaPercent/brokers/topics shape; an open instance discloses broker hostnames, topics and messages, while an instance with the optional basic auth returns 401
both modules carry hard-test coverage with cross false-positive guards both ways, non-enum-status and missing-key negative cases, a wire-level check that the kafdrop module sends the Accept header, and 401 secured-instance cases
add a recon module for an n8n instance that serves /rest/settings without
authentication; the endpoint exists so the frontend can load but it discloses
the webhook url structure, instance id, release channel, configured
authentication method and whether owner setup is still pending, which aids
reconnaissance and can indicate a claimable instance; it is unauthenticated by
design so this is reported as information disclosure.
add a recon module for a grafana with anonymous access enabled: by default
grafana requires a login and /api/search returns 401, but when anonymous
access is turned on the endpoint lists every dashboard without credentials,
exposing the internal metrics, hostnames and queries they contain, and the same
anonymous session can reach backend data sources through the data source proxy;
a grafana that requires login returns 401 and is not flagged.
add recon modules for self-hosted automation servers whose api is reachable
without credentials when misconfigured: jenkins /api/json answers when
anonymous read access is enabled and lists every job, view and node, and
apache nifi /nifi-api/flow/about answers when the instance runs without
security; each open instance reaches a script console or processor that runs
arbitrary code, while a jenkins without anonymous read returns 403 and a
secured nifi returns 401 and neither is flagged.
add a recon module for an unauthenticated prometheus alertmanager: the
/api/v2/status endpoint answers without credentials and returns config.original,
the full running configuration, which discloses receiver integrations and any
embedded credentials such as slack, pagerduty and webhook urls and smtp
passwords, along with the build version and cluster peers; an instance behind an
authenticating proxy returns 401 and is not flagged.
add recon modules for self-hosted browser-automation grids that do not
authenticate their status endpoint: selenium grid /status discloses every
registered node with its os and browser slots, and selenoid /status discloses
the running sessions, capacity and browser images; each open grid also lets
anyone create a session that drives a real browser to arbitrary internal urls,
while a grid behind an authenticating proxy returns 401 and is not flagged.
add recon modules for self-hosted background-job dashboards that ship no
authentication of their own and rely on the hosting application to protect
them: sidekiq web /sidekiq/stats discloses the redis server internals and the
job queue, celery flower /api/workers (reachable only when its api is
deliberately opened) discloses every worker's broker config and registered
tasks, and rq-dashboard /0/data/queues.json discloses the redis-backed queue
names and job counts; each open instance also allows killing, retrying or
deleting jobs, while a deployment protected by the application returns a
redirect or 401 and is not flagged.
add recon modules for self-hosted data-orchestration webservers that ship no built-in authentication: dagster /server_info discloses the webserver and core versions, and mage /api/status discloses the scheduler status and server repository path; both reach an editor or graphql api on the same instance that can execute arbitrary code.