Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Score crawl inputs by SSRF-prone field names and register ping-back canaries for operator paste confirmation.
76 lines
3.0 KiB
Go
76 lines
3.0 KiB
Go
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
|
|
|
|
"crypto-miner-server/internal/db"
|
|
|
|
"crypto-miner-server/internal/recon"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestReconScanEndpoint(t *testing.T) {
|
|
|
|
recon.SetPortDialHook(func(host string, port int, _ time.Duration) bool { return port == 80 })
|
|
|
|
t.Cleanup(func() { recon.SetPortDialHook(nil) })
|
|
|
|
recon.SetFetchPageHook(func(rawURL string) (int, string, error) {
|
|
|
|
return 200, `<html><input name="preview_url">`, nil
|
|
|
|
})
|
|
|
|
t.Cleanup(func() { recon.SetFetchPageHook(nil) })
|
|
|
|
database, _ := db.New(t.TempDir())
|
|
|
|
t.Cleanup(func() { _ = database.Close() })
|
|
|
|
h := NewReconHandler(database, nil)
|
|
|
|
body, _ := json.Marshal(map[string]interface{}{"host": "recon.lab", "port": 80, "scheme": "http"})
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.Scan(w, httptest.NewRequest(http.MethodPost, "/api/v1/recon/scan", bytes.NewReader(body)))
|
|
|
|
if w.Code != http.StatusOK {
|
|
|
|
t.Fatalf("%d %s", w.Code, w.Body.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReconSSRfCanaryFlow(t *testing.T) {
|
|
|
|
recon.SetPortDialHook(func(host string, port int, _ time.Duration) bool { return port == 80 })
|
|
|
|
t.Cleanup(func() { recon.SetPortDialHook(nil) })
|
|
|
|
recon.SetFetchPageHook(func(rawURL string) (int, string, error) {
|
|
|
|
return 200, `<html><input name="callback_url" id="cb">`, nil
|
|
|
|
})
|
|
|
|
t.Cleanup(func() { recon.SetFetchPageHook(nil) })
|
|
|
|
database, _ := db.New(t.TempDir())
|
|
|
|
t.Cleanup(func() { _ = database.Close() })
|
|
|
|
h := NewReconHandler(database, nil, func() string { return "https://canary.test" })
|
|
|
|
body, _ := json.Marshal(map[string]interface{}{"host": "recon.lab", "port": 80, "scheme": "http", "ssrf_canary": true})
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.Scan(w, httptest.NewRequest(http.MethodPost, "/api/v1/recon/scan", bytes.NewReader(body)))
|
|
|
|
var report recon.ScanReport
|
|
|
|
_ = json.Unmarshal(w.Body.Bytes(), &report)
|
|
|
|
if report.Canary == nil || report.Canary.PasteFieldName != "callback_url" {
|
|
|
|
t.Fatalf("%+v", report.Canary)
|
|
|
|
}
|
|
|
|
pw := httptest.NewRecorder()
|
|
|
|
pingReq := httptest.NewRequest(http.MethodGet, "/recon/ping/"+report.ScanID, nil)
|
|
|
|
pingCtx := chi.NewRouteContext()
|
|
|
|
pingCtx.URLParams.Add("scan_id", report.ScanID)
|
|
|
|
pingReq = pingReq.WithContext(context.WithValue(pingReq.Context(), chi.RouteCtxKey, pingCtx))
|
|
|
|
h.CanaryPing(pw, pingReq)
|
|
|
|
sw := httptest.NewRecorder()
|
|
|
|
statusReq := httptest.NewRequest(http.MethodGet, "/api/v1/recon/canary/"+report.ScanID, nil)
|
|
|
|
statusCtx := chi.NewRouteContext()
|
|
|
|
statusCtx.URLParams.Add("scan_id", report.ScanID)
|
|
|
|
statusReq = statusReq.WithContext(context.WithValue(statusReq.Context(), chi.RouteCtxKey, statusCtx))
|
|
|
|
h.CanaryStatus(sw, statusReq)
|
|
|
|
var st recon.SSRFCanaryInfo
|
|
|
|
_ = json.Unmarshal(sw.Body.Bytes(), &st)
|
|
|
|
if st.Status != "confirmed" {
|
|
|
|
t.Fatalf("%+v", st)
|
|
|
|
}
|
|
|
|
if !strings.Contains(report.Canary.PasteTarget, report.ScanID) {
|
|
|
|
t.Fatal(report.Canary.PasteTarget)
|
|
|
|
}
|
|
|
|
}
|
|
|