BishopFox / sliver

Adversary Emulation Framework
GNU General Public License v3.0
8.26k stars 1.09k forks source link

MacOS and Linux extensions #561

Open rkervella opened 2 years ago

rkervella commented 2 years ago

Make extensions work for MacOS.

usiegl00 commented 2 years ago

I have an in memory executable loader for Mac OS that might be useful. See: https://github.com/usiegl00/tamatoa I can re-write it in go if you'd like.

rkervella commented 2 years ago

I'd love to see an implementation of this in Go. I'm currently using https://github.com/binject/universal to load dylibs, but there's still issues I need to sort out as most of the time the host process (sliver implant) crashes.

My experiments are here if you want to have a look.

usiegl00 commented 2 years ago

Binject universal is using the same technique to load dylibs as tamatoa. You should be able to load machos by just modifying the header. This is how metasploit does it: https://github.com/rapid7/metasploit-framework/blob/4c0133d26d8007678b311d0dd82fa0c125347e4f/external/source/shellcode/osx/stager/main.c#L106

rkervella commented 2 years ago

Yes I mean we can load MachO, that's not the issue. The issue is keeping the implant alive. Memory allocated on the heap by the loaded dylibs are garbage collected by the Go runtime afterwards, which makes it crash because it doesn't recognize the layout of the GC'ed object.

usiegl00 commented 2 years ago

Is there a way to pause the GC for a set amount of time? (i.e. SetGCPercent(-1) then SetGCPercent(100)) See: https://pkg.go.dev/runtime/debug#SetGCPercent If not, can we call pthread_create to execute the dylib in a new thread?

moloch-- commented 2 years ago

Disabling GC for the entire process is not a tenable solution.

rkervella commented 2 years ago

Disabling GC for the entire process is not a tenable solution.

Agreed with @moloch-- , I'd like to find a solution that doesn't rely on these quirks if possible. Calling pthread_create could be an idea indeed, I'd need to look into it.

usiegl00 commented 2 years ago

Another option would be to call fork before running and then use a shared memory location to get the output.

rkervella commented 2 years ago

Made some progress on the MacOS front by fixing universal's behavior for MacOS 12 and up, thanks to @usiegl00 's PR to metasploit. Once this PR is merged, we'll be able to use universal to load dylibs. It doesn't resolve the issues I had with heap interactions, but the side effect is we can load CGO compiled dylibs, which should allow us to do all we want in Go / CGO.

We can probably even use this technique to pass a callback function to the extension code.

Paradoxis commented 5 months ago

Do you guys have any plans of implementing this any time soon? The PR to load dylibs should looks like it's been merged. I have some nice cross-platform extensions ready written in Rust (shameless blog promo on how to write them), would be nice to use in Linux implants as I've been using them quite a bit as of late :)

rkervella commented 5 months ago

Do you guys have any plans of implementing this any time soon? The PR to load dylibs should looks like it's been merged. I have some nice cross-platform extensions ready written in Rust (shameless blog promo on how to write them), would be nice to use in Linux implants as I've been using them quite a bit as of late :)

There is a work in progress to use purego to load shared libraries on Linux and MacOS, I don't have an ETA on merging that though. The current issue is puregoz doesn't seem to work with garble right now, so there's a tradeoff. Also, specifically on Linux, this would mean dropping the shared library in a memory file descriptor and then load it. But that's all still work in progress (check here if you want to play with it).

rkervella commented 5 months ago

On another note, I've started working on the sliver-sdk to facilitate writing extensions. Right now we have template for Go and Rust, but the idea is to add more.