poseidon icon indicating copy to clipboard operation
poseidon copied to clipboard

Adding lsopen command

Open coolcoolnoworries opened this issue 1 year ago • 3 comments

Sending a pull request to add "lsopen" as a new command.

Uses the LaunchServices API to run applications and binaries directly out of PID 1 (launchd), the macOS equivalent of explorer.exe on Windows. Using "shell" or "run" commands will spawn processes directly as children. With "lsopen", the command can work as a form of PPID spoofing, which is especially helpful to evade detections built around strange process trees.

For example, the following pstree output shows ping as a child process of the poseidon payload "testbeforepush", as it was spawned with the "run" command. Seeing the ping binary (or any other native binary) spawning out of an unsigned poseidon payload can lead to high fidelity opportunities for detection, since its not typical.

image

If we use "lsopen" instead, we can detach from the process tree and avoid correlation with our payload.

image

image

Added logic to stomp the "_" variable to match the spawned path, which removes an ioc that would normally point back to the poseidon process.

Added logic to unset the DYLD_INSERT_LIBRARIES var if its present, so that we don't have any issues with inherited dylib injections into the new process.

Also created a documentation page for the command to explain usage and nuances.

Added opsec bonus, currently - as of July 18 2024 - neither TrueTree nor direct ESF can determine the true parent of a process spawned with lsopen.

Last note is 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. Otherwise, this is most useful for running detached processes where we don't care or need to receive the output directly (e.g. application bundles, dedicated clipboard monitors, other payloads, tools with alternative output capabilities, etc.).

This is my first time pushing a new command, so please let me know if I missed anything in the process. Apologies in advance if I did, I can modify as needed. Thanks.

coolcoolnoworries avatar Jul 19 '24 08:07 coolcoolnoworries

Oh wow, this looks awesome! Sorry for the late comment on this, I didn't even notice there was an open PR until today! I'll check it out and let you know how it looks!

its-a-feature avatar Sep 04 '24 20:09 its-a-feature

Have you had success with the HideApp flag? I do something really similar in the Apfell agent, https://github.com/MythicAgents/apfell/blob/master/Payload_Type/apfell/apfell/agent_code/launchapp.js#L9, but even when specifying to hide, it never hides :/

its-a-feature avatar Sep 04 '24 21:09 its-a-feature

Have you had success with the HideApp flag? I do something really similar in the Apfell agent, https://github.com/MythicAgents/apfell/blob/master/Payload_Type/apfell/apfell/agent_code/launchapp.js#L9, but even when specifying to hide, it never hides :/

Haha its a weird one, Ive never had success with a full hide using that flag either. Best I can tell, it just prevents the app from spawning a window, but it still pops up in dock. For something close to a true hide, Ive had the most success with setting LSUIElement to "1" in a bundle's Info.plist, but that would mean either changing an existing bundle on disk - invalidating the sig - or bringing your own custom one to drop. Something like this:

<key>LSUIElement</key>
<string>1</string>

coolcoolnoworries avatar Sep 09 '24 18:09 coolcoolnoworries

I want to try to get this merged in this week. Can you:

  • update the help args to be the CLI args instead of JSON
  • remove the extra go.mod in the lsopen
  • undo the mod to the main go.mod file
  • https://github.com/MythicAgents/poseidon/pull/56/files#diff-6d70aa266fee0d9abb5ce44c12132d7675197d36b1ad35a76ed5b2c71fc59f31R49 <-- be sure to set error here

There's a helpful way to make responses so you don't have to set each individual component:

msg := task.NewResponse()

then for errors, you can do:

msg.SetError(fmt.Sprintf("Failed to unmarshal parameters. Reason: %s", err.Error()))
task.Job.SendResponses <- msg

and it'll automatically set completed, user_output to that error message, and update the status. Otherwise, if it's successful, you can still do your normal:

msg.Completed = true
msg.UserOutput = outputMsg

its-a-feature avatar Oct 07 '24 15:10 its-a-feature

Just saw this - got it, working on it now.

coolcoolnoworries avatar Oct 14 '24 05:10 coolcoolnoworries