Sliver C2 — VSCode Operator UI
A VSCode extension that turns the Sliver C2 console into a GUI: connect to a sliver-server, watch sessions and beacons update live, run commands, open interactive shells, manage listeners, and generate implants — all from the editor.
Internal red-team tooling. Use only against infrastructure you are authorized to operate.
Features
- Connections view — import operator
.cfg files (stored encrypted in VSCode SecretStorage), connect/disconnect, multiple servers.
- Live Sessions / Beacons / Jobs views — driven by the server
Events stream, so they update as implants come and go.
- Command console — a per-session panel that runs commands via the
Execute RPC with scrollback and history.
- Interactive shell — a real VSCode terminal bridged to a Sliver shell tunnel (
CreateTunnel → TunnelData → Shell).
- Listeners — start mTLS / HTTP / HTTPS / DNS listeners; stop running jobs.
- Implant generation — a form that drives the
Generate RPC and saves the build.
- Beacon tasking — queue async commands and inspect task history.
Architecture
src/
grpc/ ts-proto-generated client stubs (committed; regen with `pnpm run proto`)
client/ SliverClient (mTLS + Events stream + reconnect), ConnectionManager, config parsing
storage/ SecretStore (secrets.cfg) + ConnectionStore (globalState metadata)
ui/ TreeDataProviders (Connections/Sessions/Beacons/Jobs/Server), nodes, status bar
commands/ all command handlers
webview/ command console + implant-generation panels
terminal/ tunnel-backed interactive shell (vscode.Pseudoterminal)
fs/ SliverFileSystemProvider (sliver:// remote file browsing)
buf.gen.yaml ts-proto codegen config (see "Protocol buffers" below)
Key design notes:
- The gRPC stubs are generated by
buf from a pinned upstream Sliver tag, so they track a known version with no vendored protos to maintain.
- Everything (including
@grpc/grpc-js) is bundled by esbuild into dist/extension.js, so the .vsix ships no node_modules.
- 64-bit fields are generated as strings (
forceLong=string), so there is no Long runtime dependency.
Develop
This project uses pnpm (packageManager is pinned in package.json).
pnpm install
pnpm run proto # regenerate gRPC stubs (only when bumping Sliver)
pnpm run compile # esbuild → dist/extension.js
pnpm run watch # rebuild on change
pnpm run check # typecheck + lint + unit tests
Press F5 in VSCode to launch the Extension Development Host.
Everything (including @grpc/grpc-js) is bundled into dist/extension.js by
esbuild, so the .vsix ships no node_modules. The ts-proto stubs depend on
@bufbuild/protobuf (a devDependency, bundled).
Protocol buffers
The gRPC stubs in src/grpc/** are generated by buf,
which fetches the Sliver .proto files directly from the pinned upstream tag
— there are no vendored protos to maintain. The generator config is buf.gen.yaml
(ts-proto, pinned exactly for reproducible output).
To bump to a new Sliver release, change the tag= in the proto script in
package.json, then regenerate and commit:
pnpm run proto # buf generate from the new tag → src/grpc
pnpm run typecheck # surfaces any field/RPC renames to fix
git add src/grpc && git commit -m "proto: bump Sliver to <tag>"
The generated stubs are committed (so builds need no codegen step); CI runs
pnpm run proto and fails on any drift, keeping them in sync with the pinned tag.
Package
pnpm run vsix # → sliver-vscode.vsix (bundled, --no-dependencies)
code --install-extension sliver-vscode.vsix
Verify end-to-end against a real server
# 1. Run a server (multiplayer)
sliver-server
# 2. In the sliver-server console, mint an operator config
sliver > new-operator --name dev --lhost 127.0.0.1 --save ./dev.cfg
# 3. In VSCode: Sliver activity bar → Connections → Import → dev.cfg → Connect
# A GetVersion handshake validates mTLS + token before the views populate.
# 4. Start a listener (Jobs view → Start Listener → mtls), generate an implant
# (Jobs view → Generate Implant), run it on a test host, and watch the
# session appear live in the Sessions view. Right-click → Interactive Shell.
Updating to a newer Sliver
See Protocol buffers — bump the tag= in the proto
script, run pnpm run proto, and fix anything the typechecker flags.