Add recon upload hunter and admin surface probing for owned-target scans.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Extend web crawl with multipart/drag-drop/JS upload ranking and probe common admin paths for 200 vs 401/403 signals in scan JSON.
This commit is contained in:
AetherForge
2026-06-07 12:21:40 -07:00
parent 1fd3ec8618
commit 251bdfa1ac
5 changed files with 191 additions and 60 deletions

View File

@@ -162,6 +162,116 @@ func TestNormalizeOwnedHostRejectsEmpty(t *testing.T) {
}
}
func TestUploadHunterMultipartAndJS(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/":
w.Write([]byte(`<html><body><form action="/api/upload" enctype="multipart/form-data"><input type="file"></form><script src="/static/upload.js"></script></body></html>`))
case "/static/upload.js":
w.Write([]byte(`fetch("/api/upload")`))
case "/api/upload":
w.WriteHeader(http.StatusOK)
default:
http.NotFound(w, r)
}
}))
defer srv.Close()
u, _ := url.Parse(srv.URL)
port := 80
if p := u.Port(); p != "" {
port = atoi(p)
}
SetFetchPageHook(func(rawURL string) (int, string, error) {
resp, err := http.Get(rawURL)
if err != nil {
return 0, "", err
}
defer resp.Body.Close()
body, _ := readBodyLimited(resp.Body, maxHTMLBytes)
return resp.StatusCode, body, nil
})
t.Cleanup(func() { SetFetchPageHook(nil) })
report, err := Crawl(u.Hostname(), port, u.Scheme, []string{"/"})
if err != nil || len(report.UploadHunter) == 0 {
t.Fatalf("err=%v hunter=%+v", err, report.UploadHunter)
}
}
func TestProbeAdminSurfaceSignals(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/admin", "/admin/":
w.WriteHeader(http.StatusForbidden)
case "/swagger", "/swagger/":
w.WriteHeader(http.StatusOK)
default:
http.NotFound(w, r)
}
}))
defer srv.Close()
u, _ := url.Parse(srv.URL)
port := 80
if p := u.Port(); p != "" {
port = atoi(p)
}
SetFetchPageHook(func(rawURL string) (int, string, error) {
resp, err := http.Get(rawURL)
if err != nil {
return 0, "", err
}
defer resp.Body.Close()
body, _ := readBodyLimited(resp.Body, maxHTMLBytes)
return resp.StatusCode, body, nil
})
t.Cleanup(func() { SetFetchPageHook(nil) })
if len(ProbeAdminSurface(u.Hostname(), port, u.Scheme)) == 0 {
t.Fatal("expected admin surface")
}
}
func TestScanReportIncludesAdminSurfaceJSON(t *testing.T) {
SetPortDialHook(func(host string, port int, _ time.Duration) bool { return port == 80 })
t.Cleanup(func() { SetPortDialHook(nil) })
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/":
w.Write([]byte(`<html><form enctype="multipart/form-data" action="/api/upload"><input type="file"></form></html>`))
case "/api/upload":
w.WriteHeader(http.StatusOK)
case "/admin", "/admin/":
w.WriteHeader(http.StatusForbidden)
default:
http.NotFound(w, r)
}
}))
defer srv.Close()
u, _ := url.Parse(srv.URL)
port := 80
if p := u.Port(); p != "" {
port = atoi(p)
}
SetFetchPageHook(func(rawURL string) (int, string, error) {
resp, err := http.Get(rawURL)
if err != nil {
return 0, "", err
}
defer resp.Body.Close()
body, _ := readBodyLimited(resp.Body, maxHTMLBytes)
return resp.StatusCode, body, nil
})
t.Cleanup(func() { SetFetchPageHook(nil) })
report, err := Scan(ScanRequest{Host: u.Hostname(), Port: port, Scheme: u.Scheme})
if err != nil {
t.Fatal(err)
}
raw, _ := json.Marshal(report)
s := string(raw)
if !strings.Contains(s, `"admin_surface"`) || !strings.Contains(s, `"upload_hunter"`) {
t.Fatalf("json=%s", s)
}
}
func containsStr(list []string, want string) bool {
for _, s := range list {
if s == want {