Repository navigation
Conversation
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
Scanning a vhost on a given server, or an origin behind a CDN, needs the
hostname in the Host header, SNI and template variables while the
connection goes to another ip, and one host often has to be scanned on
several ips. -sa scans the ips DNS returns, and a target of the ip with
a Host header sends no SNI and sets {{Hostname}} to the ip.
A target line can now pin its target to an ip, like curl --resolve:
{"target": "https://app.example", "ip": "192.0.2.10"}
Each target and ip pair is a separate target with its own template
selection. The ip is validated and canonicalized, the target must have
a hostname, -sa and -iv don't resolve a pinned target, and an ip the
network policy denies is skipped. Pinned targets are refused with
-proxy and -automatic-scan.
The engine pins the host of every target with a CustomIP, from a target
line or -sa, with fastdialer.WithResolveOverride on the context each
template runs with. HTTP, network, websocket and javascript templates
then connect to the ip, while redirects to other hosts and the proxy
dial are no longer pinned. HTTP keeps a client per pinned ip, network
and websocket results report the ip, and scheme-less targets are probed
through it.
The host errors cache keys a failed dial of a pinned target by its ip,
also for IDN hosts, and -mup counts the urls of each ip apart, so a url
listed with several ips, or resolved to several with -sa, is kept on
each of them.
go.mod points fastdialer at a fork commit with WithResolveOverride until
a fastdialer release includes it.
mazzz3r
force-pushed
the
feat/target-line-ip
branch
from
October 12, 2026 17:02
1771d42 to
d21749b
Compare
go.mod takes dev's versions, including fastdialer v0.5.25, and keeps fastdialer replaced by the branch of projectdiscovery/fastdialer#580, which now includes v0.5.25.
go.mod takes dev's versions, including fastdialer v0.5.26, and keeps the fastdialer replace.
fastdialer#580 is merged into main, so the fork branch it was on no longer exists. The replace resolves the same commit through the upstream module path github.com/projectdiscovery/fastdialer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Scanning a vhost on a given server, or an origin behind a CDN, needs the hostname in the Host header, SNI and template variables while the connection goes to another ip, and one host often has to be scanned on several ips. nuclei had no input for that: a target of the ip with a
Hostheader sends no SNI and sets{{Hostname}}to the ip, and-sa, which scans the ips DNS returns, didn't reach them correctly.A target line can now pin its target to an ip, like
curl --resolve. Each target and ip pair is a separate target, so the same host can be listed with several ips, and-sanow goes through the same path. Run in a container without network, withapp.testresolving to 127.0.0.1 and 127.0.0.2, a server on each whose title or banner names it, andredir.testredirecting toother.test(127.0.0.3); JSONL fields trimmed toipand the extracted value:dev-lwith two lines pinninghttp://app.test:18080to .1 and .2unknown keys ipip=127.0.0.1 srv=127.0.0.1,ip=127.0.0.2 srv=127.0.0.2-mup 1unknown keys ipapp.test:18301, network templateunknown keys ipip=127.0.0.1 one,ip=127.0.0.2 twonuclei/netunknown keys ipip=127.0.0.1 one,ip=127.0.0.2 two-u http://app.test:18080 -saip=127.0.0.1 srv=127.0.0.1,ip=127.0.0.2 srv=127.0.0.1ip=127.0.0.1 srv=127.0.0.1,ip=127.0.0.2 srv=127.0.0.2-u app.test:18301 -sa, network templateip=127.0.0.2 two, twiceip=127.0.0.1 one,ip=127.0.0.2 two-u http://redir.test:18201 -sa, redirect followedsrv=127.0.0.1 host=other.test:18202srv=127.0.0.3 host=other.test:18202Depends on projectdiscovery/fastdialer#580:
WithResolveOverride, and the dial cache fix without which the second ip of a host is served from the first one's cached dialer.go.modpoints fastdialer at the branch of that PR, which includes v0.5.25, until a fastdialer release includes it; thatreplaceline is the only change togo.mod.Changes
CustomIP, from a target line or-sa, withfastdialer.WithResolveOverrideon the context each template runs with (executeTemplateOnInput). Every protocol that dials through fastdialer connects to the ip: HTTP, network, websocket and javascript. Javascript library calls get it through the template's context, which they receive since cancel javascript library calls with the template execution timeout #7822. Before, only HTTP used the ip, through thefastdialer.IPvalue, which also pinned redirects to other hosts and the dial to an HTTP proxy. HTTP keeps a client per pinned ip, so a keep-alive connection to one ip never serves a request pinned to another. Network and websocket results report the pinned ip, and scheme-less targets are probed through it.ip. It is validated and canonicalized (::ffff:192.0.2.10becomes192.0.2.10; zones and unspecified addresses are rejected), and the target must have a hostname: IP, CIDR and ASN targets are rejected. The registry keys targets by input and ip, so each pair keeps its own template selection.-saand-ivdon't resolve pinned targets. A pinned ip denied by the network policy (-eh,-lna) is skipped with a warning. Pinned targets are refused with-proxy, since the proxy resolves the host itself, and with-automatic-scan, which drops the ip.pkg/input/README.mddocuments the key.{"target": ...}alone runs every template as before, but it no longer turns on per-target profiles or blocks-automatic-scan.pinnedAddresscompares both in ASCII form. Before, failures of pinned targets were recorded under host:port, and an unresponsive pinned ip was never skipped.-mupcounts the urls of each ip apart, withFingerprinter.AcceptOn. Before, a url listed with several ips, or resolved to several with-sa, was scanned on its first ip only under-mup 1; the-sacase happens ondevtoo.targetprofile.ParseLinereturns aLine, andBind/Mergetake a*contextargs.MetaInput; nuclei is their only caller.Headless templates and
unsafe: trueraw requests don't use the ip, and the README says so.Testing
targetprofile: ip parsing and canonicalization, each rejected form, an ip-only line leaving the scan unscoped, and two ips of one host keeping separate selections.list: pinned lines with-saon (no DNS lookup), deduplication of equivalent ips, a pinned ip excluded by-eh, and a url pinned to two ips under-mup 1; the-satest also checks that-mup 1keeps a url on both resolved ips.normalize:AcceptOn. The-muptests fail without the fix.contextargs:MetaInput.Hostname, andWithCustomIPreaching a local listener for a host that doesn't resolve.hosterrorscache: the pinned key mapping, including an IDN host, which fails with the old comparison.protocols/http/target-line-ip.yamlpinspinned.testto 127.0.0.1 and, where it can be bound, 127.0.0.2, and checks that each server answers withHost: pinned.test.TestNetwork/PinnedTargetLinerunsnetwork/basic.yamlon a pinnedpinned.test:port.protocols/javascript/target-line-ip.yamlreads a banner overnuclei/netfrom both ips of a pinnedpinned.testand checks theipof each result. Before the rebase onto cancel javascript library calls with the template execution timeout #7822 it got no result: the library call didn't get the template's context, so it dialedpinned.test, which doesn't resolve.go test -racepasses forpkg/input/...,pkg/core,pkg/protocols/common/contextargs,pkg/protocols/common/hosterrorscache,pkg/protocols/networkandinternal/runner, andgo test ./pkg/protocols/http/passes. In agolang:1.27.1container without network the same runs pass except the interactsh tests ofnetworkandhttpandTestReqURLPattern, which fail the same way ondevthere.go vet -tags=integration ./...is clean;golangci-lint runon the changed packages reports nothing in the changed files; the two issues it reports ininternal/tests/integration(exporters_test.go,runner_test.go) are ondevas well.Feature request: https://github.com/orgs/projectdiscovery/discussions/7862
Checklist