Files
AetherForge/scripts/_recon_handler_canary.go.bak
AetherForge 49c24e611c
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add recon form fingerprint library and SSRF canary mode.
Score crawl inputs by SSRF-prone field names and register ping-back canaries for operator paste confirmation.
2026-06-07 12:23:11 -07:00

188 lines
4.8 KiB
Go

package api
import (
"encoding/json"
"log"
"net/http"
"strings"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
dbpkg "crypto-miner-server/internal/db"
"crypto-miner-server/internal/recon"
)
type ReconHandler struct {
db *dbpkg.Database
wsHub *WSHub
publicURL func() string
canaryHub *ReconCanaryHub
}
func NewReconHandler(database *dbpkg.Database, hub *WSHub, publicURL ...func() string) *ReconHandler {
var fn func() string
if len(publicURL) > 0 {
fn = publicURL[0]
}
return &ReconHandler{db: database, wsHub: hub, publicURL: fn, canaryHub: NewReconCanaryHub()}
}
func (h *ReconHandler) Scan(w http.ResponseWriter, r *http.Request) {
var req recon.ScanRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid JSON", http.StatusBadRequest)
return
}
req.Host = strings.TrimSpace(req.Host)
if req.Host == "" {
http.Error(w, "host required", http.StatusBadRequest)
return
}
host, err := recon.NormalizeHost(req.Host)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
req.Host = host
scanID := uuid.New().String()
if req.SSRFCanary {
h.registerSSRfCanary(scanID, host, r)
}
report, err := recon.ScanStream(req, scanID, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
report.ScanID = scanID
if req.SSRFCanary {
h.finalizeSSRfCanary(scanID, report)
}
if h != nil && h.db != nil {
_ = (&OathLedgerBridge{DB: h.db, Hub: oathHub(h)}).Record(AuthUsername(r), dbpkg.OathReconScan, "", "", dbpkg.OathOutcomeSuccess,
map[string]string{"host": report.Host, "scan_id": scanID},
map[string]interface{}{"host": report.Host, "scan_id": scanID, "open_ports": openPortList(report.Ports), "ssrf_score": crawlSSRFScore(report.Crawl)})
}
writeJSON(w, report)
}
func (h *ReconHandler) registerSSRfCanary(scanID, host string, r *http.Request) {
base := ""
if h.publicURL != nil {
base = strings.TrimSpace(h.publicURL())
}
if base == "" && r != nil {
hh := strings.TrimSpace(r.Header.Get("X-Forwarded-Host"))
if hh == "" {
hh = r.Host
}
proto := strings.TrimSpace(r.Header.Get("X-Forwarded-Proto"))
if proto == "" {
proto = "https"
}
if hh != "" {
base = proto + "://" + hh
}
}
url := recon.BuildSSRfCanaryURL(base, scanID)
info := &recon.SSRFCanaryInfo{ScanID: scanID, URL: url, Status: "pending", PasteTarget: url}
if h.db != nil {
_ = h.db.InsertReconSSRfCanary(scanID, host, url, "", "")
}
if h.canaryHub != nil {
h.canaryHub.Register(scanID, info)
}
}
func (h *ReconHandler) finalizeSSRfCanary(scanID string, report *recon.ScanReport) {
info, _ := h.getCanaryInfo(scanID)
if info == nil || report == nil || report.Crawl == nil {
return
}
recon.ApplyCanaryPasteTarget(report.Crawl.FingerprintFields, info.URL)
if top := recon.TopFormFieldFingerprint(report.Crawl.FingerprintFields); top != nil {
info.PasteFieldName, info.PasteFieldID = top.Name, top.ID
if h.canaryHub != nil {
h.canaryHub.UpdatePasteField(scanID, top.Name, top.ID)
}
if h.db != nil {
_ = h.db.UpdateReconSSRfCanaryPasteField(scanID, top.Name, top.ID)
}
}
report.Canary = info
}
func (h *ReconHandler) getCanaryInfo(scanID string) (*recon.SSRFCanaryInfo, error) {
if h.canaryHub != nil {
if info := h.canaryHub.Get(scanID); info != nil {
return info, nil
}
}
if h.db != nil {
return h.db.GetReconSSRfCanary(scanID)
}
return nil, nil
}
func (h *ReconHandler) CanaryStatus(w http.ResponseWriter, r *http.Request) {
scanID := strings.TrimSpace(chi.URLParam(r, "scan_id"))
info, err := h.getCanaryInfo(scanID)
if scanID == "" || err != nil || info == nil {
http.Error(w, "canary not found", http.StatusNotFound)
return
}
writeJSON(w, info)
}
func (h *ReconHandler) CanaryPing(w http.ResponseWriter, r *http.Request) {
scanID := strings.TrimSpace(chi.URLParam(r, "scan_id"))
if scanID == "" {
writeJSON(w, map[string]interface{}{"ok": false})
return
}
hit := h.canaryHub != nil && h.canaryHub.MarkHit(scanID)
if !hit && h.db != nil {
hit, _ = h.db.MarkReconSSRfCanaryHit(scanID)
}
if !hit {
writeJSON(w, map[string]interface{}{"ok": false, "error": "canary not found"})
return
}
log.Printf("[recon] SSRF canary hit scan_id=%s", scanID)
if h.wsHub != nil {
BroadcastReconCanaryHit(h.wsHub, scanID)
}
writeJSON(w, map[string]interface{}{"ok": true, "scan_id": scanID, "status": "confirmed"})
}
func oathHub(h *ReconHandler) *WSHub {
if h == nil {
return nil
}
return h.wsHub
}
func openPortList(ports []recon.PortResult) []int {
var out []int
for _, p := range ports {
if p.Open {
out = append(out, p.Port)
}
}
return out
}
func crawlSSRFScore(c *recon.CrawlReport) int {
if c == nil {
return 0
}
return c.SSRFScore
}
func crawlCMS(c *recon.CrawlReport) []string {
if c == nil {
return nil
}
return c.CMSFingerprints
}