From b21fb489ee628216153735da7a5628e40f151c40 Mon Sep 17 00:00:00 2001 From: godfuzz3r Date: Thu, 9 Oct 2025 09:57:12 +0300 Subject: [PATCH 1/2] for `file` scan ## at the end of each input line will be interpreted as ipaddress --- pkg/readers/file.go | 27 ++++++++++++++++++++++-- pkg/runner/drivers/chromedp.go | 38 +++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/pkg/readers/file.go b/pkg/readers/file.go index 133f0083..466af69c 100644 --- a/pkg/readers/file.go +++ b/pkg/readers/file.go @@ -3,6 +3,7 @@ package readers import ( "bufio" "fmt" + "net" "net/url" "os" "strconv" @@ -120,7 +121,7 @@ func (fr *FileReader) urlsFor(candidate string, ports []int) []string { if hasScheme && hasPort { // return the candidate as is - urls = append(urls, parsedURL.String()) + urls = append(urls, decodeLastHashtag(parsedURL.String())) return urls } @@ -168,7 +169,7 @@ func (fr *FileReader) urlsFor(candidate string, ports []int) []string { Path: parsedURL.Path, } - urls = append(urls, fullURL.String()) + urls = append(urls, decodeLastHashtag(fullURL.String())) } } @@ -197,3 +198,25 @@ func (fr *FileReader) ports() []int { func isIPv6(hostname string) bool { return len(hostname) > 0 && hostname[0] == '[' && hostname[len(hostname)-1] == ']' } + +// it's for vhosts screenshooting. ## may be encoded +func decodeLastHashtag(url string) string { + i := strings.LastIndex(url, "%23%23") + pad := 0 + if i == -1 { + i = strings.LastIndex(url, "#%23") + pad = 4 + } else { + pad = 6 + } + if i != -1 { + _url := url[:i] + "##" + url[i+pad:] + ipaddrIndex := strings.LastIndex(_url, "##") + 2 + ipaddr := net.ParseIP(_url[ipaddrIndex:]) + if ipaddr != nil { + return _url + } + } + + return url +} diff --git a/pkg/runner/drivers/chromedp.go b/pkg/runner/drivers/chromedp.go index b6bf2d25..fcc3271e 100644 --- a/pkg/runner/drivers/chromedp.go +++ b/pkg/runner/drivers/chromedp.go @@ -8,6 +8,8 @@ import ( "fmt" "image" "log/slog" + "net" + "net/url" "os" "os/exec" "path/filepath" @@ -57,7 +59,7 @@ func (b *browserInstance) Close() { // see Witness for more information on why we're explicitly not using tabs // (to do that we would alloc in the NewChromedp function and make sure that // we have the browser started with chromedp.Run(browserCtx)). -func getChromedpAllocator(opts runner.Options) (*browserInstance, error) { +func getChromedpAllocator(opts runner.Options, target string) (*browserInstance, error) { var ( allocCtx context.Context allocCancel context.CancelFunc @@ -96,6 +98,22 @@ func getChromedpAllocator(opts runner.Options) (*browserInstance, error) { allocOpts = append(allocOpts, chromedp.ExecPath(opts.Chrome.Path)) } + // Use specific ip address for domain if it's included and addres is hostname + if strings.Contains(target, "##") { + targetUri, _ := url.ParseRequestURI(target) + + ipaddrIndex := strings.LastIndex(target, "##") + 2 + ipaddr := net.ParseIP(target[ipaddrIndex:]) + + // ignore ipaddr from hashtag if hostname is already an ip address + if ipaddr != nil && net.ParseIP(targetUri.Host) == nil { + allocOpts = append(allocOpts, + chromedp.Flag("host-resolver-rules", fmt.Sprintf("MAP %s %s", targetUri.Hostname(), ipaddr)), + chromedp.Flag("host-rules", fmt.Sprintf("MAP %s %s", targetUri.Hostname(), ipaddr)), + ) + } + } + allocCtx, allocCancel = chromedp.NewExecAllocator(context.Background(), allocOpts...) } else { @@ -128,7 +146,7 @@ func (run *Chromedp) Witness(target string, thisRunner *runner.Runner) (*models. // a resources thing I guess with a parent browser process? so, using this // driver now means the resource usage will be higher, but, your accuracy // will also be amazing. - allocator, err := getChromedpAllocator(run.options) + allocator, err := getChromedpAllocator(run.options, target) if err != nil { return nil, err } @@ -339,7 +357,7 @@ func (run *Chromedp) Witness(target string, thisRunner *runner.Runner) (*models. // navigate to the target if err := chromedp.Run( - navigationCtx, chromedp.Navigate(target), + navigationCtx, chromedp.Navigate(stripHashtagIp(target)), ); err != nil && err != context.DeadlineExceeded { return nil, fmt.Errorf("could not navigate to target: %w", err) } @@ -474,3 +492,17 @@ func (run *Chromedp) Witness(target string, thisRunner *runner.Runner) (*models. func (run *Chromedp) Close() { run.log.Debug("closing browser allocation context") } + +// removes last ## with ipaddress +func stripHashtagIp(target string) string { + if strings.Contains(target, "##") { + i := strings.LastIndex(target, "##") + ipaddr := net.ParseIP(target[i+2:]) + + // ignore ipaddr from hashtag if hostname is already an ip address + if ipaddr != nil { + return target[:i] + } + } + return target +} From b0622859c6a158d0074c4047a0bf46bcbf3523ab Mon Sep 17 00:00:00 2001 From: godfuzz3r Date: Thu, 9 Oct 2025 10:16:56 +0300 Subject: [PATCH 2/2] add Fragment to url when no port passed --- pkg/readers/file.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/readers/file.go b/pkg/readers/file.go index 466af69c..89c47c3e 100644 --- a/pkg/readers/file.go +++ b/pkg/readers/file.go @@ -164,9 +164,10 @@ func (fr *FileReader) urlsFor(candidate string, ports []int) []string { } fullURL := url.URL{ - Scheme: scheme, - Host: host, - Path: parsedURL.Path, + Scheme: scheme, + Host: host, + Path: parsedURL.Path, + Fragment: parsedURL.Fragment, } urls = append(urls, decodeLastHashtag(fullURL.String()))