diff --git a/CHANGELOG.txt b/CHANGELOG.txt
@@ -1,5 +1,6 @@
## v0.0.7
+- fix(net): probe https then fall back to http, http-only hosts were skipped ~ @djnnvx
- fix(scan): open ports nerva cannot fingerprint no longer vanish from portscan.json ~ @djnnvx
- chore(deps): bump dependencies ~ @djnnvx
- refactor(core): dead code deletion, simplification ~ @djnnvx
diff --git a/helpers/net.go b/helpers/net.go
@@ -91,6 +91,25 @@ func CheckProxy(proxy string) {
}
}
+// ResolveWebTarget probes https first and falls back to http, so a host that
+// only serves plaintext is still scanned instead of skipped.
+func ResolveWebTarget(domain string, forceInsecure bool) (string, bool) {
+ return resolveWebTarget(domain, forceInsecure, HasUnavailableWebInterface)
+}
+
+func resolveWebTarget(domain string, forceInsecure bool, unavailable func(string) bool) (string, bool) {
+ schemes := []string{"https://", "http://"}
+ if forceInsecure {
+ schemes = []string{"http://"}
+ }
+ for _, scheme := range schemes {
+ if target := scheme + domain; !unavailable(target) {
+ return target, true
+ }
+ }
+ return "", false
+}
+
func HasUnavailableWebInterface(url string) bool {
url = strings.TrimSuffix(url, "/")
diff --git a/helpers/net_test.go b/helpers/net_test.go
@@ -0,0 +1,55 @@
+package helper
+
+import "testing"
+
+func TestResolveWebTargetFallsBackToHTTP(t *testing.T) {
+ probed := []string{}
+ unavailable := func(url string) bool {
+ probed = append(probed, url)
+ return url != "http://example.com"
+ }
+
+ got, ok := resolveWebTarget("example.com", false, unavailable)
+ if !ok {
+ t.Fatalf("host serving plaintext was skipped; probed %v", probed)
+ }
+ if got != "http://example.com" {
+ t.Errorf("got %q, want http://example.com", got)
+ }
+ if len(probed) != 2 || probed[0] != "https://example.com" {
+ t.Errorf("want https probed first then http, got %v", probed)
+ }
+}
+
+func TestResolveWebTargetPrefersHTTPS(t *testing.T) {
+ unavailable := func(string) bool { return false }
+
+ got, ok := resolveWebTarget("example.com", false, unavailable)
+ if !ok || got != "https://example.com" {
+ t.Errorf("got %q (ok=%v), want https://example.com", got, ok)
+ }
+}
+
+func TestResolveWebTargetForceInsecureSkipsHTTPS(t *testing.T) {
+ probed := []string{}
+ unavailable := func(url string) bool {
+ probed = append(probed, url)
+ return false
+ }
+
+ got, ok := resolveWebTarget("example.com", true, unavailable)
+ if !ok || got != "http://example.com" {
+ t.Errorf("got %q (ok=%v), want http://example.com", got, ok)
+ }
+ if len(probed) != 1 {
+ t.Errorf("force-insecure must not probe https, got %v", probed)
+ }
+}
+
+func TestResolveWebTargetDeadHost(t *testing.T) {
+ unavailable := func(string) bool { return true }
+
+ if got, ok := resolveWebTarget("example.com", false, unavailable); ok {
+ t.Errorf("dead host reported alive as %q", got)
+ }
+}
diff --git a/prune/run.go b/prune/run.go
@@ -27,13 +27,9 @@ func (opts *PruneOpts) Run() {
for scanner.Scan() {
targetDomain := scanner.Text()
- httpAddr := "https://" + targetDomain
- if opts.ForceInsecure {
- httpAddr = "http://" + targetDomain
- }
- if !helper.HasUnavailableWebInterface(httpAddr) {
- fmt.Printf("[+] %s is alive !\n", targetDomain)
+ if target, ok := helper.ResolveWebTarget(targetDomain, opts.ForceInsecure); ok {
+ fmt.Printf("[+] %s is alive (%s)!\n", targetDomain, target)
results = append(results, targetDomain)
}
}
diff --git a/scan/fingerprint.go b/scan/fingerprint.go
@@ -18,12 +18,8 @@ import (
func (opts *ScanOpts) Fingerprint() {
fmt.Printf("\n[SCAN] domain: %s\n", opts.Domain)
- fullDomain := "https://" + opts.Domain
- if opts.ForceInsecure {
- fullDomain = "http://" + opts.Domain
- }
-
- if helper.HasUnavailableWebInterface(fullDomain) {
+ fullDomain, ok := helper.ResolveWebTarget(opts.Domain, opts.ForceInsecure)
+ if !ok {
fmt.Printf("[FINGERPRINT %s] => no web panel online. skipping\n", opts.Domain)
return
}
diff --git a/scan/run.go b/scan/run.go
@@ -58,21 +58,17 @@ func (opts *ScanOpts) webModules() []core.Module {
func (opts *ScanOpts) Run() {
fmt.Printf("\n[SCAN] domain: %s\n", opts.Domain)
- fullDomain := "https://" + opts.Domain
- if opts.ForceInsecure {
- fullDomain = "http://" + opts.Domain
- }
-
if opts.PortScan {
opts.runPortScan()
}
- if helper.HasUnavailableWebInterface(fullDomain) {
+ fullDomain, ok := helper.ResolveWebTarget(opts.Domain, opts.ForceInsecure)
+ if !ok {
fmt.Printf("[SCAN %s] => no web panel online. skipping\n", opts.Domain)
return
}
- fmt.Printf("[SCAN %s] web panel online....running web scans\n", opts.Domain)
+ fmt.Printf("[SCAN %s] web panel online (%s)....running web scans\n", opts.Domain, fullDomain)
/* per-domain output dir; setting opts.ScanPath directly would nest dirs */
pathForDomain := opts.ScanPath + "/" + opts.Domain
| | | | | |