Sliver extension development
Sliver extensions are how you bolt post-exploitation tooling onto the implant without recompiling it. Three flavours matter in practice: BOFs (COFF object files executed by the coff-loader extension), .NET assemblies (run via the .NET extension that hosts the CLR), and native shared objects (.dll/.so/.dylib) loaded by the implant itself. The packaging and OPSEC stories differ for each, and the Armory is just a git-backed index that serves the same extension.json you would hand-install.
Manifest anatomy
Every extension is a directory with an extension.json and one binary per supported GOOS/GOARCH. The schema is small but unforgiving — the server validates it before pushing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"name": "whoami-ext",
"command_name": "whoami-ext",
"version": "0.1.0",
"extension_author": "tyrus",
"original_author": "tyrus",
"repo_url": "https://example.invalid/whoami-ext",
"help": "Print the current token user via GetUserNameExW",
"entrypoint": "Run",
"depends_on": "",
"init": "",
"files": [
{ "os": "windows", "arch": "amd64", "path": "whoami.x64.o" },
{ "os": "windows", "arch": "386", "path": "whoami.x86.o" }
],
"arguments": [
{ "name": "format", "type": "string", "desc": "json|text", "optional": true }
]
}
Key gotchas:
entrypointis the exported symbol the loader calls. For BOFs it is the function name (goby convention, but Sliver lets you rename it). For .NET it is ignored — the assembly’s entry point is used.depends_onchains another extension. A BOF extension must declaredepends_on: "coff-loader"or it will not execute.initruns once on first load; useful for the .NET host to spin up the CLR.
BOF path
Write the BOF as you would for Cobalt Strike — see bof-cobalt-strike-development for the calling convention details. The wire format Sliver uses is the same beacon pack: integers, shorts, strings, and binary blobs, length-prefixed. The implant ships coff-loader which is a port of TrustedSec’s COFFLoader; it resolves __imp_ thunks against an allowlist of Win32 APIs.
Build:
1
2
x86_64-w64-mingw32-gcc -c whoami.c -o whoami.x64.o \
-masm=intel -Os -fno-asynchronous-unwind-tables
Then drop the .o next to extension.json, zip the directory, and extensions load ./whoami-ext.tar.gz from the client. Sliver pushes the manifest + binary to the implant on first invocation and caches it for the session.
.NET path
The .NET extension hosts the CLR in-process. You hand it a normal assembly (any Main-bearing PE) and it runs it on a worker thread with stdout/stderr captured. AMSI and ETW are live in that process unless you patched them earlier — see amsi-memory-patching-deep and etw-tampering-deep. The host extension exposes --amsi-bypass and --etw-bypass flags that patch the well-known byte sequences before invoking the assembly, but those signatures are well-known and shipped detections will fire.
Tradecraft: prefer compiling assemblies with /platform:anycpu, strip PDB paths, and avoid System.Management.Automation references — they pull the PowerShell engine and trip script-block logging.
Native shared-object path
For Go or C extensions compiled as a DLL/dylib, the implant dlopens the file and calls the exported entrypoint. This runs in-process by default, which is fast but means a crash takes the implant with it. Sliver supports --process on the client to spawn a sacrificial host and inject — use it for anything that touches unstable APIs or third-party libraries.
Shipping via the Armory
The Armory is a list of git repos in ~/.sliver-client/armories.json. Each repo has an armory.json index, signed with a minisign key. To publish:
1
2
3
4
minisign -Gm armory.json
git add armory.json armory.json.minisig whoami-ext.tar.gz
git commit -m "add whoami-ext 0.1.0"
git push
Clients pin the public key on first add. Rotating the key requires a manual armory remove + re-add from every operator, so treat the signing key like a release key.
OPSEC notes
- In-process extensions inherit the implant’s process token, image-load history, and AMSI/ETW state. If you have not unhooked or moved to indirect syscalls (syscall-direct-and-indirect, hells-halos-tartarus-gates-comparison), the EDR sees every Win32 call the extension makes.
coff-loaderallocates RWX for the loaded COFF. Modern EDRs alert on RWX in beacon processes — pre-stage with RW then RX viaVirtualProtect, or fork the loader to do so.- .NET extensions trigger
clr.dll/mscoree.dllloads. If your implant lives in a process that has no business hosting the CLR (e.g.,notepad.exe), the image-load telemetry is a giveaway. Pick a host that already runs managed code, or use theexecute-assemblystyle spawn-and-inject flow. - The Armory transport is plain HTTPS git. Mirror the repos you depend on; do not rely on upstream availability mid-engagement.
For an end-to-end view of where extensions slot into a Sliver engagement see sliver-c2-deep, and for the broader payload toolchain osep-payload-development-toolkit and osep-ad-attack-chain-walkthrough.